Onboarding for secured charge cards

Seamlessly take your customers through KYC and credit application approval

Overview

Onboarding for secured charge cards involves using Bond APIs and webhooks to request credit on behalf of your customers and manage state throughout the credit application process.

Once the credit application is approved, the onboarded customer gets a security deposit account (SDA) and can be issued a secured charge card.

In this guide we will highlight the Bond API calls, webhook events, and architectural approaches to use when building out the front-end onboarding flow for secured charge cards.

Prerequisites

Before you can create a secured charge card, you need to successfully complete the following steps:

  1. Get your API keys
  2. Get your Program ID
  3. Register webhooks

1. Getting your API keys

To interact with the API endpoints on Bond's platform, a pair of identity and authorization API keys must be supplied in the request headers. For more information, see Getting your API key.

2. Finding your Program ID

A Program ID is a UUID value provided by Bond that represents a relationship between you and a bank and is often associated one-to-one with a financial product. For the puposes of this guide, the program ID is associated with consumer secured credit accounts. You can find your program_id in the Bond Portal under the Developers tab. For more information, see Getting your Program ID.

3. Registering webhooks

We use webhooks to signal asynchronously that work has been done in response to certain API requests. These webhooks are for convenience and any information provided asynchronously by the webhooks can also be queried using synchronous API requests. For this guide we are interested in webhooks specifically in the following categories:

  • credit
  • kyc

Webhook registration can be done through Bond Portal or via API. For more information, see Webhook events and subscriptions.

📘

Note

When creating the webhook subscription, you can subscribe exclusively to kyc.* and credit.* webhook events to limit the webhooks posted to your application endpoint. Read this for more on webhook configuration with wildcards.

Then you will make three API calls:

  1. Create a customer
  2. Create a credit application
  3. Submit a credit application

After submitting a credit application for a new customer, your application can use the kyc and credit webhooks to monitor KYC and credit application statuses, respectively.

The webhook credit.application.approved payload will indicate credit application approval and contain the account ID for the customer's SDA.

Once the SDA has been created, you can issue a secured charge card and transfer funds:

Building your onboarding flow

Because a secured charge card customer must complete the KYC and credit application processes to receive a security deposit account, it is important to manage state for these processes and keep the customer informed of any outstanding action items.

Step 1. Create a Customer

On Bond's platform, every one of your customers that is issued a financial product must have an associated Customer object. This customer object stores important information on the customer, including name, address, and other sensitive information that is required for regulatory purposes.

We'll onboard an imaginary customer, Angie Johnson, for a credit builder card product. An example of the customer creation request for Angie is provided below. For more information on the request and response schemas of the customer creation API, please refer to the Create a customer API reference.

curl --request POST \
  --url https://sandbox.bond.tech/api/v0.1/customers/ \
  --header 'Identity: YOUR-IDENTITY' \
  --header 'Authorization: YOUR-AUTHORIZATION' \
  --header 'Content-Type: application/json' \
  --data '{
    "dob":"1998-06-07",
    "last_name":"Johnson",
    "first_name":"Angie",
    "addresses":[
      {
        "address_type":"PHYSICAL",
        "street":"345 California Ave.",
        "street2": "Suite 600",
        "city": "San Francisco",
        "state": "CA",
        "zip_code": "12345-1234",
        "country": "US",
        "is_primary": true
      }
    ]
  }'

After sending the customer creation request, we receive a successful 201 response:

{
    "customer_id": "ae268265-1f29-4603-a958-a9b0ab7ce573",
    "bond_brand_id": "dbdac53b-0336-4edc-aa68-911db00f04e1",
    "brand_person_id": null,
    "date_created": "2022-09-09T16:55:44.351177+00:00",
    "dob": "1998-06-10",
    "first_name": "Angie",
    "middle_name": "",
    "last_name": "Johnson",
    "email": "[email protected]",
    "phone": "5551112222",
    "phone_country_code": "1",
    "kyc_requests_available": 3,
    "addresses": [
        {
            "address_id": "dcb894f6-44ff-4e80-9e10-6b2976ff3257",
            "address_type": "PHYSICAL",
            "street": "345 California Ave.",
            "street2": "Suite 600",
            "city": "San Francisco",
            "state": "CA",
            "zip_code": "12345-1234",
            "country": "US",
            "is_primary": true,
            "date_created": "2022-09-09T16:55:44.354325+00:00"
        }
    ],
    "business_id": null,
    "ssn": "XXX-XX-4451"
}

