KYC & Compliance

AI skills for checking, managing, and re-triggering KYC verification for customers.

Overview

This skill enables AI agents and applications to check and manage KYC (Know Your Customer) verification status for Eclipse customers.

It supports:

  • Retrieving current KYC verification status and individual check results
  • Triggering or re-triggering KYC ratification
  • Interpreting per-check checked/passed/pending results for operator action

KYC verification in Eclipse is called ratification. Ratification checks may include identity verification, liveness detection, and sanctions screening — the specific checks performed depend on the KYC provider configured for the tenant.


1. Get KYC Status

Trigger: "check KYC", "KYC status", "verify customer identity", "is the customer verified"

Required inputs: tenantId, customerId

GET {baseUrl}/eclipse-conductor/rest/v2/tenants/{tenantId}/customers/{customerId}/ratify
Authorization: Bearer {jwt}

Returns a list of EclipseKycResultV2 objects, most recent first. Pass offset=0&limit=1 for the latest result only.

EclipseKycResultV2 has a status field in its schema, but the ratify pipeline never populates it — treat it as always absent. The object is otherwise a lastModified timestamp plus one check field per configured check (e.g. sanctionsListCheck, selfieMatchesNationalIdentity, nationalIdentityIsLegitimate, permitIsLegitimate). There is no populated top-level status; verdict is always per-check.

📘

Always use v2 for ratify

Use the v2 ratify endpoints (/rest/v2/.../ratify) for all new integrations — each check reports its own pending/inReview state, which v1 does not.

Response — all relevant checks passed:

[
  {
    "lastModified": "2026-05-19T08:30:20.000Z",
    "sanctionsListCheck": { "checked": true, "passed": true, "pending": false },
    "selfieMatchesNationalIdentity": { "checked": true, "passed": true, "pending": false },
    "nationalIdentityIsLegitimate": { "checked": true, "passed": true, "pending": false }
  }
]

Response — a check failed:

[
  {
    "lastModified": "2026-05-19T08:30:20.000Z",
    "sanctionsListCheck": { "checked": true, "passed": false, "pending": false, "comment": "Match found on OFAC list" },
    "selfieMatchesNationalIdentity": { "checked": true, "passed": true, "pending": false }
  }
]

Each check field contains checked (was this check run?), passed (did it pass? absent if not yet resolved), pending (is this specific check still in progress?), inReview (awaiting manual review), and optionally score and comment.

Notes

  • Determine overall standing by reading the check fields relevant to the wallet type's KYC rule set — there is no single top-level status to branch on. See KYC/B Rulesets for how to find out which checks a given wallet type actually requires.
  • A failed or pending check that isn't part of the target wallet type's ruleset does not block eligibility — always confirm via Check Wallet Eligibility rather than treating any single passed: false as a hard stop.
  • passed: false on a check — review that field's comment. Common causes: ID mismatch, name mismatch, sanctions hit, poor document quality.
  • pending: true or inReview: true on a check — that specific check is still resolving (e.g. awaiting a third-party provider or manual review); re-fetch GET .../ratify?offset=0&limit=1 after 30–60 seconds. This does not block the ratify call itself, which already returned.
  • Sanctions check failures must be escalated to a compliance officer; do not clear them via API alone.

API Reference: GET /customers/{customerId}/ratify (v2) · POST /customers/{customerId}/ratify (v2)


2. Trigger KYC Ratification

Trigger: "ratify user", "run KYC", "re-verify customer"

Required inputs: tenantId, customerId

POST {baseUrl}/eclipse-conductor/rest/v2/tenants/{tenantId}/customers/{customerId}/ratify
Authorization: Bearer {jwt}
Content-Type: application/json
{}

Request body may be empty ({}) to run the default check set, or include type (NORMAL or COMPARISON) and checksToRun — a comma-separated string of check names, e.g. "sanctionsListCheck,pepCheck" — to limit which checks are executed.

