Onboarding & compliance lifecycle

Understand submissions, information requests, endorsements, and account KYC statuses, then use webhooks to show users the tasks they need to complete.

After an account is created, Due sometimes needs more information from your user: to finish KYC, to open a new rail, to let a transfer through, or to answer a compliance question. This guide explains the objects involved one by one, lists every status, and then shows how to build on webhooks so you always know whether your user has something to do.

The building blocks

Submission: the form

A submission is a form your user fills in. It lists what Due needs (requirements: fields, documents, questionnaires, agreements) and holds what has been provided so far.

  • Every time Due needs information from a user, it is collected through a submission. There is no other way to provide data.
  • You fill it through the API (/v1/kyc/submissions/{id}/…) or send the user to a hosted link.
  • It is editable while it is open. Once completed it is frozen: a correction is always a new submission, never an edit of the old one.

A submission tells you what to collect. It does not tell you why it is needed or whether it was accepted. That is what the next objects are for.

→ Submission statuses · How to fill one: Individual KYC process, Business KYB process · Hosted submission link

Information request: the reason and the review

An information request is Due asking for information, with a reason, and reviewing the answer. Think of it as a container for submissions rather than as a record of its own: it points to the submission being reviewed (submissionId), and if that submission needs correcting it opens another one and asks again.

Due raises an information request when it needs something beyond base KYC:

WhyExamplekindAbout (submission.subject.kind)
Open a railA partner needs the state of residence for brl_bank_v1endorsementapplicant
Compliance check on the accountSource-of-funds questionnaire, enhanced due diligence, video identificationdue_diligenceapplicant
A partner's follow-upA bank asks for proof of source of wealthdue_diligenceapplicant
Let a transfer throughA large payment needs an invoicedue_diligencetransfer or transaction
Know the other partyThe relationship with a recipientdue_diligencecounterparty

Information requests can appear at any time, including long after onboarding. Only those with kind: endorsement belong to an endorsement (their presetCode is the endorsement code).

→ Information request statuses · Requests for information on transfers · Video-identification EDD

Endorsement: a rail to unlock

An endorsement is an additional approval that unlocks specific channels, for example named EUR accounts or USD accounts at a partner bank (see endorsementsRequired in GET /v1/channels). You request it; if more information is needed, Due raises information requests for it. Once they are approved, the endorsement continues by itself.

→ Endorsement statuses · KYC/KYB endorsements

Account KYC: the base verification

kyc.status on the account is the result of base KYC/KYB. Base KYC is the one case with no information request: its form is a submission on its own, and the outcome shows on kyc.status.

→ Account KYC statuses · Handle KYC status webhooks · Sumsub KYC Sharing

How they fit together

You seeIt means
A submission with an information request pointing to itDue asked for this for the reason given by the information request
A submission with no information requestBase KYC (first verification, or something to redo)
An endorsementA rail waiting for its information requests, then for the partner

The one rule to build on: your user has a task when a submission is open and it either belongs to an information request that is requested, or is a base-KYC submission while kyc.status is pending or resubmission_required. See What the user sees and Opening a task.


Statuses

Submission

StatusMeaningUser has work?
openWaiting for the userYes
processingThe user completed it; Due is handing it to review (usually seconds)No
completedHanded to review. Says nothing about whether it was acceptedNo
stateDiagram-v2
    direction LR
    [*] --> open
    open --> processing: user completes
    processing --> completed

A submission only moves forward. Once it leaves open it can't be read or written through /v1/kyc/submissions/{id}, and its hosted link stops working.

Information request

StatusMeaning
requestedWaiting for the information. It stays requested until its submission is completed, so for a moment after the user finishes it is still requested
on_reviewUnder review
approvedAccepted. Whatever was waiting on it continues
rejectedNot accepted, and final. Nothing more can be submitted
expiredIt was approved and its validity has ended. Nothing is wrong and there is nothing to do: it simply stops counting as approved, and a new information request is raised the next time a policy needs it. Only due_diligence information requests expire
stateDiagram-v2
    direction LR
    [*] --> requested
    [*] --> approved: nothing was missing
    requested --> on_review
    on_review --> approved
    on_review --> requested: resubmission needed, new submission
    on_review --> rejected
    rejected --> [*]
    approved --> expired: validity ended

If a review needs something corrected, the information request goes back to requested rather than being rejected. It keeps its id and carries a new submissionId, and that new submission is the user's task. Watch the status, not the id.

rejected is final. It is not a step on the way to another attempt.

An information request with submissionId: null needed nothing new and was created approved. A fast review can go from requested straight to approved or rejected.

Endorsement

StatusMeaning
requestedWaiting for its information requests
processingDue is registering the account at the partner
awaiting_approvalThe partner is reviewing
approvedThe rail works
processing_errorRegistration failed or the partner refused. Contact Due

There is no rejected. An endorsement can be requested only when kyc.status is passed. If nothing is missing it starts at processing or approved.

If an endorsement's information request is rejected, the endorsement stays requested. It does not move to an error status, and the rail does not open. Read the endorsement's information requests to know whether it is still progressing.

Account KYC

StatusWhat to do
pending / resubmission_requiredThe user completes the open base-KYC submission. If there is none, open the hosted KYC flow (POST /v1/kyc/session)
manual_reviewWait
passedVerified. Keep handling information requests
failedRejected. Keep KYC-gated services blocked

