Skip to main content

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:

  1. Register the customer.
  2. Query for an existing execution to resume safely and prevent duplicates.
  3. Start the workflow when no execution exists.
  4. Poll status until a node is waiting, the case enters manual review, or the execution reaches a terminal state.
  5. Submit node data (OCR / particular / liveness / OTP / forms).
  6. 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" }
FieldDescription
statusCurrent workflow execution status
caseNoWIDTH case reference created for the execution
caseStatusCurrent case review state: UNASSIGNED, UNDER_REVIEW, RETURNED, OTHER_GROUP, APPROVED, REJECTED or CLOSED
riskScoreCalculated risk score, where available
resolvedAtTime the case reached a final resolution
StatusMeaningAction
RUNNINGExecuting, no node waitingContinue polling (2–5 sec)
WAITINGNode waiting for inputRead currentNode, submit data
COMPLETEDWorkflow finishedStop polling, read result
FAILEDExecution failedStop polling, contact support
TIMEOUTExceeded time limitStop polling, contact support
CANCELLEDExecution cancelledStop polling
Manual review is a normal workflow state, not an API failure. Poll every 3–5 seconds during automated processing. When 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:

nodeTypeAPIPurpose
flow_action_identity_documentworkflow.kyc.ocrUpload ID, get OCR result
flow_action_individual_particularworkflow.kyc.particular.dataSubmit personal info
flow_action_liveness_testworkflow.kyc.liveness.dataSubmit liveness status
flow_action_validate_emailworkflow.kyc.otp.send/verifyEmail OTP
flow_action_validate_phoneworkflow.kyc.otp.send/verifyPhone OTP
flow_action_individual_required_documentworkflow.kyc.required.docSupporting documents
flow_action_individual_required_formsworkflow.kyc.required.formForms / 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"
}