In the response, we want to pay particular attention to the customer_id UUID value ae268265-1f29-4603-a958-a9b0ab7ce573, which uniquely identifies the newly created customer object associated with Angie.

Step 2: Create a Credit Application

After supplying Bond with initial information on the customer via the customers API, the next step is to create a credit application to request credit for that customer. As part of the credit application process, KYC will automatically be performed to ensure that the consumer is not flagged by any OFAC checks or other fraud prevention measures.

During onboarding, you will collect some basic financial information from Angie in order to request credit on her behalf. In this case, Angie earns $70,000 annually and pays $2,000 monthly for housing.

For more information on the request and response schemas for the Credit API, please refer to the Create credit application API reference. An example of a credit application creation request for Angie is shown below.

curl --request POST \
     --url https://sandbox.bond.tech/api/v0.1/credit/applications \
     --header 'Accept: application/json' \
     --header 'Authorization: null' \
     --header 'Content-Type: application/json' \
     --header 'Identity: null' \
     --data '
{
     "program_id": "YOUR-PROGRAM-ID",
     "applicant": {
          "customer_id": "ae268265-1f29-4603-a958-a9b0ab7ce573",
          "total_annual_income": 7000000,
          "monthly_housing_payment": 200000
     }
}
'

Note that we are using the customer_id value 3a43b612-825c-48d4-b025-47083ab10755 that we received in the customer creation response.

For more information, see Creating a credit application in the API Guide.

The result should be a 201 response with the following payload:

{
    "application_id": "b629182a-3aeb-4834-846a-1688d4649bb9",
    "applicant": {
        "monthly_housing_payment": 200000,
        "total_annual_income": 7000000,
        "customer_id": "ae268265-1f29-4603-a958-a9b0ab7ce573",
        "first_name": "Angie",
        "middle_name": "",
        "last_name": "Johnson",
        "date_of_birth": "1998-06-10",
        "address_id": "dcb894f6-44ff-4e80-9e10-6b2976ff3257",
        "street1": "345 California Ave.",
        "street2": "Suite 600",
        "city": "San Francisco",
        "state": "CA",
        "zip_code": "12345-1234",
        "country": "US",
        "ssn": "XXX-XX-4451",
        "email": "[email protected]",
        "phone": "5551112222",
        "phone_country_code": "1"
    },
    "date_created": "2022-09-09T16:57:21.966498Z",
    "date_updated": "2022-09-09T16:57:21.966792Z",
    "program_id": "06a95847-6705-40dd-bbda-835348651ddf",
    "application_status": "created",
    "accounts": {
        "security_deposit_account_id": null
    }
}

Take note of the application_id, which is the UUID for Angie's credit application.

If Angie's credit application information needs to be updated at any point prior to submission, the PATCH /credit/applications/{application_id} operation can be used to update the values in the applicant object.

Step 3: Submit a Credit Application

Now that Angie's credit application has been created, it is time to submit it. For this we'll use the Submit credit application API.

Bond's v0.1 credit APIs have integrated KYC into the credit application process to ensure that the customer has been validated before receiving credit. Ultimately, Angie's credit applicant information and KYC result will determine whether her credit application is approved.

In order to make a request to submit the application, we will use the POST /credit/applications/{application_id}/submit operation. Here the application_id is used as a path variable. A credit application submission request for Angie's application ID b629182a-3aeb-4834-846a-1688d4649bb9 is shown below:

curl --request POST \
     --url https://sandbox.bond.tech/api/v0.1/credit/applications/b629182a-3aeb-4834-846a-1688d4649bb9/submit \
     --header 'Accept: application/json' \
     --header 'Authorization: null' \
     --header 'Identity: null'

