Running KYC

KYC process overview

After your customer has provided the information necessary to complete an onboarding flow, you must authenticate and verify that information.

This is a critical step, known as KYCKYC - Know Your Customer. The compliance that financial institutions put into their policies and procedures to verify a customer's identity. in which you validate your customer's identity and documents, and ensure they're compliant with federal regulations.

Using the Customer object, you can perform a host of checks and screenings to ensure compliance programmatically. The full KYC process is shown below.

As part of the KYC check, you might receive a webhook event (kyc.verification.document_required) asking for documents to be uploaded for validation. This might happen, for example, when a customer’s name does not match with the name in an SSN lookup. For details regarding how to upload documents, see KYC document upload.

Starting a KYC process

To initiate a KYC request, use the POST /{customer_id}/verification-kyc operation and provide the parameters as shown in the table below.

Parameter

Type

Description

program_id
required

String

Program UUID, for example 72585109-8222-4221-b15b-48e87ffed790.

ssn
required

String

Social security number of the form ######### or ###-##-####, for example 123-45-6789.

phone
required

String

Numeric or numeric/dashes, for example 555-111-2222.

phone_country_code
required

String

Numeric, between 1 and 3 digits, for example 325.

email
required

String

Valid email address, for example [email protected]

ip
conditionally required

String

Valid IPV4 dot-separated address, for example 192.168.1.139
can be conditionally required based on program_id

The combination of customer_id and program_id in the request indicates that Bond will do a verification check on a particular customer on behalf of a particular bank partnered with the brand.

The KYC request for the customer is asynchronous (meaning that the reply is not always immediate), so you must configure a webhook to listen for KYC events.

An example of a KYC request is shown below.

curl --request POST \ 
--url 'https://sandbox.bond.tech/api/v0/customers/{CUSTOMER_ID}/verification-kyc' \
--header 'Identity: YOUR-IDENTITY' \
--header 'Authorization: YOUR-AUTHORIZATION' \
--header 'Content-Type: application/json' \
--data-raw '{
    "program_id": "b50063df-0b39-43f7-9458-25b08a75d42a",
    "ssn": "000-00-0000",
    "phone": "555-555-5555",
    "phone_country_code": "1",
    "email": "[email protected]",
    "ip": "127.1.1.1"
}
require "uri"
require "net/http"

url = URI("https://sandbox.bond.tech/api/v0/customers/{CUSTOMER_ID}/verification-kyc")

http = Net::HTTP.new(url.host, url.port);
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(url)
request["Identity"] = YOUR-IDENTITY
request["Authorization"] = YOUR-AUTHORIZATION
request["Content-Type"] = "application/json"
request.body = "{\n    \"program_id\": \"b50063df-0b39-43f7-9458-25b08a75d42a\",\n    \"ssn\": \"000-00-0000\",\n    \"phone\": \"555-555-5555\",\n    \"phone_country_code\": \"1\",\n    \"email\": \"[email protected]\",\n    \"ip\": \"127.1.1.1\"\n}"

response = http.request(request)
puts response.read_body
const options = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Identity: 'YOUR-IDENTITY',
    Authorization: 'YOUR-AUTHORIZATION'
  },
  body: JSON.stringify({
    program_id: 'bf6d856f-0288-414a-bd13-f9afdd473da6',
    ssn: '123-45-6789',
    phone: '555-111-2222',
    phone_country_code: '1',
    email: '[email protected]',
    ip: '257.134.1.57'
  })
};

fetch('https://sandbox.bond.tech/api/v0/customers/931e2341-c3eb-4681-97d4-f6e09d90da14/verification-kyc', options)
  .then(response => response.json())
  .then(response => console.log(response))
  .catch(err => console.error(err));
import requests

url = "https://sandbox.bond.tech/api/v0/customers/{CUSTOMER_ID}/verification-kyc"

