Individual KYC process
Core concepts
- The account KYC process is built around submissions, which are essentially sessions with requirements and data you put into them.
- After fulfilling all requirements, a submission may be closed and becomes uneditable.
- After the submission has been reviewed, the applicant becomes active.
- If new data is required, a new submission may be created for an applicant.
In this guide, we'll cover the KYC process for an individual account.
Create an account
You want to call the create account endpoint first if you have not done it yet.
curl --request POST \
--url https://api.due.network/v1/accounts \
--header 'accept: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'content-type: application/json' \
--data '
{
"type": "individual",
"name": "John Snow",
"email": "[email protected]",
"country": "GB",
"category": "employed"
}
'Grab the id from the response - this is the ACCOUNT_ID.
{
"id": "acct_DlsiOQ7RxG1hgJVL",
// ...
}Initiate the KYC process
curl --request POST \
--url https://api.due.network/v1/kyc \
--header 'Due-Account-Id: <ACCOUNT_ID>' \
--header 'accept: application/json' \
--header 'authorization: Bearer <API_KEY>'This will return a submission entity containing all the requirements you need to fulfill.
{
"id": "ksub_hvwkgs0ATW36rTpw",
"applicantId": "acct_DlsiOQ7RxG1hgJVL",
"status": "open",
"reason": "initial",
"requirements": [
{
"kind": "static",
"info": [...],
"documents": [...],
"questionnaires": [...]
},
{
"kind": "conditional",
"if": [...],
"then": [...]
},
{
"kind": "oneOf",
"oneOf": [...]
}
],
"documents": [],
"questionnaires": []
}The id field would be the SUBMISSION_ID.
Understanding requirements
Want a visual overview? See the Individual KYC requirements diagram for the current verification flow without diving into the schema structure.
Note: The tax residence country used to determine applicable requirements is derived from the
countryfield provided when creating the account.
The requirements field contains an array of requirement objects. The structure is uniform across all requirement types, allowing for flexible and composable verification flows.
Note: Examples in this documentation are illustrative and may not reflect actual requirements. Always refer to the requirements returned by the API for your specific account.
Top-level structure
The requirements array uses AND logic — every item in the array must be satisfied for the submission to be complete.
Requirement kinds
Each requirement object has a kind field indicating its type:
| Kind | Description |
|---|---|
static | Always required. Contains info, documents, and/or questionnaires fields. |
conditional | Only required when a condition is met. Contains if and then fields. |
oneOf | Exactly one of the options must be satisfied. Contains a oneOf array. |
Static requirements
Static requirements are always required regardless of any conditions.
{
"kind": "static",
"info": [
{
"key": "firstName",
"type": "string"
},
{
"key": "lastName",
"type": "string"
}
],
"documents": [
{
"category": "ID",
"oneOf": [
{ "type": "PASSPORT" },
{ "type": "ID_CARD", "variants": ["FRONT_SIDE", "BACK_SIDE"] }
]
}
],
"questionnaires": [
{
"templateId": "pep_sanctions",
"questions": [...]
}
]
}Conditional requirements
Conditional requirements are only required when the if condition is satisfied. The if field contains an array of requirements (using AND logic) that act as the condition. The then field contains an array of requirements to fulfill when the condition is met.
{
"kind": "conditional",
"if": [
{
"kind": "static",
"documents": [
{
"category": "ID",
"oneOf": [
{ "type": "PASSPORT" }
]
}
]
}
],
"then": [
{
"kind": "static",
"info": [
{
"key": "passportExpiryDate",
"type": "date",
"required": true
}
]
}
]
}In this example, the passportExpiryDate field is only required if a PASSPORT is provided.
OneOf requirements
OneOf requirements allow the applicant to satisfy exactly one of the provided options.
{
"kind": "oneOf",
"oneOf": [
{
"kind": "static",
"info": [
{
"key": "address",
"type": "object",
"fields": [
{ "key": "country", "type": "iso3166_1_alpha2" },
{ "key": "postCode", "type": "string" },
{ "key": "town", "type": "string" },
{ "key": "street", "type": "string" }
]
}
]
},
{
"kind": "static",
"documents": [
{
"category": "PROOF_OF_ADDRESS",
"oneOf": [
{ "type": "UTILITY_BILL" },
{ "type": "OTHER" }
]
}
]
}
]
}In this example, the applicant can either provide a structured address or upload a proof of address document.
Nesting
Requirements can be nested. For example, a conditional requirement's then clause can contain oneOf requirements, and vice versa. This allows for complex verification flows while maintaining a uniform structure.
Document structure
The documents array describes required documents grouped by category. Each category contains a oneOf array listing acceptable document types.
{
"category": "ID",
"oneOf": [
{ "type": "PASSPORT" },
{ "type": "ID_CARD", "variants": ["FRONT_SIDE", "BACK_SIDE"] },
{ "type": "RESIDENCE_PERMIT", "variants": ["FRONT_SIDE", "BACK_SIDE"] }
]
}The variants field indicates which parts of a document need to be uploaded. If no variants are specified, a single upload is expected.
Provide applicant information
From the requirements, identify all info fields across static and applicable conditional requirements:
curl --request POST \
--url https://api.due.network/v1/kyc/submissions/<SUBMISSION_ID>/info \
--header 'Due-Account-Id: <ACCOUNT_ID>' \
--header 'accept: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'content-type: application/json' \
--data '
{
"firstName": "John",
"lastName": "Snow",
"dob": "1980-01-01",
"phone": "+440000000000",
"taxResidenceCountry": "GB"
}
'Add verification documents
Step 1: Request document upload token
To upload a document, request a token specifying the document type, issuing country, and filename.
curl --request POST \
--url https://api.due.network/v1/kyc/submissions/<SUBMISSION_ID>/documents \
--header 'Due-Account-Id: <ACCOUNT_ID>' \
--header 'accept: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'content-type: application/json' \
--data '
{
"type": "PASSPORT",
"issuingCountry": "GB",
"filename": "passport.jpg"
}
'For documents with variants, include the variant field:
{
"type": "ID_CARD",
"variant": "FRONT_SIDE",
"issuingCountry": "GB",
"filename": "id_card_front.jpg"
}This returns a token for uploading:
{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..."
}Step 2: Upload the document
curl --request POST \
--url https://api.due.network/v1/kyc/submissions/documents/<TOKEN> \
--header 'Due-Account-Id: <ACCOUNT_ID>' \
--header 'accept: application/json' \
--header 'authorization: Bearer <API_KEY>' \
--header 'content-type: image/jpeg' \
--data-binary '@path/to/passport.jpg'Repeat for all required documents based on the applicable requirements.
Complete the submission
After all requirements are fulfilled, complete the submission:
curl --request POST \
--url https://api.due.network/v1/kyc/submissions/<SUBMISSION_ID>/complete \
--header 'Due-Account-Id: <ACCOUNT_ID>' \
--header 'accept: application/json' \
--header 'authorization: Bearer <API_KEY>'After review and approval, a webhook is sent:
{
"type": "bp.kyc.status_changed",
"data": {
"id": "<ACCOUNT_ID>",
"kyc": {
"status": "passed"
}
}
}Updated 28 minutes ago