How KYC works
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 KYC in which you validate your customer's identity, 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.
Idempotency
Note
The KYC endpoint is idempotent 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"
}
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.
Starting a KYC process
To initiate a KYC request, use the POST /customers/{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 | 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 | string | Valid email address, for example [email protected] |
ip conditionally required based on program configuration | 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.1/customers/2df10ec1-130f-41bb-b0cf-f3af48350eb7/verification-kyc \
--header 'Accept: application/json' \
--header 'Authorization: YOUR-AUTHENTICATION' \
--header 'Content-Type: application/json' \
--header 'Identity: YOUR-IDENTITY' \
--data '
{
"program_id": "72585109-8222-4221-b15b-48e87ffed790",
"ip": "24.54.101.22"
}
'
require 'uri'
require 'net/http'
require 'openssl'
url = URI("https://sandbox.bond.tech/api/v0.1/customers/2df10ec1-130f-41bb-b0cf-f3af48350eb7/verification-kyc")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Accept"] = 'application/json'
request["Content-Type"] = 'application/json'
request["Identity"] = 'YOUR-IDENTITY'
request["Authorization"] = 'YOUR-AUTHENTICATION'
request.body = "{\"program_id\":\"72585109-8222-4221-b15b-48e87ffed790\",\"ip\":\"24.54.101.22\"}"
response = http.request(request)
puts response.read_body
const options = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Identity: 'YOUR-IDENTITY',
Authorization: 'YOUR-AUTHENTICATION'
},
body: JSON.stringify({program_id: '72585109-8222-4221-b15b-48e87ffed790', ip: '24.54.101.22'})
};
fetch('https://sandbox.bond.tech/api/v0.1/customers/2df10ec1-130f-41bb-b0cf-f3af48350eb7/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/customers/2df10ec1-130f-41bb-b0cf-f3af48350eb7/verification-kyc"
payload = {
"program_id": "72585109-8222-4221-b15b-48e87ffed790",
"ip": "24.54.101.22"
}
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Identity": "YOUR-IDENTITY",
"Authorization": "YOUR-AUTHENTICATION"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
var client = new RestClient("https://sandbox.bond.tech/api/v0.1/customers/2df10ec1-130f-41bb-b0cf-f3af48350eb7/verification-kyc");
var request = new RestRequest(Method.POST);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Identity", "YOUR-IDENTITY");
request.AddHeader("Authorization", "YOUR-AUTHENTICATION");
request.AddParameter("application/json", "{\"program_id\":\"72585109-8222-4221-b15b-48e87ffed790\",\"ip\":\"24.54.101.22\"}", 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\",\"ip\":\"24.54.101.22\"}");
Request request = new Request.Builder()
.url("https://sandbox.bond.tech/api/v0.1/customers/2df10ec1-130f-41bb-b0cf-f3af48350eb7/verification-kyc")
.post(body)
.addHeader("Accept", "application/json")
.addHeader("Content-Type", "application/json")
.addHeader("Identity", "YOUR-IDENTITY")
.addHeader("Authorization", "YOUR-AUTHENTICATION")
.build();
Response response = client.newCall(request).execute();
The results of the KYC will be sent via webhook events. For information on subscribing to Bond platform webhook events, see Event subscriptions and Accepting webhook requests.
A example of a KYC initialized response is shown below.
{
"customer_id": "2df10ec1-130f-41bb-b0cf-f3af48350eb7",
"kyc_status": "submitted"
}
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.document_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"
},
]
}
{
"customer_id": "ef096b64-7409-40cd-9879-badd905dc6a5",
"event": "kyc.verification.reenter_information",
"description": "Re enter the below information for this customer's KYC attempt on program a2a0f2fc-ea8f-4ada-ad22-a1fbb0ca138a",
"fields": [
"first_name",
"last_name",
"ssn"
]
}
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 to the KYC vendor (Persona). Send a POST /customer/{customer_id}/verification-kyc request to retry.If the customer does not upload documents within 28 days, the customer's KYC attempt expires and a kyc.verification.timeout webhook is sent to any subscribed listeners. A new KYC must be initialized for customers when this happens.You can differentiate between the two webhook cases by checking if the kyc.verification.document_required webhook was sent and/or by tracking completed KYC process steps using a flag or timestamp on your backend. |
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 that have been submitted are under review. |
kyc.verification.reenter_information | This is optional and will be sent at the same time as kyc.verification.document_required Customer might have entered information incorrectly, and can potentially resolve the exception by reentering their information. This includes an 'incorrect_information' field that indicates the specific piece of information that should be checked by the customer. |
kyc.verification.timeout
eventsTo distinguish between timeout events due to connection issues vs. document upload expiry timeouts, you may need to check to see if a
kyc.verification.document_required
webhook was sent for that customer and have some sort of flag or timestamp on your backend to track completion of KYC process steps. Worth noting, however, is that whether thedocument_required
webhook is sent depends on how the customer failed or abandoned their KYC process. Some customers can just abandon their KYC without reaching the document collection step, in which case their KYC will expire without thedocument_required
webhook being sent.
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 request to retrieve the KYC status 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"
}
The following table provides a list of possible KYC statuses.
Status | Description |
---|---|
passed | Successful KYC. Signaled by the kyc.verification.success webhook event. |
failed | Unsuccessful KYC. Signaled by the kyc.verification.failure event. |
document required | KYC requires additional supporting information. Signaled by the kyc.verification.document_required event. |
under review | KYC has been flagged for further review. Signaled by the kyc.verification.under_review webhook. |
error | KYC request has encountered a server error. Signaled by the kyc.verification.error webhook. Use the Start KYC endpoint to retry. |
expired | KYC request has encountered a networking error. Signaled by the kyc.verification.timeout webhook. Use the Start KYC endpoint to retry. |
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
.
Reenter Information
In some cases, you will receive a webhook to reenter KYC information at the same time as the documents required webhook. This indicates that the user may have mistakenly entered incorrect information, and the exception can be resolved if they make a correction. You can still follow the Documents required path if you would like.
We recommend showing the customer the information that might need to be changed in your UI. If they change that information, call the Update a Customer endpoint to update that in Bond, then submit the KYC again.
In the event of an SSN being corrected by the customer, you do not need to use the Update a Customer endpoint. You can just submit the KYC again using the updated SSN.
Updated about 1 year ago