A successful 200 response is shown below:

{
    "application_id": "b629182a-3aeb-4834-846a-1688d4649bb9",
    "applicant": {
        "monthly_housing_payment": 200000,
        "total_annual_income": 7000000,
        "customer_id": "bcc4279f-3acd-4582-b601-1c9cf21e3727",
        "first_name": "Angie",
        "middle_name": "",
        "last_name": "Johnson",
        "date_of_birth": "1998-06-10",
        "address_id": "dcb894f6-44ff-4e80-9e10-6b2976ff3257",
        "street1": "345 California Ave.",
        "street2": "Suite 600",
        "city": "San Francisco",
        "state": "CA",
        "zip_code": "12345-1234",
        "country": "US",
        "ssn": "XXX-XX-4451",
        "email": "[email protected]",
        "phone": "5551112222",
        "phone_country_code": "1"
    },
    "date_created": "2022-09-09T16:57:21.966498Z",
    "date_updated": "2022-09-09T17:02:52.098491Z",
    "program_id": "06a95847-6705-40dd-bbda-835348651ddf",
    "application_status": "submitted",
    "accounts": {
        "security_deposit_account_id": null
    }
}

Notice that the application_status has changed from created to submitted, indicating that Angie's credit application has been submitted and that KYC is underway.

KYC process

As shown in the previous response, the application_status value of kyc_started indicates that the Bond platform has automatically initiated regulatory KYC checks on Angie. These KYC checks are equivalent to the KYC checks initiated by the Start KYC (Know-Your-Customer) endpoint and should be handled in the same way. In the majority of cases, these checks run automatically and are completed in seconds. However, in certain scenarios, additional information may be required to complete identity verification. For more information, see Running KYC.

There are a few cases that arise during KYC that may require additional intervention. These cases are described in the table below.

KYC WebhookNotes
kyc.verification.document_required
response required
The documents required for the document required state are accessible via webhook or via the Retrieve KYC Status endpoint using the relevant customer_id and program_id.
kyc.verification.reenter_information
response optional
To respond to the reenter_information webhook, information provided via the customer object can be patched directly via the Update a customer endpoint, while information provided via the credit application can be changed directly via the Patch credit application endpoint.

After changing the relevant fields described by the kyc.verification.reenter_information webhook, the credit application can be resubmitted again using the Submit credit application endpoint.
kyc.verification.failureWhen the background KYC checks result in a failure, the application_status value is kyc_warning. However, an application can be resubmitted after a failed KYC up to a limit of three times using the Submit credit application endpoint.

Credit application results

Approval

Pending a successful KYC, the credit application moves into the approved state. This is accompanied by a credit.application.approved webhook event. The approved state can also be viewed using the Get credit application endpoint. An example of a GET response for an approved application is shown below.

{
    "application_id": "b629182a-3aeb-4834-846a-1688d4649bb9",
    "applicant": {
        "monthly_housing_payment": 200000,
        "total_annual_income": 6000000,
        "customer_id": "bcc4279f-3acd-4582-b601-1c9cf21e3727",
        "first_name": "Angie",
        "middle_name": "",
        "last_name": "Johnson",
        "date_of_birth": "1998-06-10",
        "address_id": "dcb894f6-44ff-4e80-9e10-6b2976ff3257",
        "street1": "345 California Ave.",
        "street2": "Suite 600",
        "city": "San Francisco",
        "state": "CA",
        "zip_code": "12345-1234",
        "country": "US",
        "ssn": "XXX-XX-4451",
        "email": "[email protected]",
        "phone": "5551112222",
        "phone_country_code": "1"
    },
    "date_created": "2022-09-09T16:57:21.966498Z",
    "date_updated": "2022-09-09T17:02:52.098491Z",
    "program_id": "06a95847-6705-40dd-bbda-835348651ddf",
    "application_status": "approved",
    "accounts": {
        "security_deposit_account_id": "173121d8-f05e-4bb6-b280-b5b761d2214f"
    }
}

