Workflows
curl --request GET \
--url https://api.example.com/kyc/workflowsimport requests
url = "https://api.example.com/kyc/workflows"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/kyc/workflows', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/kyc/workflows",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/kyc/workflows"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/kyc/workflows")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/kyc/workflows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyWorkflows
Workflows
GET
/
kyc
/
workflows
Workflows
curl --request GET \
--url https://api.example.com/kyc/workflowsimport requests
url = "https://api.example.com/kyc/workflows"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/kyc/workflows', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/kyc/workflows",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/kyc/workflows"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/kyc/workflows")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/kyc/workflows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyManage KYC onboarding workflows that define the steps and validation requirements for end-user onboarding processes. Workflows allow you to create customizable onboarding experiences with document upload, form fields, selfie verification, liveness detection, remediation behavior, and automated validation checks with automation rules.
Each attribute resolves to the API-provided value, else the value of its
Overview
Workflows define the structure and steps for KYC onboarding sessions. Each workflow consists of:- Steps: Sequential steps users must complete (document upload, form fields, selfie, liveness, etc.)
- Validation Configuration: Automated checks to run (watchlists, crypto, adverse media, face match)
- Automation Rules: Advanced rules for automatic decisions (approve/deny/flag/manual review)
- Entity Types: Target entity types (individual, company, or both)
- Versioning: Workflow updates create new versions while active sessions use their original version
Step Types
Document upload, form fields, selfie, liveness detection, and validation steps
Field Types
Text, number, date, country, name, wallet, email, phone, and semantic field types
Automation Rules
Country rules, list matches, face dedup, document validation, and more
Examples
Complete integration examples and common workflow patterns
Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST | /kyc/workflows | Create a new workflow |
POST | /kyc/workflows/from-template/{type} | Create workflow from template |
GET | /kyc/workflows | List all workflows |
GET | /kyc/workflows/{workflow_id} | Get workflow details |
GET | /kyc/workflows/{workflow_id}/versions | List workflow versions |
PUT | /kyc/workflows/{workflow_id} | Update workflow (creates new version) |
DELETE | /kyc/workflows/{workflow_id} | Soft delete workflow |
Authentication
Requireskyc:create permission for creating workflows and kyc:read permission for retrieving workflows. Include your Bearer token in the Authorization header.
Available Templates
Pre-built templates for common use cases:| Template | Description | Entity Type |
|---|---|---|
basic-kyc | Standard individual KYC with ID, address, and personal info | Individual |
company-kyc | Company KYC with incorporation docs, shareholders, directors | Company |
merchant-onboarding | Merchant onboarding with business license and sub-merchants | Both |
pep-enhanced | Enhanced due diligence for high-risk/PEP individuals | Individual |
Create Workflow
Create a new custom workflow.Endpoint
POST /kyc/workflows
Request Body Parameters
| Property | Type | Required | Description |
|---|---|---|---|
name | string | ✓ | Workflow name |
description | string | Workflow description | |
template_type | string | Template: basic-kyc, company-kyc, merchant-onboarding, pep-enhanced, or custom (default) | |
entity_types | array | Target entity types: individual, company, or both | |
industry | string | Target industry (e.g., “Financial Services”, “E-commerce”) | |
tags | array | Tags for categorization | |
custom_tags | object | Key-value string pairs ({"key": "value"}) included in all session webhook payloads. Max 20 entries, 50 chars per key, 200 chars per value. | |
session_attributes | array | Catalog of session attributes the integration sends in POST /kyc/sessions as attributes and that conditional_logic can reference with attribute. Max 20. See Session attributes. | |
steps | array | ✓ | Array of workflow step objects. See Step Types. |
validation_config | object | Validation configuration (see below) |
validation_config Properties
| Property | Type | Description |
|---|---|---|
run_lists | boolean | Run watchlist checks (OFAC, UN, etc.). Default: true |
run_crypto | boolean | Run crypto wallet checks. Default: false |
run_adverse_media | boolean | Run adverse media checks. Default: false |
auto_face_match | boolean | Automatically run face matching between ID and selfie. Default: false |
face_match_threshold | number | Minimum confidence threshold for face match (0.0-1.0). Default: 0.8 |
cross_validate_documents | boolean | Validate consistency between uploaded documents. Default: true |
correction_resolution | object | Controls what happens after the customer resolves reviewer-requested corrections |
automation_rules | object | Advanced automation rules. See Automation Rules. |
correction_resolution Properties
| Property | Type | Description |
|---|---|---|
mode | string | What happens after all corrections are resolved: manual_review (default), auto_process, or completed |
post_reprocess_mode | string | What happens when a reviewer manually reprocesses a remediated session: manual_review keeps the session awaiting reviewer decision; autoprocess applies automation outcomes |
Request Examples
Create Basic KYC Workflow
curl -X POST https://kyc.legaltalent.ai/kyc/workflows \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Basic KYC",
"description": "Standard KYC onboarding workflow",
"entity_types": ["individual"],
"custom_tags": {
"department": "onboarding",
"region": "latam"
},
"steps": [
{
"step_id": "step_1",
"order": 1,
"name": "ID Document",
"type": "document_upload",
"document_requirements": [
{
"document_type": "id",
"required": true,
"auto_extract": true
}
]
},
{
"step_id": "step_2",
"order": 2,
"name": "Selfie",
"type": "selfie",
"instructions": "Please take a clear selfie matching your ID photo"
},
{
"step_id": "step_3",
"order": 3,
"name": "Personal Information",
"type": "form_fill",
"custom_fields": [
{
"field_id": "full_name",
"name": "Full Name",
"type": "name",
"name_type": "individual",
"validate_lists": true,
"required": true
},
{
"field_id": "nationality",
"name": "Nationality",
"type": "country",
"country_type": "nationality",
"required": true
},
{
"field_id": "email",
"name": "Email",
"type": "email",
"validate_email": true,
"required": true
}
]
}
],
"validation_config": {
"run_lists": true,
"run_crypto": false,
"run_adverse_media": false,
"auto_face_match": true,
"face_match_threshold": 0.8,
"automation_rules": {
"enabled": true,
"auto_process": true,
"country_rules": [
{
"country_code": "KP",
"action": "auto_deny",
"applies_to": ["all"],
"reason": "Sanctioned country"
}
],
"face_match_threshold": 0.85,
"face_match_action_on_fail": "auto_deny",
"default_action": "manual_review"
}
}
}'
Create from Template
curl -X POST https://kyc.legaltalent.ai/kyc/workflows/from-template/basic-kyc \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My Basic KYC Workflow",
"description": "Customized basic KYC for my application"
}'
Create Workflow with Liveness Detection
curl -X POST https://kyc.legaltalent.ai/kyc/workflows \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "KYC with Liveness",
"entity_types": ["individual"],
"steps": [
{
"step_id": "step_1",
"order": 1,
"name": "ID Document",
"type": "document_upload",
"document_requirements": [
{"document_type": "id", "required": true, "auto_extract": true}
]
},
{
"step_id": "step_2",
"order": 2,
"name": "Liveness Check",
"type": "liveness",
"liveness_config": {
"min_confidence": 90.0,
"challenge_type": "FaceMovementChallenge",
"save_reference_image": true,
"use_for_face_match": true,
"max_retries": 3
}
}
],
"validation_config": {
"run_lists": true,
"auto_face_match": true
}
}'
Response Format
{
"status": "success",
"data": {
"workflow_id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "tenant123",
"name": "Basic KYC",
"description": "Standard KYC onboarding workflow",
"template_type": "custom",
"entity_types": ["individual"],
"custom_tags": {
"department": "onboarding",
"region": "latam"
},
"steps": [...],
"validation_config": {
"run_lists": true,
"run_crypto": false,
"run_adverse_media": false,
"auto_face_match": true,
"face_match_threshold": 0.8,
"cross_validate_documents": true,
"automation_rules": {
"enabled": true,
"auto_process": true,
"country_rules": [...],
"default_action": "manual_review"
}
},
"version": 1,
"is_active": true,
"created_at": "2024-11-22T10:30:00Z",
"updated_at": "2024-11-22T10:30:00Z"
}
}
List Workflows
Retrieve all workflows for your tenant.Endpoint
GET /kyc/workflows
Request Example
curl https://kyc.legaltalent.ai/kyc/workflows \
-H "Authorization: Bearer YOUR_TOKEN"
Response Format
{
"status": "success",
"data": {
"workflows": [
{
"workflow_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Basic KYC",
"description": "Standard KYC onboarding workflow",
"template_type": "basic-kyc",
"step_count": 3,
"is_active": true,
"version": 1,
"created_at": "2024-11-22T10:30:00Z",
"updated_at": "2024-11-22T10:30:00Z"
}
]
}
}
Get Workflow Details
Retrieve detailed information about a specific workflow.Endpoint
GET /kyc/workflows/{workflow_id}
Query Parameters
| Parameter | Type | Description |
|---|---|---|
version | integer | Specific version to retrieve (defaults to latest) |
Request Example
curl https://kyc.legaltalent.ai/kyc/workflows/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer YOUR_TOKEN"
Update Workflow
Update a workflow (creates a new version).Endpoint
PUT /kyc/workflows/{workflow_id}
Request Body
Same as create workflow, but all fields are optional (only include fields you want to update).Request Example
curl -X PUT https://kyc.legaltalent.ai/kyc/workflows/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Workflow Name",
"validation_config": {
"run_lists": true,
"run_crypto": true,
"run_adverse_media": true,
"auto_face_match": true,
"automation_rules": {
"enabled": true,
"auto_process": true
}
}
}'
Delete Workflow
Soft delete a workflow.Endpoint
DELETE /kyc/workflows/{workflow_id}
Request Example
curl -X DELETE https://kyc.legaltalent.ai/kyc/workflows/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer YOUR_TOKEN"
Response Format
{
"status": "success",
"data": {
"workflow_id": "550e8400-e29b-41d4-a716-446655440000",
"deleted": true
}
}
List Workflow Versions
Retrieve all versions of a workflow.Endpoint
GET /kyc/workflows/{workflow_id}/versions
Request Example
curl https://kyc.legaltalent.ai/kyc/workflows/550e8400-e29b-41d4-a716-446655440000/versions \
-H "Authorization: Bearer YOUR_TOKEN"
Response Format
{
"status": "success",
"data": {
"versions": [
{
"version": 2,
"created_at": "2024-11-23T14:00:00Z",
"is_active": true
},
{
"version": 1,
"created_at": "2024-11-22T10:30:00Z",
"is_active": false
}
]
}
}
Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created - Workflow created successfully |
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Missing or invalid token |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Workflow or template not found |
| 500 | Internal Server Error |
Best Practices
- Use Templates: Start with a template and customize as needed
- Step Ordering: Ensure step orders are sequential (1, 2, 3, etc.)
- Liveness over Selfie: Use
livenessstep type for better fraud prevention - Face Matching: Enable
auto_face_matchwhen collecting both ID and selfie/liveness - Automation Rules: Configure automation rules for automatic decision making
- Semantic Fields: Use semantic field types (country, name, wallet) for automatic validation
- Entity Types: Set appropriate entity types to filter workflows in your UI
- Versioning: Track workflow versions - active sessions continue using their original version
- Custom Tags: Use
custom_tagsto attach metadata (e.g., department, region, campaign) that will be forwarded in all session webhook payloads
Session attributes
A workflow can declare session attributes: facts the integration knows before the end user opens the link (industry, product, segment…) and sends inPOST /kyc/sessions as attributes. Steps, form fields and document requirements can then be shown, hidden or required through conditional_logic without asking the end user.
Catalog
session_attributes is a list (max 20) of definitions:
| Field | Type | Description |
|---|---|---|
key | string | Identifier used in the API payload and in conditions. Lowercase letters, digits and _, starting with a letter |
label | string | Display name in the dashboard |
type | text | select | boolean | country | Value type. select requires options; country expects ISO 3166-1 alpha-2 |
options | string[] | Allowed values for select |
required | boolean | When true, session creation fails if the attribute is missing |
default | string | boolean | Value used when neither the API nor the fallback field provides one |
fallback_field_id | string | Form field that captures the value when the API omitted it. Hidden and not required once the API sends the attribute |
{
"session_attributes": [
{
"key": "industry",
"label": "Industry",
"type": "select",
"options": ["restaurants", "retail", "services"],
"required": false,
"fallback_field_id": "industry_field"
}
]
}
Conditions on attributes
A condition names either a form field (field_id) or an attribute (attribute), never both. Operators are the same as for fields.
{
"field_id": "food_license",
"name": "Food handling license",
"type": "file",
"conditional_logic": {
"show_if": { "attribute": "industry", "operator": "equals", "value": "restaurants" },
"required_if": { "attribute": "industry", "operator": "in", "value": ["restaurants"] }
}
}
fallback_field_id field, else default. An attribute that resolves to nothing makes show_if conditions false and is_empty true, so a conditional field stays hidden until the value is known. Because attributes are known at creation, a workflow’s first step may be hidden from the start.
Referencing an attribute that the catalog does not declare, or a fallback_field_id that is not a form field of the workflow, is rejected when the workflow is created or updated.
Related Documentation
- Step Types - Detailed step configuration
- Field Types - All available field types
- Automation Rules - Complete automation reference
- Examples - Integration examples