payload="{\n    \"program_id\": \"b50063df-0b39-43f7-9458-25b08a75d42a\",\n    \"ssn\": \"000-00-0000\",\n    \"phone\": \"555-555-5555\",\n    \"phone_country_code\": \"1\",\n    \"email\": \"[email protected]\",\n    \"ip\": \"127.1.1.1\"\n}"
headers = {
  'Identity': YOUR-IDENTITY,
  'Authorization': YOUR-AUTHORIZATION,
  'Content-Type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data = payload)

print(response.text.encode('utf8'))
var client = new RestClient("https://sandbox.bond.tech/api/v0/customers/{CUSTOMER_ID}/verification-kyc");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Identity", "e6b234b4-650e-48e4-93eb-6550a07cc075");
request.AddHeader("Authorization", "sasAXw8ElVJihL3+3mzItCsVtKzh4Kqa0xdECC/F1GbwA6LQTAs/KxkZ9QFYST3l");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n    \"program_id\": \"b50063df-0b39-43f7-9458-25b08a75d42a\",\n    \"ssn\": \"000-00-0000\",\n    \"phone\": \"555-555-5555\",\n    \"phone_country_code\": \"1\",\n    \"email\": \"[email protected]\",\n    \"ip\": \"127.1.1.1\"\n}",  ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Unirest.setTimeouts(0, 0);
HttpResponse<String> response = Unirest.post("https://sandbox.bond.tech/api/v0/customers/{CUSTOMER_ID}/verification-kyc")
  .header("Identity", "e6b234b4-650e-48e4-93eb-6550a07cc075")
  .header("Authorization", "sasAXw8ElVJihL3+3mzItCsVtKzh4Kqa0xdECC/F1GbwA6LQTAs/KxkZ9QFYST3l")
  .header("Content-Type", "application/json")
  .body("{\n    \"program_id\": \"b50063df-0b39-43f7-9458-25b08a75d42a\",\n    \"ssn\": \"000-00-0000\",\n    \"phone\": \"555-555-5555\",\n    \"phone_country_code\": \"1\",\n    \"email\": \"[email protected]\",\n    \"ip\": \"127.1.1.1\"\n}")
  .asString();

The results of the KYC will be sent via webhook events. For information on subscribing to Bond webhook events, see Event subscriptions and Accepting webhook requests.

A example of a KYC response is shown below.

{
    "customer_id": "2d5a1e70-2662-42a3-bb90-52e9bd450a62",
    "kyc_status": "initialized"
}

Examples of KYC responses are shown below.

{
    "event": "kyc.verification.success",
    "customer_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00"
}
{
    "event": "kyc.verification.failure",
    "customer_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00"
}
{
    "event": "kyc.verification.kba",
    "customer_id": "08bf97a6-ea23-457e-bec3-e4de068a3df6",
    "occurred_at": "2021-02-02-00:50:59.749551+00:00",
    "reasons": {
        "ofac": "passed",
        "bureau": "kba triggered",
        "mobile": "passed",
        "ip": "passed",
        "email": "passed"
    },
    "kba": [
        {
            "question_id": "1",
            "question": "Which one of the following retail credit cards do you have? If there is not a matched retail credit card, please select 'NONE OF THE ABOVE'",
            "choices": [
                {
                    "choice_id": "1",
                    "choice": "AMAZON"
                },
                {
                    "choice_id": "2",
                    "choice": "TARGET"
                },
                {
                    "choice_id": "3",
                    "choice": "COSTCO"
                },
                {
                    "choice_id": "4",
                    "choice": "KOHLS"
                },
                {
                    "choice_id": "5",
                    "choice": "NONE OF THE ABOVE"
                }
            ]
        }
    ]
}
{
    "event": "kyc.verification.error",
    "customer_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00"
}
{
    "event": "kyc.verification.timeout",
    "customer_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00"
}
{
    "event": "kyc.verification.documents_required",
    "customer_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00",
    "documents":[
      {
        "document_type": "government_id",
        "upload_link": "https://withpersona.com/verify?template-id=tmpl_111111111111111111111111&reference-id=a5bcf5a8-c4e0-4025-8183-5346176ee3db",
        "status": "required"
      },
      {
        "document_type": "utility_bill",
        "upload_link": "https://withpersona.com/verify?template-id=tmpl_222222222222222222222222&reference-id=a5bcf5a8-c4e0-4025-8183-5346176ee3db",
        "status": "required"
      },
      {
        "document_type": "social_security_card",
        "upload_link": "https://withpersona.com/verify?template-id=tmpl_333333333333333333333333&reference-id=a5bcf5a8-c4e0-4025-8183-5346176ee3db",
        "status": "required"
      }, 
    ]
}
{
    "event": "kyc.verification.under_review",
    "customer_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00"
}

After a customer has been successfully vetted, further KYC attempts are not be allowed. The KYC service responds with an error and displays the timestamp of the previous successful KYC request.

For a complete specification and interactive examples, see Starting the KYC process in the Bond API Reference.

KYC webhook events

We provide a kyc.verification.status passed/failed response to the callback_url configured in the webhook. If the kyc.verification.status is passed, the customer's information has been validated and they are eligible to access the services provided by the bank. You can now create a card for the customer.

If the response returned is failed, this triggers a KBAKBA - Knowledge Based Authentication. A form of identity validation done by prompting the user to answer questions based on their private information. Performed in the event that the KYC process fails. Generally KBA is interchangeable with OOW. For example, "Out of these 4 addresses, in which have you resided previously?". request. The KBA process prompts the customer to answer multiple-choice questions that are easy to answer for them, but difficult for others. If the KBA process fails, the overall customer verification has failed.

Event values

Description

kyc.verification.error

KYC failed to complete due to server error. Send a POST /customer/{customer_id}/verification-kyc to retry

kyc.verification.failure

KYC failed due to low confidence in identity validation

kyc.verification.kba

KYC requires further information to continue

kyc.verification.success

KYC passed

kyc.verification.timeout

KYC failed to complete due to connection timeout. Send a POST /customer/{customer_id}/verification-kyc to retry

kyc.verification.document_required

KYC requires further information to continue. This includes a documents field that indicates the types of documents required. See documents required.

kyc.verification.under_review

KYC documents submitted are being reviewed.

Sometimes these events may include a description field in the payload prompting further action, as shown in the "PO Box" failure example below that requests a new address.

{
    "event": "kyc.verification.failure",
    "customer_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00",
    "error_code": "po_box_failure",
    "description": "Customer address is a PO Box. Request a new address"
}

For further information, see Webhooks and Webhook events and subscriptions.

For a complete specification and interactive examples, see Create webhook subscription in the Bond API Reference.

Knowledge Based Authentication (KBA)

KBA is triggered when a KYC validation fails. KBA allows users to personally validate their identity through multiple-choice questions that should be straightforward for them to answer, but difficult for others to answer.

KBA includes up to three rounds of four multiple-choice questions each. If a customer answers a question incorrectly, they are posed new questions. After three failed attempts at answering questions, the overall KYC process fails.

To send the customer's answers to Bond, use the POST /customers/{customer_id}/verification-kba operation and provide the question_id and choice_id pair array parameters as shown below. The kba_responses object has the following structure:

Object

Type

Description

kba_responses
required

Array of objects.

Array of question-answer objects.

The question_id and choice_id fields within kba_responses is a list of objects with the following structure:

kba_responses Object

Type

Description

question_id
required

String

Corresponds to a question_id from the /kyc/programmatic response.

choice_id
required

String

Corresponds to the choice_id for this question from the /kyc/programmatic response.

KBA submission example

curl --request POST \
  --url https://sandbox.bond.tech/api/v0/customers/2df10ec1-130f-41bb-b3df-f3af48350eb7/verification-kba \
  --header 'Authorization: YOUR-AUTHORIZATION' \
  --header 'Content-Type: application/json' \
  --header 'Identity: YOUR-IDENTITY' \
  --data '{"kba_responses":[{"question_id":"846bew67b4qw8b6c784tr","choice_id":"764cbw4r367bcrt"}]}'
const fetch = require('node-fetch');

const url = 'https://sandbox.bond.tech/api/v0/customers/2df10ec1-130f-41dd-b0cf-f3af48350eb7/verification-kba';

const options = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Identity: 'YOUR-IDENTITY',
    Authorization: 'YOUR-AUTHORIZATION'
  },
  body: JSON.stringify({
    kba_responses: [{question_id: '846bew67b4qw8b6c784tr', choice_id: '764cbw4r367bcrt'}]
  })
};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('error:' + err));
const options = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Identity: 'YOUR-IDENTITY',
    Authorization: 'YOUR-AUTHORIZATION'
  },
  body: JSON.stringify({
    kba_responses: [{question_id: '846bew67b4qw8b6c784tr', choice_id: '764cbw4r367bcrt'}]
  })
};

