Running KYC

Overview of the Know Your Customer verification process, webhook events, Knowledge Based Authentication, and retrieving the KYC status.

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.

When calling the KYC API, you can optionally include documentation for verifying the identity of the customer.

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.

Idempotency

📘

Note

The KYC endpoint is idempotentidempotent - Has the same result when called multiple times with the same idempotent key and repeated requests using the same Idempotency-Key within a 24 hour period will fail.

Once a customer has successfully passed the KYC process, no further KYC attempts are allowed. Any further call for KYC authentication responds with an error and returns the timestamp of the previously successful KYC process.

Idempotency is a Web API design principle that prevents you from running the same operation multiple times. Because a certain amount of intermittent failure is to be expected, you need a way to reconcile failed requests with a server, and idempotency provides a mechanism for that. Including an idempotency key makes POST requests idempotent, which prompts the API to do the record keeping required to prevent duplicate operations. You can safely retry requests that include an idempotency key as long as the second request occurs within 24 hours from when you first receive the key (keys expire after 24 hours).

Providing the idempotency key string in the header is optional and example is shown below.

{
   "Authorization": "YOUR-AUTHORIZATION",
   "Identity": "YOUR-IDENTITY",
   "Idempotency-Key": "dd6dcedd-2a11-4098-1223-876902123abc"
}

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

docs
optional

array

Array of one or more physical or mailing addresses, (see Object tables below).

The government_id array within the Government ID object has the following structure.

Government ID object

Type

Description

type
required

string

One of drivers_license, passport, state_id, passport_card, permanent_resident_card, work_permit, visa.

country
required

string

ISO 3166-1 alpha-2 country code, maximum two characters.

front_image

array

Array of file_name (required) and image (required) objects.

back_image

array

Array of file_name (required) and image (required) objects.

proof_of_address

array

Array of file_name (required) and image (required) objects.

social_security_card

array

Array of file_name (required) and image (required) objects.

The proof_of_address array within the Docs object has the following structure.

Proof of Address object

Type

Description

type
required

string

utility_bill or property_tax_bill.

file_name
required

string

File name of the image to upload.

image
required

string

Image file name to associate with the uploaded image, for example GVyDQobyAxMCAwIFINCj4+DQoNCnN0YXJ0eHJlZg0KMjcxNA0KJSVFT0YNCg==.

The social_security_card array within the Docs object has the following structure.

Social Security Card object

Type

Description

file_name
required

string

File name of the image to upload.

image
required

string

Image file name to associate with the uploaded image, for example GVyDQobyAxMCAwIFINCj4+DQoNCnN0YXJ0eHJlZg0KMjcxNA0KJSVFT0YNCg==.

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 initialized 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.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"
      }, 
    ]
}

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.

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.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.

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.

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"
}

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

KYC exceptions

Documents required

A KYC process may require one or more documents to be submitted. When this happens, a webhook is sent with the type of document required and the KYC status is set to kyc.verification.document_required. For details on uploading documents, see KYC document upload.

Depending on whether the documents are approved or they need to be reviewed, the status changes to either kyc.verification.success or kyc.verification.under_review respectively, and a webhook is sent.

Documents under review

After you upload the required documents and they need to be reviewed, a webhook is sent and the KYC status is set to kyc.verification.under_review. You don’t need to take any further action. Once the review is complete, a webhook is sent and the status is updated to either kyc.verification.success or kyc.verification.failure.


Did this page help you?