We take special note of the security_deposit_account_id field 173121d8-f05e-4bb6-b280-b5b761d2214f under the accounts key. This value uniquely identifies the security deposit account that has been created for Angie.

Adverse action notices

Not all credit applications are approved. When a credit application is rejected, an adverse action notice is generated. These notices need to be supplied to the end consumer (in this case, Angie), in an approved template via email.

The Get adverse actions API can be used to get additional information on the reason for the rejection.

An example of a request to get the reason for rejection is shown below.

curl --request GET \
     --url https://api.bond.tech/api/v0.1/credit/applications/b629182a-3aeb-4834-846a-1688d4649bb9/adverse-actions \
     --header 'Accept: application/json' \
     --header 'Authorization: YOUR-AUTHORIZATION' \
     --header 'Identity: YOUR-IDENTITY'

For the majority of credit application we expect that the application will encounter no difficulties, so we receive the 404 response shown below.

{
    "status": "ERROR_CODE_CREDIT_APPLICATION_GENERIC",
    "code": 3001,
    "message": "Not found.",
    "details": {}
}

An example of a response for a different application_id that contains an adverse action is shown below.

{
    "id": "585ffb6a-98f9-40f6-a2b9-81ab22fb169a",
    "application_id": "8d3dbb5c-97c1-4c18-97bc-e376e62cc2c9",
    "date_created": "2022-05-15T21:39:10.130756Z",
    "date_updated": "2022-05-15T21:39:10.192226Z",
    "service": "kyc",
    "reasons": [
        "Unable to verify identity"
    ]
}

The most common reason for adverse action notices is when the customer and the credit application fail three submission attempts at KYC. After three attempts an error is generated. An example of this error is shown below.

{
    "status": "ERROR_CODE_CREDIT_APPLICATION_MAX_KYC_ATTEMPTS",
    "code": 3010,
    "message": "Customer has exceeded maximum number of KYC attempts.",
    "details": {
        "@type": "type.googleapis.com/bond.credit.v1.CreditCustomerErrorDetail",
        "application_id": "9b3dbb5c-97c1-4c18-97bc-e376e62cc2c9",
        "customer_id": "3a43b612-825c-48d4-b025-47083ab10755"
    }
}

Architectural approaches

Polling

You may choose to process the Bond webhooks server-side and update customer records in your own database instance to track the status of the customer throughout onboarding. By doing this, the client can then poll via REST API calls to check the status.

You may check KYC status using your own API, or alternatively with the Bond API /verification-kyc endpoint.

An example of a KYC status request using Angie's customer_id is shown below:

curl --location --request GET 'https://api.bond.tech/api/v0.1/customers/ae268265-1f29-4603-a958-a9b0ab7ce573/verification-kyc?program_id=YOUR-PROGRAM-ID' \
--header 'Content-Type: application/json' \
--header 'Authorization: YOUR-AUTHORIZATION' \
--header 'Identity: YOUR-IDENTITY'

An example of a successful 200 response looks like:

{
    "customer_id": "ae268265-1f29-4603-a958-a9b0ab7ce573",
    "brand_id": "dbdac53b-0336-4edc-aa68-911db00f04e1",
    "kyc_status": "document required",
    "documents": [
        {
            "document_type": "social_security_card",
            "status": "required",
            "upload_link": "https://withpersona.com/verify?inquiry-id=inq_sqGWyUWW3DmxJPKhWrfoMbiE&template-id=vtmpl_X9v7uAX8PdK8ptdtySR3oGSK"
        }
    ]
}

This would indicate that the customer needs to submit an identification document to verify themselves in order to pass KYC. Typically, Bond KYC and Credit Application webhooks will arrive within seconds, so polling will not occur for too long once the customer completes an onboarding action item.

WebSockets

Alternatively, you can use WebSockets. WebSockets makes it possible to open a two-way interactive communication session between the user's browser and a server. With the WebSockets API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.

From a technical perspective, secured charge card onboarding involves making a few Bond API calls (synchronous) on the client, followed by processing Bond webhooks (asynchronous) on the server.

