Verifying Beneficial Owners

Overview

Before a business is fully verified, you also need to authenticate and verify all Beneficial Owners. This is done using the KYCKYC - Know Your Customer. The compliance that financial institutions put into their policies and procedures to verify a customer's identity. process which validates their identity and documents, and ensures that they're compliant with federal regulations. ▶ Run in Postman

If a Beneficial Owner fails KYC, this triggers the 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?". process. If they fail both checks, you can't issue a card. For details, see KBA.

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 beneficial owner's name does not match with the name in an SSN lookup. For details regarding how to upload documents, see KYC document upload.

If there are two beneficial owners and only one passes KYC, you would need to have a discussion with the business.

Starting a KYC process

To initiate KYC on a Beneficial Owner, use the POST /businesses/{business_id}/beneficial_owners/{beneficial_owner_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 beneficial_owner_id and program_id in the request indicates that Bond will do a verification check on a particular Beneficial Owner on behalf of a particular bank partnered with the brand.

The KYC request for the Beneficial Owner 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 request to start a KYC on a Beneficial Owner is shown below.

curl --request POST \
     --url https://sandbox.bond.tech/api/v0/businesses/business_id/beneficial_owners/beneficial_owner_id/verification-kyc \
     --header 'Authorization: YOUR-AUTHORIZATION' \
     --header 'Content-Type: application/json' \
     --header 'Identity: YOUR-IDENTITY' \
     --data '
{
     "program_id": "72585109-8222-4221-b15b-48e87ffed790",
     "ssn": "123-45-6789",
     "phone": "555-111-2222",
     "phone_country_code": "1",
     "email": "[email protected]",
     "ip": "192.168.1.139",
     "kyc_retry_request": false
}
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://sandbox.bond.tech/api/v0/businesses/business_id/beneficial_owners/beneficial_owner_id/verification-kyc")

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

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request["Identity"] = 'YOUR-IDENTITY'
request["Authorization"] = 'YOUR-AUTHORIZATION'
request.body = "{\"program_id\":\"72585109-8222-4221-b15b-48e87ffed790\",\"ssn\":\"123-45-6789\",\"phone\":\"555-111-2222\",\"phone_country_code\":\"1\",\"email\":\"[email protected]\",\"ip\":\"192.168.1.139\",\"kyc_retry_request\":false}"

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: '72585109-8222-4221-b15b-48e87ffed790',
    ssn: '123-45-6789',
    phone: '555-111-2222',
    phone_country_code: '1',
    email: '[email protected]',
    ip: '192.168.1.139',
    kyc_retry_request: false
  })
};

fetch('https://sandbox.bond.tech/api/v0/businesses/business_id/beneficial_owners/beneficial_owner_id/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/businesses/business_id/beneficial_owners/beneficial_owner_id/verification-kyc"

payload = {
    "program_id": "72585109-8222-4221-b15b-48e87ffed790",
    "ssn": "123-45-6789",
    "phone": "555-111-2222",
    "phone_country_code": "1",
    "email": "[email protected]",
    "ip": "192.168.1.139",
    "kyc_retry_request": False
}
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/businesses/business_id/beneficial_owners/beneficial_owner_id/verification-kyc");
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", "{\"program_id\":\"72585109-8222-4221-b15b-48e87ffed790\",\"ssn\":\"123-45-6789\",\"phone\":\"555-111-2222\",\"phone_country_code\":\"1\",\"email\":\"[email protected]\",\"ip\":\"192.168.1.139\",\"kyc_retry_request\":false}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"program_id\":\"72585109-8222-4221-b15b-48e87ffed790\",\"ssn\":\"123-45-6789\",\"phone\":\"555-111-2222\",\"phone_country_code\":\"1\",\"email\":\"[email protected]\",\"ip\":\"192.168.1.139\",\"kyc_retry_request\":false}");
Request request = new Request.Builder()
  .url("https://sandbox.bond.tech/api/v0/businesses/business_id/beneficial_owners/beneficial_owner_id/verification-kyc")
  .post(body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Identity", "YOUR-IDENTITY")
  .addHeader("Authorization", "YOUR-AUTHORIZATION")
  .build();

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

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.

An example of a response to a successful request to initiate a KYC process is shown below.

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

Examples of possible KYC responses are shown below.

{
    "event": "kyc.verification.success",
    "beneficial_owner_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00"
}
{
    "event": "kyc.verification.failure",
    "beneficial_owner_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00",
    "reasons": {
        "ofac": "failed",
        "bureau": "passed",
        "mobile": "passed",
        "ip": "passed",
        "email": "passed"
     }
}
{
    "event": "kyc.verification.kba",
    "beneficial_owner_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"
                }
            ]
        }
    ],
    "kba_expiration": "2021-02-27-19:20:13.155292+00:00"
}
{
    "event": "kyc.verification.error",
    "beneficial_owner_id": "a5bcf5a8-c4e0-4025-8183-5346176ee3db",
    "occurred_at": "2021-02-02-00:50:58.484840+00:00"
}
{
    "event": "kyc.verification.timeout",
    "beneficial_owner_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 KYC 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 Business Owner'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 Business Owner.

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 Business Owner to answer multiple-choice questions that are easy to answer for them, but difficult for others. If the KBA process fails, the overall Business Owner verification has failed.

Event enum values

Description

kyc.verification.error

KYC failed to complete due to a server error. Send a
POST /businesses/{business_id}/beneficial_owners/ {beneficial_owner_id}/verification-kyc to retry.

kyc.verification.failure

KYC failed due to low confidence in the identity validation.

kyc.verification.kba

KYC requires further information to continue. Includes a kba_expiration that indicates when the KBA question set expires.

kyc.verification.success

KYC passed.

kyc.verification.timeout

KYC failed to complete due to a connection timeout. Send a POST /businesses/{business_id}/beneficial_owners/ {beneficial_owner_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.

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.

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

If the response is kyc.verification.document_required, this means that the customer must upload the required identity documents for varfification.

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 Beneficial Owner answers a question incorrectly, they are posed new questions. After three failed attempts at answering questions, the overall KYC process fails. Submitting a fourth attempt results in a 400 error and the Maximum number of attempts exceeded message.

To send the Beneficial Owner'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();

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":"KBA question set previously answered on 2021-09-30 07:58:30.015376+00:00"
"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 KYC status

To retrieve the KYC status for a Beneficial Owner, use the GET /businesses/{business_id}/beneficial_owners/{beneficial_owner_id}/verification-kyc operation and 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.


Did this page help you?