KYC/KYB endorsements

Learn how to use KYC endorsement endpoints to unlock gated virtual account channels.

Use endorsements to unlock gated virtual account channels, such as EUR or GBP, when an already onboarded and KYC-approved user needs an additional eligibility check.

When to use this flow

Use this flow when:

  • The user is already onboarded.
  • The user has successfully completed standard KYC.
  • The user or platform wants to create a specific virtual account that needs extra info.
  • The target channel requires one or more endorsements.

Endorsements are additional checks required by specific channels. A gated channel exposes the endorsement codes it requires. You request those endorsements for the account before creating the virtual account.

Expected flow

  1. List available channels.
  2. Check whether the desired channel requires endorsements - endorsementsRequiredfield.
  3. Request each required endorsement code for the account.
  4. Check whether the endorsement response created a new submission.
  5. If extra information is required, complete the new submission.
  6. Wait for the endorsement to be completed or approved.
  7. Create the virtual account.

Prerequisites

Before you start:

  1. Complete the user’s onboarding.

  2. Complete the user’s standard KYC flow.

  3. Store the account ID, for example:

    acct_DqvgNSAuQv5OEK9v
  4. Use the account ID in the Due-Account-Id header for all account-scoped KYC and endorsement requests.

  5. Check the channel requirements
    TODO edrs1 is not what we expose. need something meaningful


Endorsement codes are defined on channels.

When a channel is gated, the channel configuration contains the list of required endorsements. For example, a EUR virtual account channel may require an endorsement code such as:

edrs1

Retrieve all enabled payment channels with their specifications and pricing and search for your desired channel, then look for the endorsement field, endorsementsRequired.

GET /v1/channels


{
        "rail": "ach",
        "railSettings": {
            "rail": "ach",
            "speed": "1-2 days",
            "memoConfig": {
                "minCharacters": 0,
                "maxCharacters": 10,
                "required": false
            },
            "schemas": [
                "bank_us"
            ]
        },
        "currencyCode": "USD",
        "feeBps": 20,
        "feeFixed": "20",
        "limitMin": "1",
        "limitMax": "500000",
        "kycLevels": [],
        "type": "deposit",
        "accountType": "individual",
        "permissions": {
            "restrictedResidencies": [
                "US"
            ],
            "restrictedCategories": [
                "gambling"
            ]
        },
        "purposeCodes": {},
        "endorsementsRequired": [
            "edrs1"
        ]
    }

For the EUR/GBP virtual account migration, the relevant endorsement codes will be added to the EUR/GBP channels when those gated channels are enabled.

2. Request the endorsement

Request each endorsement required by the channel.

curl --request POST \
  --url https://api.sandbox.due.network/v1/kyc/endorsements/edrs1 \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Due-Account-Id: acct_DqvgNSAuQv5OEK9v'

Replace:

ValueDescription
<API_KEY>Your Due API key.
acct_DqvgNSAuQv5OEK9vThe account ID that needs access to the gated channel.
edrs1The endorsement code required by the channel.

3. Execute the endorsement

The endorsement response tells you whether additional information is required.

If no extra information is required, the endorsement can continue without opening a new data collection step.

If extra information is required, the response includes a new KYC submission.

Example response:

{
  "ownerId": "acct_DqvgNSAuQv5OEK9v",
  "code": "edrs1",
  "status": "requested",
  "submission": {
    "id": "ksub_i11Sowou6GxhBRux",
    "ownerId": "acct_DqvgNSAuQv5OEK9v",
    "applicantId": "ka_TQIIfHqJ86nsoWU4",
    "status": "open",
    "info": {},
    "requirements": [
      {
        "info": [
          {
            "key": "firstName",
            "type": "string"
          },
          {
            "key": "lastName",
            "type": "string"
          },
          {
            "key": "dob",
            "type": "date"
          },
          {
            "key": "phone",
            "type": "e164"
          },
          {
            "key": "nationality",
            "type": "iso3166_1_alpha2"
          }
        ],
        "kind": "static"
      },
      {
        "documents": [
          {
            "category": "ID",
            "oneOf": [
              {
                "type": "ID_CARD",
                "variants": ["FRONT_SIDE", "BACK_SIDE"]
              },
              {
                "type": "RESIDENCE_PERMIT",
                "variants": ["FRONT_SIDE", "BACK_SIDE"]
              },
              {
                "type": "PASSPORT"
              }
            ]
          },
          {
            "category": "SELFIE",
            "oneOf": [
              {
                "type": "SELFIE"
              }
            ]
          }
        ],
        "kind": "static"
      },
      {
        "kind": "oneOf",
        "oneOf": [
          {
            "info": [
              {
                "key": "address",
                "type": "object",
                "fields": [
                  {
                    "key": "country",
                    "type": "iso3166_1_alpha2"
                  },
                  {
                    "key": "postCode",
                    "type": "swift_x"
                  },
                  {
                    "key": "town",
                    "type": "swift_x"
                  },
                  {
                    "key": "street",
                    "type": "swift_x"
                  }
                ]
              },
              {
                "key": "location",
                "type": "object",
                "fields": [
                  {
                    "key": "latitude",
                    "type": "latitude"
                  },
                  {
                    "key": "longitude",
                    "type": "longitude"
                  }
                ]
              }
            ],
            "kind": "static"
          },
          {
            "documents": [
              {
                "category": "PROOF_OF_ADDRESS",
                "oneOf": [
                  {
                    "type": "UTILITY_BILL"
                  }
                ]
              }
            ],
            "kind": "static"
          }
        ]
      },
      {
        "info": [
          {
            "key": "bvn",
            "type": "string"
          }
        ],
        "kind": "static"
      },
      {
        "kind": "static",
        "questionnaires": [
          {
            "templateId": "declarationsApi",
            "questions": [
              {
                "id": "is_pep",
                "title": "Are you, or any of your relatives, a Politically Exposed Person (PEP)?",
                "requiredWhen": null,
                "schema": {
                  "type": "boolean"
                }
              },
              {
                "id": "is_sanction",
                "title": "Have you ever faced, or are you currently facing, sanctions of any kind?",
                "requiredWhen": null,
                "schema": {
                  "type": "boolean"
                }
              },
              {
                "id": "is_crime",
                "title": "Have you ever been, or are you currently being, convicted of a crime?",
                "requiredWhen": null,
                "schema": {
                  "type": "boolean"
                }
              }
            ]
          }
        ]
      }
    ],
    "documents": [],
    "questionnaires": [],
    "presetCode": "edrs1",
    "source": "api"
  }
}

4. Check whether a submission was created

Check the submission object in the endorsement response.

FieldDescription
ownerIdThe account that owns the endorsement.
codeThe endorsement code requested.
statusThe current endorsement status.
submission.idThe new submission ID created for missing information.
submission.statusThe submission status. An open submission requires action.
submission.requirementsThe information, documents, or questionnaire answers required for the endorsement.
submission.presetCodeThe endorsement code that created the submission.

If submission.status is open, collect and submit the required information before creating the virtual account.

5. Retrieve the submission by ID

Use the submission ID returned by the endorsement response.

curl --request GET \
  --url https://api.sandbox.due.network/v1/kyc/submissions/ksub_i11Sowou6GxhBRux \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Due-Account-Id: acct_DqvgNSAuQv5OEK9v'

The response shows the current submission state and the remaining requirements.

6. Complete the missing requirements

The endorsement submission uses the regular Due submission flow.

Depending on the requirements array, the user may need to provide:

  • Personal information, such as name, date of birth, phone, or nationality.
  • Identity documents, such as passport, ID card, or residence permit.
  • A selfie.
  • Address information or proof of address.
  • Questionnaire answers, such as declarations for PEP, sanctions, or criminal convictions.

There are two supported ways to complete the submission:

  1. Submit the information through the API.
  2. Send the user through Due’s hosted submission flow.