When the relevant webhooks for the credit application process are received and processed, the server needs to notify the client of these updates.

At a high level, using WebSockets entails the following steps:

  1. Open a WebSocket connection between your client and server

  2. Client-side, make three Bond API calls:

    • POST /customer
    • POST /credit/applications
    • POST credit/applications/:application_id/submit
  3. Server-side, use a route handler for your configured webhooks endpoint:

    • If kyc.verification.document_required webhook received, then send message to client with the document upload_link so that user can upload documents in a client-side iframe.
    • If kyc.verification.under_review webhook received, then send message to client indicating that the user will need to wait for manual review to be completed.
    • If credit.application.resubmit_required, send message to client indicating that the user will need to restart the KYC process.
    • If credit.application.approved webhook received, persist the accounts.security_deposit_account_id value and then send a message to client indicating credit application approval.
  4. Client-side, in your UI:

    • Show a loading spinner or message while client waits for messages from server via the WebSocket connection. When a message is received, you can display relevant status information or move the user to the next onboarding step.

The following chart shows how to handle different webhook events and send updates to the client:

Webhook EventAction
kyc.verification.document_requiredSend message to client with the document upload_link(s) so that user can upload documents in a client-side iframe.
kyc.verification.under_reviewSend message to client indicating that the user will need to wait for manual review to be completed. This may take 1-2 business days.
kyc.verification.resubmit_requiredThis is optional and can be sent at the same time as kyc.verification.document_required.

The customer may have entered incorrect information that can cause KYC failure. Reentering this information may resolve the issue. This includes an incorrect_information field that identifies exactly what should be checked by the customer.

Send a message to client letting customer know to resubmit certain information earlier provided in an earlier onboarding step.
kyc.verification.timeoutSend message to client indicating that KYC has timed out.
kyc.verification.failureSend message to client indicating that KYC failed due to low confidence in identity validation.
credit.application.adverse_actionSend message to client indicating that an adverse action has been generated and that the applicant could not be approved for credit.
credit.application.resubmit_requiredSend message to client indicating that the user will need to restart the KYC process. The application needs to be resubmitted with the same application_id.
credit.application.approvedPersist the accounts.security_deposit_account_id value and then send a message to client indicating credit application approval.

A sample code snippet for configuring a WebSocket client in JavaScript is shown below:

const url = 'wss://myserver.com/something'
const connection = new WebSocket(url)

connection.onopen = function () {
  
};