Response: A single EclipseKycResultV2 object with individual check fields and lastModified. Checks that run inline resolve synchronously in this same response. A check backed by an asynchronous third-party provider (for example Standard Bank's sbsaRelyComplyCheck) can come back pending: true instead — that's a per-check state, not a separate job-level status to poll for.

Expected output: List each check relevant to the wallet type's KYC rule set with its passed/pending state. If any relevant check has pending: true, re-fetch GET .../ratify?offset=0&limit=1 after 30–60 seconds to see if that specific check has resolved.


3. Upload Identity Documents

Trigger: "upload ID", "upload passport", "upload permit"

Required inputs: tenantId, customerId, documentType, image or PDF of the document

Checks like nationalIdentityIsLegitimate, passportIsLegitimate, and permitIsLegitimate need a document to check against — upload it before triggering ratification.

POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/documents?performOcr=true&validateDocType=true
Authorization: Bearer {jwt}
Content-Type: application/json

{
  "documentType": "NATIONAL_IDENTITY",
  "mediaType": "image/jpeg",
  "base64EncodedDocument": "<base64>"
}

documentType is NATIONAL_IDENTITY, PASSPORT, or PERMIT depending on which identity document the customer supplied (see Customer Management — Identity Document Alternatives). mediaType is image/jpeg, image/png, or application/pdf.

A customer onboarding with a passport must also upload a PERMIT document — passportNumberMatchesPermit, lastNameMatchesPermit, and isPermitExpiryDateMatch cross-check it against the passport.

📘

Note

A rejected document (bad OCR read, wrong documentType) throws rather than returning a valid: false flag — catch the error and prompt the customer to retake the photo.

API Reference: POST /customers/{customerId}/documents


4. AWS Liveness — Eclipse Hosted Flow

Trigger: "aws liveness", "liveness check", "facial liveness", "start liveness session", "liveness detection"

Required inputs: tenantId, customerId, landingUrl (the URL the web view redirects to after liveness completes)

Eclipse Hosted AWS Liveness delegates the entire liveness UI to an Eclipse-managed web view. The channel only needs to open a URL in a web view — Eclipse handles the AWS Amplify SDK, video streaming, and result retrieval.

Prerequisites:

  • Tenant config ratifyUserProcessors includes selfieIsLegitimate=AWSLivenessRatifyProcessor
  • A valid JWT

Step 1 — Request an AWS liveness session

Post a FACIAL_PHOTO document with mediaType: aws/liveness and a landingUrl in extraInfo. The landingUrl is where Eclipse redirects the customer's browser after the liveness check completes.

POST {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/documents?performOcr=false&validateDocType=false
Authorization: Bearer {jwt}
Content-Type: application/json

{
  "documentType": "FACIAL_PHOTO",
  "mediaType": "aws/liveness",
  "extraInfo": "{\"landingUrl\":\"https://your-app.example.com/liveness/callback\"}"
}

To append custom query parameters to the completionUrl that Eclipse returns, pass the completionUrlParams request header. Eclipse appends the header value directly to the completionUrl after an &.

Response (201 Created):

{
  "documentId": 608561,
  "documentType": "FACIAL_PHOTO",
  "customerId": 1592544,
  "mediaType": "aws/liveness",
  "extraInfo": "{\"landingUrl\":\"https://your-app.example.com/liveness/callback\",\"awsLivenessSessionId\":\"e64e642b-f8db-499b-b3f1-db10f3100387\",\"completionUrl\":\"https://eclipsetests.backdraft.ukheshe.rocks/t/wXExW\"}",
  "awsFaceLivenessSessionId": "e64e642b-f8db-499b-b3f1-db10f3100387"
}

Extract completionUrl from extraInfo — this is the Eclipse-hosted liveness URL to open in the customer's web view.

📘

Note

The AWS session ID expires 3 minutes after issue. Open the completionUrl promptly after receiving it.

Step 2 — Open the completionUrl in a web view

Serve completionUrl in an iFrame or browser. Eclipse manages the full AWS Amplify liveness UI. On completion, the customer's browser is redirected to the landingUrl with one of:

  • {landingUrl}?status=COMPLETED — liveness check succeeded
  • {landingUrl}?status=CANCELLED — user cancelled or an error occurred

Do not proceed to Step 3 if status=CANCELLED.

Step 3 — Update the FACIAL_PHOTO document to retrieve liveness results

Once status=COMPLETED, PUT the document back using the same documentId and the original response body. This triggers Eclipse to fetch the liveness result and score from AWS and store them on the document.

PUT {baseUrl}/eclipse-conductor/rest/v1/tenants/{tenantId}/documents/{documentId}?returnFullResult=false
Authorization: Bearer {jwt}
Content-Type: application/json

{
  "documentId": 608561,
  "documentType": "FACIAL_PHOTO",
  "customerId": 1592544,
  "mediaType": "aws/liveness",
  "extraInfo": "{\"landingUrl\":\"https://your-app.example.com/liveness/callback\",\"awsLivenessSessionId\":\"e64e642b-f8db-499b-b3f1-db10f3100387\",\"completionUrl\":\"https://eclipsetests.backdraft.ukheshe.rocks/t/wXExW\"}",
  "awsFaceLivenessSessionId": "e64e642b-f8db-499b-b3f1-db10f3100387"
}

Response (200 OK):

{
  "documentId": 608561,
  "documentType": "FACIAL_PHOTO",
  "customerId": 1592544,
  "mediaType": "image/jpeg",
  "extraInfo": "{\"AwsFaceLivenessResult\":\"SUCCEEDED\",\"AwsFaceLivenessScore\":\"75.73\",\"landingUrl\":\"https://your-app.example.com/liveness/callback\",\"awsLivenessSessionId\":\"e64e642b-f8db-499b-b3f1-db10f3100387\"}"
}

Check AwsFaceLivenessResult in extraInfo:

  • SUCCEEDED — confidence score met the threshold; proceed to ratification
  • Anything else — liveness failed; advise the customer to retry

The default confidence score threshold is 55. This is controlled by tenant config aws.face.liveness.ConfidenceScoreMin.

Step 4 — Run KYC ratification

POST {baseUrl}/eclipse-conductor/rest/v2/tenants/{tenantId}/customers/{customerId}/ratify
Authorization: Bearer {jwt}
Content-Type: application/json

{}

This response carries selfieIsLegitimate's result directly — read it without polling. Other checks in the same response can still come back pending: true if the tenant's ruleset includes an asynchronous check (for example Standard Bank's sbsaRelyComplyCheck) — see Handling the Asynchronous sbsaRelyComplyCheck if that applies. See Customer Management — Step 3 to confirm wallet eligibility next.

Notes

  • Do not initiate a new liveness session if KYC fails for reasons unrelated to liveness (e.g. name mismatch). Only the failed check needs to be addressed — re-running liveness incurs third-party costs.
  • Do not re-run liveness on a customer who has already passed it unless explicitly instructed by an authorised operator.
  • If status=CANCELLED at the landingUrl, report the failure to the operator and offer the customer the option to retry from Step 1.

Common Patterns for AI Agents

1. KYC Status Check Flow (Standard)

  1. Get KYC status
  2. All relevant checks passed: true — no action needed; report to operator
  3. Any relevant check pending: true or inReview: true — re-fetch after 30–60 seconds; the ratify call itself already returned, only that check's outcome is still resolving
  4. Any relevant check passed: false — confirm via wallet eligibility (GET .../wallet-types) whether it actually blocks the customer; a failed check outside the target wallet type's ruleset doesn't. Escalate any sanctions failures to compliance regardless.

2. Upload Identity Documents Flow

  1. Upload the identity document(s) the customer supplied — NATIONAL_IDENTITY, or PASSPORT + PERMIT — with performOcr=true&validateDocType=true
  2. A rejected document throws — prompt the customer to retake the photo and retry
  3. Once accepted, trigger ratification

3. AWS Liveness Flow (Eclipse Hosted)

  1. POST FACIAL_PHOTO with mediaType: aws/liveness and landingUrl in extraInfo → receive completionUrl
  2. Open completionUrl in the customer's web view
  3. On redirect to landingUrl?status=COMPLETED, PUT the document using the original response body → check AwsFaceLivenessResult in extraInfo
  4. If SUCCEEDED, POST to ratify — the response returns the check results directly
  5. Check wallet eligibility (GET .../wallet-types) — do not gate this on a KYC "status" resolving, there isn't one

Do not re-run liveness if KYC fails for a reason other than selfieIsLegitimate.

4. Re-Ratification Flow

Use only when:

  • The customer has updated their identity documents
  • A previous FAILED attempt had a correctable cause (e.g. name mismatch now resolved)
  • Explicitly instructed by an authorised operator

Warning: There is no top-level customer status that "resets" — but re-triggering ratify with no checksToRun re-runs every check from scratch, discarding all previously passed results. Checks show pending: true until each one resolves, and wallet-type eligibility (evaluated live against current check state) can transiently evaluate as not-allowed during that window. It also re-invokes paid third-party providers (sanctions/PEP/etc.) for every check, not just the one being corrected. Pass checksToRun — optionally with mergePreviousResult: true — to limit re-ratification to the specific check(s) needing correction and preserve everything else.


Error Handling

[
  {
    "type": "BUSINESS",
    "severity": "LOW",
    "description": "KYC ratification already in progress",
    "code": "KYC001",
    "traceId": "3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d",
    "spanId": "1a2b3c4d5e6f7a8b",
    "environment": "eclipse-sandbox"
  }
]
HTTP StatusMeaningCommon Cause
400Bad requestRatification already in progress for this customer
401UnauthorisedJWT missing, expired, or malformed
403ForbiddenCaller role does not have KYC management permissions
404Not foundCustomer ID does not exist or belongs to a different tenant

Best Practices

  • Never do a full re-trigger (no checksToRun) without explicit operator instruction — it re-runs every check from scratch and re-invokes paid third-party providers. Prefer checksToRun to target only the check(s) that need correction.
  • Escalate all sanctions check failures to a compliance officer immediately; do not attempt to clear them programmatically.
  • There is no top-level KYC status — always state which individual checks passed, failed, or are pending when reporting to an operator.
  • If a relevant check shows pending: true, re-fetch GET .../ratify?offset=0&limit=1 after 30–60 seconds; the ratify call itself already returned, only that check's outcome is still resolving.
  • When a traceId is returned in an error, record it and reference it in any support escalation.
  • To know which checks a given wallet type actually requires, look up its KYC ruleset — see KYC/B Rulesets.

Did this page help you?