Verifying Beneficial Owners

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

Overview

Before a business is fully verified, you also need to authenticate and verify all Beneficial Owners. This is done using the KYC process which validates their identity and documents, and ensures that they're compliant with federal regulations.

▶ Run in Postman

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

📘

Note

Most of a customer's details can't be updated after they've passed KYC as the new information needs to be verified before the update can occur. If you need to update information for a customer that has passed KYC, contact Bond directly.
For details, see Updating a customer.

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.

ParameterTypeDescription
program_id
required
stringProgram UUID, for example 72585109-8222-4221-b15b-48e87ffed790.
ssnstringSocial security number of the form ######### or ###-##-####, for example 123-45-6789.
phonestringNumeric or numeric/dashes, for example 555-111-2222.
phone_country_codestringNumeric, between 1 and 3 digits, for example 325.
emailstringValid email address, for example [email protected]
ip
conditionally required based on the program ID
stringValid 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.1/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.1/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.1/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.1/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.1/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.1/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.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"
      }, 
    ]
}

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.

Event enum valuesDescription
kyc.verification.errorKYC 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.failureKYC failed due to low confidence in the identity validation.
kyc.verification.successKYC passed.
kyc.verification.timeoutKYC 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_requiredKYC requires further information to continue. This includes a documents field that indicates the types of 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.

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

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.

ParameterTypeDescription
program_id
required
stringUnique 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.1/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.1/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.1/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.1/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.1/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.1/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.

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.