A status can change again after a review (for example passed → resubmission_required). Always act on the latest value.


Building on webhooks

What to store

TableKeyFields
accountsaccount idstatus, kycStatus
submissionssubmission idaccountId, status, informationRequestId (empty for base KYC), subjectKind, presetCode
information_requestsinformation request idaccountId, kind, presetCode, status, submissionId, createdAt
endorsementsaccount id + codestatus

Where each row first comes from

What starts itWhoFirst seen
Base KYC (start or Sumsub sharing)youThe response to your call
Base KYC has to be redoneDuetransfers.kyc.submission.status_changed with status: open
Endorsementyou, POST /v1/kyc/endorsements/{code}The response: informationRequests[] and their submission. No webhook is sent for these
Compliance check, transfer, counterparty, partner follow-up, resubmissionDueinformation_request.updated with status: requested (includes submission)

Store what your own calls return, and let webhooks create everything Due starts. A submission webhook for an id no information request claims is base KYC.

What each webhook carries

Body: { id, type, data, occurredAt, attemptedAt }. occurredAt is when Due recorded the event for delivery, not when the change itself happened, but it stays the same across redeliveries of the same event. ownerId is the account id everywhere.

typedataSent when
transfers.kyc.submission.status_changedThe submissionIt becomes processing, then completed. Also once with open when Due opens a new base-KYC submission, either a redo or because Sumsub sharing left data missing
information_request.updatedThe information request, including submissionDue raises an information request (requested), or one becomes on_review, approved or rejected, or one under review goes back to requested with a new submission
endorsement.status_changed{ endorsement: { ownerId, code, status } }An endorsement changes status (not for the initial requested)
bp.kyc.status_changedThe account, with status and kyc.statuskyc.status changes

Deliveries can be retried and can arrive out of order. Make handlers idempotent; keep a submission's latest status (it only moves forward), and for the other objects ignore events with an older occurredAt than the one you last applied.

What the user sees

function tasks(account):
    if account.status == "deactivated" or account.kycStatus == "failed":
        return []
    return submissions where accountId == account.id and status == "open" and (
             (informationRequestId is set and information_requests[informationRequestId].status == "requested")
          or (informationRequestId is empty and account.kycStatus in ["pending", "resubmission_required"]))

If there are no tasks, the user waits:

Your rows sayShowYou do
kycStatus is resubmission_required and no open base-KYC submissionThe hosted KYC flow (POST /v1/kyc/session)
An information request is rejectedThe information was not acceptedNothing (final)
An information request is expiredNothingNothing. A new one is raised when a policy needs it
An endorsement is processing_error"We're looking into it"Contact Due
Anything else"Under review"Nothing

Opening a task

Get the link when the user taps the task; don't store it.

  • Submission with an information request: POST /v1/information_requests/{informationRequestId}/session
  • Base-KYC submission: POST /v1/kyc/sessions with {"submissionId": "…"}

Then open https://app.due.network/kyc_submissions/{submissionId}?token={token}.

  • 422 err_kyc_submission_finalized means the submission was completed meanwhile: mark it as no longer open and refresh.
  • A token lasts 7 days. A token being issued does not mean the submission is open; your submissions.status does.

To render the form yourself instead, use the submission's requirements, write through /v1/kyc/submissions/{id}/… and finish with POST /v1/kyc/submissions/{id}/complete.

What webhooks don't tell you

ChangeHow to catch it
An endorsement and its information requests are createdStore the response to your own call

You don't need to track expiry. When an approved information request stops counting, Due raises a new one and you receive it as requested.

To repair one account's rows (missed delivery, new deployment), rebuild them from GET /v1/accounts/{id}, GET /v1/information_requests, GET /v1/kyc/submissions and GET /v1/kyc/endorsements.

When to contact Due

Your rows saySend
An endorsement is processing_erroraccount id, endorsement code
A submission has been processing for more than a few minutesaccount id, submission id
An information request is requested but no longer seems neededaccount id, information request id

Mistakes to avoid

  • Driving tasks from the information request's status. requested outlives the user's work; use submission.status == "open".
  • Waiting for a webhook to learn an endorsement's submissions. They are in the response to your call.
  • Guessing what a submission is for from presetCode. Use its information request; if none, it's base KYC.
  • Treating an information request id as one round of review. The same id can be sent back to requested with a new submission.
  • Treating requested as always meaning a first attempt. It also means a correction was asked for.
  • Showing base-KYC submissions once kycStatus is passed or manual_review. Leftover submissions are not closed.
  • Moving a submission backward because an older webhook arrived late.

Example: opening a rail that needs one more field

What happensYou receiveYour rows afterTasks
You call POST /v1/kyc/endorsements/brl_bank_v1The responseendorsement requested, information request requested, submission openthe submission
The user completes the submissiontransfers.kyc.submission.status_changed (processing)submission processingnone
It reaches reviewtransfers.kyc.submission.status_changed (completed), information_request.updated (on_review)submission completed, information request on_reviewnone
Review passesinformation_request.updated (approved), endorsement.status_changed (processing)information request approved, endorsement processingnone
The partner acceptsendorsement.status_changed (approved)endorsement approvednone

If the review needs something corrected, you receive information_request.updated (requested) for the same information request, now carrying a new submissionId. That new submission shows up as a task.

If the review rejects it, the information request becomes rejected and the endorsement stays requested. The rail does not open and there is nothing for the user to do.


Did this page help you?