Workflow API
SEON's Workflow API enables you to initialize and manage verification workflows that combine document verification, selfie checks, fraud detection, and AML screening in a single orchestrated flow. Use this endpoint to start a workflow session and receive a token for the frontend SDK.
Good to know
- The
workflowIdmust be a valid UUID of an active workflow created in the Admin Panel (Admin Panel / Workflows). - The
user_idfield is always required in theinputsobject to identify the end user. - Additional required inputs depend on your workflow configuration (e.g.,
emailif Email check is enabled,phone_numberif Phone check is enabled). - All SEON API requests are case-sensitive. Please follow the formatting below to avoid errors.
- IP address is auto-captured from the end user's browser if not provided in the request.
- Device fingerprinting is handled automatically by the SDK when Device check is enabled.
- All Fraud API input fields are accepted. The Workflow API supports the complete set of fields from the Fraud API, plus additional orchestration-specific fields (e.g.,
reference_image, eKYC identifiers). See the Fraud API documentation for the full list of available fields.
For more context on how to begin your API integration check the Introduction section or our Integration Guide.
Common Workflow Scenarios
| Workflow Type | Required Inputs |
| Document + Selfie (basic) | user_id |
| Document + Selfie + Face Match (URL) | user_id, reference_image |
| Email + Phone fraud check | user_id, email, phone_number |
| Full fraud check (Email + Phone + IP) | user_id, email, phone_number, (IP auto-captured) |
| AML screening | user_id, user_fullname |
| NIN eKYC (Nigeria) | user_id, user_firstname, user_lastname, user_dob, nin |
| BVN eKYC (Nigeria) | user_id, user_firstname, user_lastname, user_dob, nin |
| CPF eKYC (Brazil) | user_id, cpf |
Request
Request Attributes | Type | Required | |
|---|---|---|---|
workflowId | string (UUID) | yes | |
inputs | object | yes | |
HTTP Endpoint
Response
The endpoint returns JSON structured response.
JSON Attributes | Type | ||
|---|---|---|---|
executionId | string (UUID) | ||
token | string | ||
Response
Error Responses
| HTTP Status | Error Code | Description |
| 400 | MISSING_REQUIRED_INPUTS | Required workflow inputs not provided (e.g., missing user_id or workflow-specific fields). |
| 401 | INVALID_INPUT_FORMAT | Input field format is incorrect (e.g., invalid email format). |
| 402 | UNAUTHORIZED | Invalid or missing API key. |
| 403 | FORBIDDEN | API key doesn't have access to this workflow. |
| 404 | WORKFLOW_NOT_FOUND | Workflow ID doesn't exist or workflow is inactive. |
| 429 | RATE_LIMITED | Too many requests. Implement exponential backoff. |
| 500 | INTERNAL_ERROR | Internal server error. Contact SEON support with your executionId. |
Orchestration SDK
You can integrate SEON's Orchestration module directly into a web app by using our JavaScript SDK. Please use our npm-hosted package to ensure you always load the latest available version.
Visit the SEON Orchestration SDK npm page to see the latest version and its changelog.
- Install the SDK via npm or yarn and import it into your application.
- Initialize a workflow from your backend using the Workflow API described above to get a
token. - Call
SeonOrchestration.start(config)with the token to launch the verification flow. - Listen to events (
completed,error,cancelled) to handle the verification result. - Use webhooks or the Admin Panel to access detailed verification results and captured media.
Installation
npm / yarn
npm install @seontechnologies/seon-orchestration
# or
yarn add @seontechnologies/seon-orchestration
Import
import { SeonOrchestration } from '@seontechnologies/seon-orchestration';
Prerequisites
- Node.js >=20.0.0, npm >=7.0.0
- SEON account with workflow access
- API key (obtain from Admin Panel / Settings / API Keys)
- At least one workflow created (Admin Panel / Workflows)
Browser Compatibility
| Browser | Min Version |
| Chrome | 96 |
| Safari | 15 |
| Firefox | 79 |
| Opera | 82 |
| iOS Safari | 15 |
| Android Browser | 81 |
| Chrome for Android | 96 |
| Firefox for Android | 79 |
| Internet Explorer | Not Supported |
Configuration parameters
To configure the Orchestration SDK, you need to create a config object and pass it to SeonOrchestration.start(config).
JSON Attributes | Type | Required | |
|---|---|---|---|
token | string | yes | |
language | string | no | |
theme | object | no | |
renderingMode | string | no | |
containerId | string | conditional | |
Core Methods
| Method | Description |
| SeonOrchestration.start(config) | Start verification flow with the provided configuration |
| SeonOrchestration.close() | Close the current verification flow and clean up UI |
| SeonOrchestration.on(event, handler) | Subscribe to SDK events |
| SeonOrchestration.off(event, handler) | Unsubscribe from SDK events |
Events
| Event | Callback Signature | Description |
| opened | () => void | Flow UI opened |
| closed | () => void | Flow UI closed |
| started | () => void | Verification started |
| completed | (status: CompletionTypes) => void | Verification completed |
| cancelled | () => void | User cancelled |
| error | (errorCode: ErrorCodes) => void | Error occurred |
Completion Types: success, pending, failed, unknown
Error Codes
Error codes received via the error event:
| Code | Description |
| error_code_1 | Device not supported — No capable camera/device found, or general error screen dismissed |
| error_code_3 | Authentication failed — Unauthorized request (invalid/expired token) |
| error_code_4 | Document capture SDK error — Failed to initialize document scanning |
| error_code_5 | Document capture retry limit exceeded — User exceeded max retries for document scanning |
| error_code_6 | Liveness check retry limit exceeded — User exceeded max retries for liveness detection |
| unknown | Unhandled error — Unexpected error or unhandled promise rejection |
SDK Exceptions
Exceptions thrown by SeonOrchestration.start() (catch via try/catch):
| Error Message | Cause |
| "IDV flow is already running." | Calling start() when a flow is already active |
| "Configuration is not set." | Calling start() without passing config |
| "Failed to initialize client: {status} {statusText}" | Backend init failed (e.g., invalid/expired token) |
| "Invalid response from client init." | Invalid account configuration |
| "Container ID is required for inline rendering." | Using renderingMode: inline without containerId |
| "Container element with id '{id}' not found." | Container DOM element doesn't exist |
| "Failed to open popup window. Please allow popups and try again." | Browser blocked popup window |
| "Invalid rendering mode specified." | Invalid renderingMode value |
Example: Minimal Integration
javascript
import { SeonOrchestration } from '@seontechnologies/seon-orchestration';
// 1. Get token from YOUR backend (keeps API keys secure)
const { token } = await fetch('/api/init-verification', { method: 'POST' })
.then(r => r.json());
// 2. Start verification
await SeonOrchestration.start({ token, language: 'en' });
Example: Full Configuration
// On page load: Set up event listeners
SeonOrchestration.on('completed', (status) => {
console.log('Verification completed:', status);
});
SeonOrchestration.on('error', (errorCode) => {
console.error('Verification error:', errorCode);
});
const config = {
token: 'eyJhbGciOiJIUzI1NiIs...', // From your backend
language: 'en',
renderingMode: 'fullscreen',
theme: {
light: {
baseTextOnLight: '#1a1a1a',
baseTextOnDark: '#ffffff',
baseAccent: '#0066cc',
baseOnAccent: '#ffffff',
logoUrl: 'https://example.com/logo-dark.svg'
},
dark: {
baseTextOnLight: '#e5e5e5',
baseTextOnDark: '#1a1a1a',
baseAccent: '#4d9fff',
baseOnAccent: '#000000',
logoUrl: 'https://example.com/logo-light.svg'
},
fontFamily: 'Inter',
fontUrl: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap',
fontWeight: '500'
}
};
await SeonOrchestration.start(config);Example: Inline Rendering
<!-- In your HTML -->
<div id="verification-container" style="width: 100%; min-height: 600px;"></div>
await SeonOrchestration.start({
token,
renderingMode: 'inline',
containerId: 'verification-container'
});
| Requirement | Details |
| Container element | Must exist in DOM before start() is called |
| Minimum size | 400×600 px recommended for usability |
| Responsive | Container should be responsive; SDK adapts to available space |
Example: React Integration
import React, { useEffect, useState } from 'react';
import { SeonOrchestration, CompletionTypes, ErrorCodes } from '@seontechnologies/seon-orchestration';
export function VerificationComponent({ userId, onComplete, onError }) {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const handleCompleted = (status: CompletionTypes) => {
onComplete(status);
};
const handleError = (errorCode: ErrorCodes) => {
setError(`Error: ${errorCode}`);
onError(errorCode);
};
const handleClosed = () => setIsLoading(false);
SeonOrchestration.on('completed', handleCompleted);
SeonOrchestration.on('error', handleError);
SeonOrchestration.on('closed', handleClosed);
return () => {
SeonOrchestration.off('completed', handleCompleted);
SeonOrchestration.off('error', handleError);
SeonOrchestration.off('closed', handleClosed);
};
}, [onComplete, onError]);
const startVerification = async () => {
setIsLoading(true);
setError(null);
try {
const response = await fetch('/api/init-verification', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId }),
});
const { token } = await response.json();
await SeonOrchestration.start({ token, language: 'en' });
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
return (
<div>
{error && <div style={{ color: 'red' }}>{error}</div>}
<button onClick={startVerification} disabled={isLoading}>
{isLoading ? 'Starting...' : 'Start Verification'}
</button>
</div>
);
}