// Log errors
connection.onerror = function (error) {
  console.error('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {

};

On the server, you may update the client by sending messages using code that looks like this:

const Server = require('ws').Server;
const port = process.env.PORT || 80;
const ws = new Server({port: port});

// connect to the Websocket client
ws.on('connection', function(w){
  
  // process Bond kyc.* and credit.* webhooks and send message to client
  ...
  	    w.send('message to client');
  ...
  
  w.on('close', function() {
    console.log('closing connection');
  });

});

Handling user cases

Below is a summary of what cases to expect when taking users through the credit application process:

  • 80-90% of users will typically get passed/approved in ~3-5 seconds, depending on the applicant audience.

  • Otherwise, the user will get prompted for documentary evidence; this prompting for documentary evidence will also typically happen in just a few seconds.

  • Once the document gets submitted,

    • The document can get verified automatically (in which case you'll get another webhook for application.approved very quickly
    • OR the document cannot be verified automatically, and it will take 1-2 business days to get processed.

As soon as KYC is passed, the customer's credit application will be approved and you can proceed with setting them up with a secured charge card.

Next, it's time to issue the secured charge card. You can find more detailed information on this in the Creating a secured charge card guide.

Funding the security deposit account

To be able to make purchases on a secured charge card you need to fund the security deposit account. This can also be done after creating the secured charge card, however, until there are funds in the secured deposit account, the credit limit on the card is zero.

Executing an ACH transfer

📘

Note

To link external accounts, you need to use the v0 APIs.

When funding a security deposit account through ACH, the first step is to link an external bank account. Linking an external account can be done either via Bond's SDK or via Bond's APIs directly.

📘

Underwriting via external bank accounts

After linking the account, use the Get external account history endpoint to view recent transactions on the external account. This information can also be used to make an underwriting assessment as to whether you would like to offer your user a card.

Funding a security deposit account through ACH can be done through the Create transfer endpoint. Note that to initiate an ACH transfer, an external account must be linked to the customer. After an external account has been linked, there is a linked_account_id that identifies the linked account. For our purposes, we'll assume that the linked_account_id is f64eadeb-5398-46f7-b1a1-9a0a709039a9.

curl --request POST \
     --url https://api.bond.tech/api/v0.1/transfers \
     --header 'Accept: application/json' \
     --header 'Authorization: YOUR-AUTHORIZATION' \
     --header 'Content-Type: application/json' \
     --header 'Identity: YOUR-IDENTITY' \
     --data '
{
     "ach": {
          "class_code": "WEB",
          "same_day": false
     },
     "origination_account_id": "f64eadeb-5398-46f7-b1a1-9a0a709039a9",
     "destination_account_id": "173121d8-f05e-4bb6-b280-b5b761d2214f",
     "amount": 25000,
     "description": "Angie funding"
}
'

Note that we supplied the linked_account_id as the origination_account_id and the security_deposit_account_id as the destination_account_id because we want the funds to flow from the linked account to the security deposit account.

An example of a successful response is shown below.

{
  "date_created": "2022-05-16T17:14:09.686688",
  "date_updated": "2022-05-16T17:14:09.686688",
  "date_settled": "2022-05-16T17:14:09.686688",
  "transfer_id": "4ead6cdc-77eb-45fa-9959-3f166385a60a",
  "transaction_id": "0fec1e58-b197-4052-99cf-2218496c5482",
  "origination_account_id": "f64eadeb-5398-46f7-b1a1-9a0a709039a9",
  "destination_account_id": "173121d8-f05e-4bb6-b280-b5b761d2214f",
  "description": "Angie funding",
  "amount": 25000,
  "ach": {
    "ach_class_code": "WEB",
    "same_day": false,
    "ach_return_code": "R73",
    "failure_reason": null
  }
}

Note that ACH timelines typically include time both for transactions to be processed and to be considered settled before the funds are made available.

For more information, see ACH transfers.

Making a direct deposit

Information on the security deposit account can be retrieved using the Get account endpoint.

An example of such a request for Angie is shown below.

curl --request GET \
     --url https://api.bond.tech/api/v0.1/accounts/173121d8-f05e-4bb6-b280-b5b761d2214f \
     --header 'Accept: application/json' \
     --header 'Authorization: YOUR-AUTHORIZATION' \
     --header 'Identity: YOUR-IDENTITY'

An example of a successful response to a security deposit account information request is shown below.

{
  "account_id": "173121d8-f05e-4bb6-b280-b5b761d2214f",
  "date_updated": "2022-05-16T19:39:34Z",
  "date_created": "2022-05-16T19:39:34Z",
  "program_id": "YOUR-PROGRAM-ID",
  "customer_id": "3a43b612-825c-48d4-b025-47083ab10755",
  "type": "security_deposit",
  "status": "active",
  "description": "string",
  "routing_number": 547897762,
  "account_number": 574771265,
  "balance": {
    "current_balance": 25600,
    "ledger_balance": 21300,
    "currency": "USD"
  },
  "security_deposit": {
    "credit_account": "e913d434-bda9-494e-b6df-adf46a52c1cc"
  }
}

We use the routing_number value 547897762 and account_number value 574771265 to receive ACH transfers originated from other financial institutions, such as from payroll via direct deposit.

🚧

Sandbox limitations

Note that in the sandbox, the routing_number and account_number values are not real and will not be accessible via direct deposit.

Note again that ACH timelines typically include time both for transactions to be processed and to be considered settled before the funds are made available.

For more information, see ACH transfers.

Next, we'll look at creating and managing secured charge cards for your customers.