Verifying Beneficial Owners
Overview of the Know Your Customer verification process, webhook events, Knowledge Based Authentication, 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.
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.
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 | string | Numeric or numeric/dashes, for example 555-111-2222 . |
phone_country_code | 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.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 values | Description |
---|---|
kyc.verification.error | KYC failed to complete due to a server error. Send aPOST /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.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. |
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.
Parameter | Type | Description |
---|---|---|
program_id required | string | 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.
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
.
Updated over 2 years ago