KYC · Customer onboarding
Asynchronous workflow for individual customer onboarding: register the customer, recover or start an execution, poll its status and review outcome, then submit data for each waiting node.
Flow overview
Standard KYC workflow:
- Register the customer.
- Query for an existing execution to resume safely and prevent duplicates.
- Start the workflow when no execution exists.
- Poll status until a node is waiting, the case enters manual review, or the execution reaches a terminal state.
- Submit node data (OCR / particular / liveness / OTP / forms).
- Workflow completes or routes to manual review.
1. Register customer
Endpoint: workflow.applicant.register (POST).
{
"customerId": "C_abc123",
"email": "john@example.com"
}
At least one of customerId or email is required. This API is idempotent.
2. Resume or start a workflow
2.1 Check for an existing execution
Endpoint: workflow.apply.query (POST). Query by customerId before creating an execution. This prevents duplicate submissions and recovers the executionId after a timeout or lost submit response.
{ "customerId": "C_abc123" }
If an execution is returned, keep its executionId and continue with status polling. If no execution is returned, start a new workflow.
2.2 Start the workflow
Endpoint: workflow.apply.submit (POST). Use apiVersion=2.0.0 for the request.
{
"eventType": "KYC",
"source": "API",
"data": {
"customerId": "C_abc123",
"referenceId": "onboarding-uuid-001"
}
}
workflowId is optional. Provide it when the integration must explicitly select a published workflow. Otherwise, WIDTH resolves the applicable published workflow from the event type and tenant configuration.
The response includes the created executionId, sync processing indicator and response code. A 200 code indicates normal processing. A 403 code indicates insufficient quota; do not create another execution until quota is available.
3. Poll execution status and review outcome
Endpoint: workflow.execution.status (POST).
{ "executionId": "exec-abc123def456" }
| Field | Description |
|---|---|
status | Current workflow execution status |
caseNo | WIDTH case reference created for the execution |
caseStatus | Current case review state: UNASSIGNED, UNDER_REVIEW, RETURNED, OTHER_GROUP, APPROVED, REJECTED or CLOSED |
riskScore | Calculated risk score, where available |
resolvedAt | Time the case reached a final resolution |
| Status | Meaning | Action |
|---|---|---|
RUNNING | Executing, no node waiting | Continue polling (2–5 sec) |
WAITING | Node waiting for input | Read currentNode, submit data |
COMPLETED | Workflow finished | Stop polling, read result |
FAILED | Execution failed | Stop polling, contact support |
TIMEOUT | Exceeded time limit | Stop polling, contact support |
CANCELLED | Execution cancelled | Stop polling |
caseStatus is UNASSIGNED, UNDER_REVIEW or RETURNED, reduce polling to every 30–60 seconds. Stop when the execution reaches a terminal state.4. Submit node data
When status is WAITING, dispatch on currentNode.nodeType:
nodeType | API | Purpose |
|---|---|---|
flow_action_identity_document | workflow.kyc.ocr | Upload ID, get OCR result |
flow_action_individual_particular | workflow.kyc.particular.data | Submit personal info |
flow_action_liveness_test | workflow.kyc.liveness.data | Submit liveness status |
flow_action_validate_email | workflow.kyc.otp.send/verify | Email OTP |
flow_action_validate_phone | workflow.kyc.otp.send/verify | Phone OTP |
flow_action_individual_required_document | workflow.kyc.required.doc | Supporting documents |
flow_action_individual_required_forms | workflow.kyc.required.form | Forms / questionnaires |
flow_action_screen_check | (automatic) | AML / sanctions screening |
Identity document (OCR)
Submit: workflow.kyc.ocr (POST). Poll for OCR results at workflow.kyc.ocr.data after 2–5 seconds — OCR usually completes in 5–15 seconds.
{
"provider": "WIDTH",
"frontImage": "s3://workflow-kyc/.../front.jpg",
"backImage": "s3://workflow-kyc/.../back.jpg",
"documentType": "INTERNATIONAL_PASSPORT",
"region": "SG",
"executionId": "exec-abc123def456",
"callbackKey": "kyc:ocr:exec-abc123def456:...",
"referenceId": "onboarding-uuid-001"
}
Particular submission
Endpoint: workflow.kyc.particular.data.
{
"executionId": "exec-abc123def456",
"callbackKey": "kyc:particular:exec-abc123def456:...",
"referenceId": "onboarding-uuid-001",
"personData": "{\"name\":\"John Smith\",\"dateOfBirth\":\"1990-01-15\",\"nationality\":\"SG\",\"sex\":\"MALE\",\"countryOfResidence\":\"SG\",\"address\":\"123 Main St\"}"
}
personData is a JSON-encoded string, not a nested object. Call JSON.stringify() on the data before placing it in the request body.Liveness
Endpoint: workflow.kyc.liveness.data.
{
"provider": "SELF",
"status": "PASS",
"scores": "{\"qualityScore\":1.0,\"brightnessScore\":100,\"brightnessStatus\":\"normal\"}",
"images": "{\"faceFull\":\"s3://workflow-kyc/.../selfie.jpg\"}",
"executionId": "exec-abc123def456",
"callbackKey": "kyc:liveness:exec-abc123def456:...",
"referenceId": "onboarding-uuid-001"
}
Email / Phone OTP
Two steps: workflow.kyc.otp.send then workflow.kyc.otp.verify.
// send
{
"type": "email",
"identity": "john@example.com",
"executionId": "exec-abc123def456",
"callbackKey": "kyc:email:exec-abc123def456:...",
"referenceId": "onboarding-uuid-001"
}
// verify
{
"type": "email",
"identity": "john@example.com",
"code": "985024",
"executionId": "exec-abc123def456",
"callbackKey": "kyc:email:exec-abc123def456:...",
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"referenceId": "onboarding-uuid-001"
}
Required documents
Endpoint: workflow.kyc.required.doc. After all docs are uploaded, call workflow.kyc.required.doc.submit to advance.
{
"doc": "{\"front\":{\"url\":\"s3://workflow-kyc/.../bill.pdf\",\"filename\":\"bill.pdf\",\"content_type\":\"application/pdf\",\"size\":85618}}",
"isRequired": true,
"docType": "UTILITY_TELEPHONE_BILL",
"extraData": "{\"issueDate\":\"2026-03-01\"}",
"executionId": "exec-abc123def456",
"callbackKey": "kyc:required_doc:exec-abc123def456:...",
"referenceId": "onboarding-uuid-001"
}
Required forms
Endpoint: workflow.kyc.required.form.
{
"formId": "IndividualForm",
"formData": "{\"field_source_of_funds\":\"EMPLOYMENT\",\"field_annual_income\":\"50000-100000\"}",
"executionId": "exec-abc123def456",
"callbackKey": "kyc:required_form:exec-abc123def456:...",
"referenceId": "onboarding-uuid-001"
}