7. Option A: Submit the required information through the API

Use the existing KYC submission endpoints to provide the required information, documents, and questionnaire answers.

The requirements array tells you what to submit.

For example, this requirement means you need to provide five information fields:

{
  "info": [
    {
      "key": "firstName",
      "type": "string"
    },
    {
      "key": "lastName",
      "type": "string"
    },
    {
      "key": "dob",
      "type": "date"
    },
    {
      "key": "phone",
      "type": "e164"
    },
    {
      "key": "nationality",
      "type": "iso3166_1_alpha2"
    }
  ],
  "kind": "static"
}

This requirement means you need to provide an ID document and a selfie:

{
  "documents": [
    {
      "category": "ID",
      "oneOf": [
        {
          "type": "ID_CARD",
          "variants": ["FRONT_SIDE", "BACK_SIDE"]
        },
        {
          "type": "RESIDENCE_PERMIT",
          "variants": ["FRONT_SIDE", "BACK_SIDE"]
        },
        {
          "type": "PASSPORT"
        }
      ]
    },
    {
      "category": "SELFIE",
      "oneOf": [
        {
          "type": "SELFIE"
        }
      ]
    }
  ],
  "kind": "static"
}

This requirement means you can satisfy one of the available options:

{
  "kind": "oneOf",
  "oneOf": [
    {
      "info": [
        {
          "key": "address",
          "type": "object"
        },
        {
          "key": "location",
          "type": "object"
        }
      ],
      "kind": "static"
    },
    {
      "documents": [
        {
          "category": "PROOF_OF_ADDRESS",
          "oneOf": [
            {
              "type": "UTILITY_BILL"
            }
          ]
        }
      ],
      "kind": "static"
    }
  ]
}

In this case, provide either:

  • Address and location information, or
  • A proof-of-address document.

After all required information has been provided, complete the submission using the regular submission completion endpoint.

8. Option B: Use the hosted submission flow

You can also use Due’s hosted solution to collect the missing information from the user.

Use this option if you do not want to collect and submit the required data directly through your own API integration.

The endorsement submission uses Due’s hosted solution. Do not mix this up with Sumsub links from the standard KYC flow.

The hosted flow should be opened for the new submission created by the endorsement request.

Use the submission.id from the endorsement response:

ksub_i11Sowou6GxhBRux

The user completes the missing information in the hosted flow. After completion, the endorsement can continue processing.

Troubleshooting

The endorsement response does not include a link

POST /v1/kyc/endorsements/{code} does not return a Sumsub link.

If extra data is required, the response returns a Due submission object. Use that submission with either:

  • The regular KYC submission API flow, or
  • Due’s hosted submission flow.

The endorsement created a submission, but GET /v1/kyc/submissions does not show it

Use the submission ID returned by the endorsement response and retrieve it directly:

curl --request GET \
  --url https://api.sandbox.due.network/v1/kyc/submissions/ksub_i11Sowou6GxhBRux \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Due-Account-Id: acct_DqvgNSAuQv5OEK9v'

The submission cannot be found

Check the Due-Account-Id header.

The submission belongs to the account that requested the endorsement. If you use another account ID, the API may return a not found error.

Correct:

Due-Account-Id: acct_DqvgNSAuQv5OEK9v

Incorrect:

Due-Account-Id: <another_account_id>

The channel does not show an endorsement code

Endorsement codes are defined on channels.

If the channel does not include an endorsement requirement yet, there is no endorsement code to request for that channel. When the gated channel configuration is enabled, the channel will expose the required endorsement codes.

Summary

For gated virtual account channels, use this endorsement flow:

  1. Check the target channel for required endorsement codes.
  2. Request each endorsement with POST /v1/kyc/endorsements/{code}.
  3. If the response includes a new submission, complete that submission.
  4. Use either the KYC submission API flow or Due’s hosted flow.
  5. Confirm the endorsement is no longer blocking the channel.
  6. Create the virtual account.