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 |
| Document + Selfie + Proof of Address (Evidence Collection) | user_id, user_address |
| 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 workflowId. |
Workflow Execution API - Request
Overview
The Workflow Execution API returns the result of one identity verification workflow execution on demand. A single request gives you the outcome of the execution and every check it performed. It also returns the data read off the submitted documents, and download links for the captured media.
Use it when the completion webhook is not enough. You can request the same execution as many times as you need, for as long as the data sits inside your data retention period.
How it works
Results become available once an execution reaches a final status. Four rules govern what a request returns:
- While the execution is in progress,
statusisPENDINGandchecksisnull. - Each entry in
checksis one check performed by the workflow. Only the result object matchingcheckTypeis populated, and every other result field isnull. - Media download links are presigned and expire 1 hour after you request them. Download the file, or call the endpoint again for fresh links.
- Results stay available for the length of your data retention period. After a purge, the execution and its check statuses are still returned, and every result object, extracted data field and media link is
null.
The webhook SEON sends when an execution finishes carries the same data object as this endpoint, so one parser handles both.
Choosing between the webhook and this API
The webhook remains the way to learn that an execution finished. This API covers four cases after that moment:
- A webhook was missed, or your service was down when it arrived.
- A media link expired and you need the image again.
- An agent opens a case days later and wants the document data on screen.
- You reconcile your own records against the execution as SEON holds it.
Property | Webhook | Workflow Execution API |
|---|---|---|
| Delivery | Pushed when the execution finishes | Requested by you, at any time |
| Repeatable | One delivery per event | Unlimited, while the data is retained |
| Media links | Valid for 1 hour from delivery | Refreshed on every request |
| Best for | Reacting to a finished verification | Reading a result later, or after a missed webhook |
Regional endpoints
Call the region your account is provisioned in. An execution created in one region is not readable from another.
Region | Base URL |
|---|---|
| EU |
|
| US |
|
| APAC |
|
Authentication
Send your license key in the X-Api-Key header, the same key you use for init-workflow. No other authentication is required.
X-Api-Key: YOUR_LICENSE_KEY
Content-Type: application/jsonEvery request is scoped to the account behind the key. Requesting an execution ID that belongs to another account returns 404 rather than 403, so a wrong ID and someone else's ID are indistinguishable.
Getting the execution ID
Every request is keyed on the workflow execution ID, a UUID generated when a verification flow starts. You get it two ways:
- Synchronously, in the response to
POST /v1/init-workflow, asdata.executionId. - Asynchronously, in the completion webhook, as
data.id.
An id that is not a valid UUID returns 400.
Get workflow execution details
GET /v1/workflow-execution/{id}The endpoint returns everything SEON holds about the execution: the outcome, every check performed, all sub-check results, the extracted document data, and the media links.
Parameter | In | Required | Description |
|---|---|---|---|
| Path | Yes | Workflow execution UUID |
| Query | No | Set to true for the reduced, media-only payload. Defaults to false |
Get captured media only
GET /v1/workflow-execution/{id}?mediaOnly=trueSetting mediaOnly to true returns a reduced payload. Each entry in checks carries only checkType and capturedMedia, with no check results and no extracted data.
This is the fastest way to pull images and video. Use it where the media is all you need. FRAUD_API entries are excluded from the response, because that check captures no media.
HTTP Endpoint
Workflow Execution API - Response
Response envelope
Every response, success or failure, uses the same three keys. success is the boolean outcome of the request, and data carries the execution. On a failed request, error carries a code and a message, and data is an empty object. Check success before reading data, so a failed request does not read as an execution with no checks.
{ "success": true, "error": {}, "data": { } }{ "success": false, "error": { "code": "...", "message": "..." }, "data": {} }
Top-level fields
The data object describes the execution itself, with the per-check detail nested under checks.
Field | Type | Description |
|---|---|---|
| String (UUID) | The workflow execution ID |
| Enum | Outcome of the execution |
| String (UUID) | The workflow this execution was started from |
| String | The workflow name as configured in the Admin Panel |
| String (ISO 8601) | When the execution started |
| String (ISO 8601) or null | When the data was deleted under your retention policy. null while the data is held |
| Object, optional | Level of Identity Proofing result. Present only when it was evaluated |
| Array or null | One entry per check performed. null while the execution is in progress |
Execution status
status reports where the execution ended. Nothing under checks is populated until it reaches a final value.
Value | Meaning |
|---|---|
| The execution has not finished. checks is null |
| The execution finished and passed |
| The execution finished and needs manual review |
| The execution finished and failed |
| The execution was abandoned or timed out |
| The execution failed for a technical reason |
React to the webhook where you can, and poll a PENDING execution where you cannot.
Level of Identity Proofing result
The loip object is present only when Level of Identity Proofing (LoIP) was evaluated for the execution.
Field | Type | Description |
|---|---|---|
| Enum |
|
| String (ISO 8601) | When LoIP was evaluated |
| Array, optional | Included only when result is NONE. Each entry has a condition and an optional reason |
Check entries
checks holds one object per check, sorted oldest first by startedAt. Which result object is populated depends on checkType.
Check type | What it covers |
|---|---|
| Identity document verification: passport, ID card, driver's license |
| Selfie and liveness verification |
| Proof of address verification |
| Credit card verification |
| Custom evidence collection step |
| Data enrichment and fraud scoring. Excluded when mediaOnly is true |
Check entry status fields
Every check entry carries the same status and timing fields, whatever its type.
Field | Type | Description |
|---|---|---|
| Enum | One of the six check types |
| Enum |
|
| String or null | Extra detail behind the status, where there is any |
| String (ISO 8601) | When the check started |
| String (ISO 8601) or null | When the check finished |
| Enum or null |
|
| Boolean or null | Whether duplicate detection matched this person against an earlier session |
| Object or null | Download links for the captured media |
Check entry identifiers
Each check entry also carries the identifiers of the session and of the user you verified.
Field | Type | Description |
|---|---|---|
| String (UUID) or null | The verification session ID. null for FRAUD_API |
| String or null | The fraud transaction ID. Set only for FRAUD_API |
| String or null | Your reference ID, where you sent one |
| String or null | The email address on the session |
| String or null | The phone number on the session |
| String or null | The user_id you sent to init-workflow |
Result objects per check type
Each check type populates one result object and, for document and address checks, one extracted-data object.
Field | Type | Description |
|---|---|---|
| Object or null | Document sub-check results |
| Object or null | Data read off the identity document |
| Object or null | Liveness and face match results |
| Object or null | Address document sub-check results |
| Object or null | Data read off the address document |
| Object or null | Card verification results |
| Object or null | Evidence collection results, one entry per file |
Sub-check fields share one set of values: PASS, FAIL, REVIEW or NOT_PERFORMED. Each result object also carries an overallResult of APPROVED, REJECTED, FAIL, REVIEW, ABANDONED or RETRY_REQUIRED.
Extracted document data
documentCheckExtractedData is what SEON read off the identity document. A field appears only where it exists on that document type, so treat every one as optional. The personal details it carries are printed below.
Field | Type | Description |
|---|---|---|
| String | Full name as printed |
| String | Given name |
| String | Surname |
| String (date) | Date of birth |
| Number | Age in years at the time of the check |
| String | Gender as printed |
| String | Place of birth |
| String | Nationality |
| String | National or personal identification number |
| String | Address as printed on the document |
| String | Postal code |
Extracted document details
The same object carries the document's own identifiers and dates, and the chip data where a chip was read. Country and state codes follow the International Organization for Standardization (ISO) standards named in the table.
Field | Type | Description |
|---|---|---|
| Enum |
|
| String | Document number |
| String | Secondary number where the document carries one, for example the foreigner identity number (NIE) on a Spanish residency card |
| String (date) | Issue date |
| String (date) | Expiry date |
| String | Issuing country, ISO 3166-1 alpha-2 |
| String | Issuing state or province, ISO 3166-2 |
| Object | Data read from the document chip, where Near Field Communication (NFC) verification ran |
Document chip data
Where both are present, the chip data in rfidData is the stronger source. It is signed by the issuing authority, while the top-level fields are read optically from the printed surface.
The object carries its own fullName, givenNames, surname, dateOfBirth, documentNumber, expiryDate, nationality, issuingCountry, gender, placeOfBirth and address.
Extracted address data
proofOfAddressExtractedData covers the address document submitted to a proof of address check.
Field | Type | Description |
|---|---|---|
| String | The address as one string |
| Object | The same address split into houseNumber, houseName, subBuilding, street, locality, city, county, postcode and countryCode |
| String | Name on the document |
| String | Who issued the document, for example the utility or bank |
| String (date) | Issue date |
| String | Document number |
extractedAddress also appears, as a deprecated alias of address. Read address.
Document check sub-checks
documentCheckResult reports the authenticity and consistency of the identity document. Alongside overallResult it carries the authenticity sub-checks:
matchCheckResult,logicCheckResult,formatCheckResultandmrzCheckResult.barcodeAnomalyCheckResult,suspiciousDataCheckResultanddataIntegrityCheckResult.screenCheckResult,photocopyCheckResult,handPresenceCheckResultandphotoForgeryCheckResult.securityFeaturesCheckResult,documentValidityCheckResultandimageQualityCheckResult.iadCheckResultfor injection attack detection, with reason codes iniadRejectionReason.
It also carries the comparisons against data you supplied: ageVerificationCheckResult, nameMatchCheckResult, dateOfBirthCheckResult, postalCodeCheckResult and stateCheckResult.
verificationMethod is OCR or EID. On an electronic identity (eID) check, eidProvider, eidLevelOfAssurance and missingClaims are populated, and are null otherwise. Where NFC ran, nfcVerificationResult holds the chip authentication outcome.
Selfie check sub-checks
selfieVerificationResult reports the liveness and face match outcome of a selfie check. Alongside overallResult it carries:
livenessCheckResultandfaceMatchingResult.iadCheckResult, with reason codes iniadRejectionReason.dfdCheckResultfor deep fake detection, with detail indfdStatusDetail.mfdCheckResultfor multiple face detection, with detail inmfdStatusDetail.
Proof of address sub-checks
proofOfAddressCheckResult reports the outcome of the address document checks. Alongside overallResult it always carries five sub-checks:
addressDocumentCheckResult, on the document itself.addressDocumentMustNotBeExpiredCheckResult, on the document age.addressValidationCheckResultandfullAddressCheckResult, on the address.nameMatchCheckResult, against the name you supplied.
Where they are configured on the workflow, it also carries tamperValidationCheckResult and contentValidationCheckResult.
Credit card check sub-checks
creditCardVerificationResult reports the card data and the card checks. The full card number and the card verification value (CVV) never reach SEON, so they are never returned.
Alongside overallResult it carries:
- The card data
maskedPan,bin,lastFour,cardholderName,expiryDate,issuingNetworkandiban. - The checks
luhnCheckResult,nameMatchCheckResultandexpiryDateCheckResult. - The image checks
screenCheckResultFront,photocopyCheckResultFront,handPresenceCheckResultFrontand theirBackcounterparts. - A
nameVerificationobject withinputName,extractedName,matchScoreandmatchResult.
Evidence collection sub-checks
evidenceCollectionCheckResult reports the outcome of a custom evidence collection step. Alongside overallResult it carries selectedDocumentType and a files array.
Each entry in files has tamperingCheckResult, nameMatchCheckResult, documentType, captureMethod and an extractedFields object. The shape of extractedFields depends on the document, and covers transactions, daily balances, vehicle categories, key information and usage details.
Captured media fields
Each capturedMedia object holds presigned download links. Only the fields relevant to that check type, and to media actually captured, are present. A single-sided document has documentFront and no documentBack.
Field | What it is |
|---|---|
| Document front-side image |
| Cropped document front-side image |
| Document back-side image, two-sided documents only |
| Cropped document back-side image |
| Document barcode image |
| Document capture, or scan, video |
| Selfie video |
| Best-frame selfie image |
| Proof of address document file |
| Face image extracted from the document NFC chip |
| Array of files collected during an evidence collection step |
Responses after data is purged
Once an execution falls outside your data retention period, dataPurgedAt is set and the response changes shape rather than failing. You can still prove that an execution happened and what it decided, without SEON holding the personal data behind it.
A full request returns id, status, workflow, createdAt, dataPurgedAt and loip. Each check entry keeps only checkType, status, sessionId, startedAt and finishedAt, and every result object, extracted data field and media link is null.
With mediaOnly set to true, checks is an empty array.
Error responses
A failed request returns the code and message in error.
HTTP | Code | Message | When |
|---|---|---|---|
| 400 |
| Invalid input json | The execution ID is not a valid UUID |
| 401 |
| Failed to authenticate | The key is missing or invalid here |
| 404 |
| Incorrect value: workflow execution not found | The ID is unknown or another account's |
| 429 | [CONFIRM: does a 429 carry an error code and message, and if so which] | The rate limit was exceeded | |
| 500 |
| System error | Contact SEON support with the request ID |
| 503 |
| System error | A dependency was slow or unavailable |
A 503 is worth retrying. It usually means the verification data could not be assembled in time, not that the execution is gone.
Rate limits
Requests count against the queries-per-second limit of your account, the same budget as the rest of the SEON API. Exceeding it returns HTTP 429. Back off and retry rather than looping.
Requests are cheap and repeatable. For a high-volume flow, react to the webhook, then call this endpoint when you need the detail or fresh media. Do not poll every execution on a timer.
Response
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>
);
}