fetch('https://sandbox.bond.tech/api/v0/customers/2df10ec1-130f-41bb-b0cf-f3af48350eb7/verification-kba', options)
  .then(response => console.log(response))
  .catch(err => console.error(err));
import requests

url = "https://sandbox.bond.tech/api/v0/customers/2df10ec1-130f-41ee-b0cf-f3af48350eb7/verification-kba"

payload = {"kba_responses": [
        {
            "question_id": "846bew67b4qw8b6c784tr",
            "choice_id": "764cbw4r367bcrt"
        }
    ]}
headers = {
    "Content-Type": "application/json",
    "Identity": "YOUR-IDENTITY",
    "Authorization": "YOUR-AUTHORIZATION"
}

response = requests.request("POST", url, json=payload, headers=headers)

print(response.text)
var client = new RestClient("https://sandbox.bond.tech/api/v0/customers/2df10ec1-130f-41bb-b0cf-f3af48350eb7/verification-kba");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Identity", "YOUR-IDENTITY");
request.AddHeader("Authorization", "YOUR-AUTHORIZATION");
request.AddParameter("application/json", "{\"kba_responses\":[{\"question_id\":\"846bew67b4qw8b6c784tr\",\"choice_id\":\"764cbw4r367bcrt\"}]}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"kba_responses\":[{\"question_id\":\"846bew67b4qw8b6c784tr\",\"choice_id\":\"764cbw4r367bcrt\"}]}");
Request request = new Request.Builder()
  .url("https://sandbox.bond.tech/api/v0/customers/2df10ec1-130f-41aa-b0cf-f3af48350eb7/verification-kba")
  .post(body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Identity", "YOUR-IDENTITY")
  .addHeader("Authorization", "YOUR-AUTHORIZATION")
  .build();

Response response = client.newCall(request).execute();

The possible responses to a KBA process are shown below.

{
    "customer_id": "2df10ec1-130f-41bb-b0cf-f3af48350eb7",
    "kyc_status": "passed"
}
{
    "customer_id": "2df10ec1-130f-41bb-b0cf-f3af48350eb7",
    "kyc_status": "kba failed"
}

KBA error examples

If KBA has failed and you submit a fourth attempt or KBA has previously been completed successfully, you receive one of the response shown below.

{
"Message":"Maximum number of attempts exceeded message"
"Status":400
"Code":"kyc_error"
"Type":"KYC Error"
}
{
"Message":"KBA question set previously answered on 2021-09-30 07:58:30.015376+00:00"
"Status":400
"Code":"kyc_error"
"Type":"KYC Error"
}

📘

Sandbox KBA

To trigger KBA in the sandbox environment, set ssn to 123-45-6789.

For a complete specification and interactive examples, see Responding to KBA in the Bond API Reference.

Retrieving the KYC status

To retrieve the KYC status for a customer, use the GET /customer/{customer_id}/verification-kyc operation and provide the following parameter.

Parameter

Description

program_id
required

Unique program identifier (credit/debit, and so on) for a brand.

An example of a succesful KYC status request is shown below.

curl --request GET \
     --url 'https://sandbox.bond.tech/api/v0/customers/931e2341-c3eb-4681-97d4-f6e09d90da14/verification-kyc?program_id=72585109-8222-4221-b15b-48e87ffed790' \
     --header 'Authorization: YOUR-AUTHORIZATION' \
     --header 'Identity: YOUR-IDENTITY'
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://sandbox.bond.tech/api/v0/customers/931e2341-c3eb-4681-97d4-f6e09d90da14/verification-kyc?program_id=72585109-8222-4221-b15b-48e87ffed790")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Identity"] = 'YOUR-IDENTITY'
request["Authorization"] = 'YOUR-AUTHORIZATION'

response = http.request(request)
puts response.read_body
const options = {
  method: 'GET',
  headers: {
    Identity: 'YOUR-IDENTITY',
    Authorization: 'YOUR-AUTHORIZATION'
  }
};

fetch('https://sandbox.bond.tech/api/v0/customers/931e2341-c3eb-4681-97d4-f6e09d90da14/verification-kyc?program_id=72585109-8222-4221-b15b-48e87ffed790', options)
  .then(response => response.json())
  .then(response => console.log(response))
  .catch(err => console.error(err));
import requests

url = "https://sandbox.bond.tech/api/v0/customers/931e2341-c3eb-4681-97d4-f6e09d90da14/verification-kyc"

querystring = {"program_id":"72585109-8222-4221-b15b-48e87ffed790"}

headers = {
    "Identity": "YOUR-IDENTITY",
    "Authorization": "YOUR-AUTHORIZATION"
}

response = requests.request("GET", url, headers=headers, params=querystring)

print(response.text)
var client = new RestClient("https://sandbox.bond.tech/api/v0/customers/931e2341-c3eb-4681-97d4-f6e09d90da14/verification-kyc?program_id=72585109-8222-4221-b15b-48e87ffed790");
var request = new RestRequest(Method.GET);
request.AddHeader("Identity", "YOUR-IDENTITY");
request.AddHeader("Authorization", "YOUR-AUTHORIZATION");
IRestResponse response = client.Execute(request);
OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
  .url("https://sandbox.bond.tech/api/v0/customers/931e2341-c3eb-4681-97d4-f6e09d90da14/verification-kyc?program_id=72585109-8222-4221-b15b-48e87ffed790")
  .get()
  .addHeader("Identity", "YOUR-IDENTITY")
  .addHeader("Authorization", "YOUR-AUTHORIZATION")
  .build();

Response response = client.newCall(request).execute();

The response includes the overall status of the KYC and failure reasons if applicable, as shown in the example below.

{
    "customer_id": "d856bf10-d4a6-4153-b8f8-2b93072dd8da",
    "brand_id": "ce027c9c-39c6-47f4-a12d-abb543f7cf63",
    "kyc_status": "passed",
    "results": {
        "ofac": "passed",
        "bureau": "passed",
        "email": "passed",
        "ip": "passed",
        "mobile": "passed"
    },
    "health": {
        "overall": "SERVICE_GOOD",
        "ofac": "SERVICE_GOOD",
        "bureau": "SERVICE_GOOD",
        "email": "SERVICE_GOOD",
        "ip": "SERVICE_GOOD",
        "mobile": "SERVICE_GOOD"
    },
    "info": {
        "ofac": {
            "name_matched": false,
            "dob_matched": false
        },
        "bureau": {
            "application_status": "Pass"
        },
        "phone": {
            "first_name_match": 100,
            "last_name_match": 100,
            "address_match": null,
            "service_status": "ACTIVE",
            "service_type": "CONTRACT",
            "service_duration": "UNKNOWN",
            "service_ported_duration": "NONE"
        }
    }
}

For a complete specification and interactive examples, see Retrieving KYC status in the Bond API Reference.


Did this page help you?