Skip to main content

Cards

The Card resource represents a debit card under an account.

Card creation request supports Idempotency, ensuring that performing multiple identical requests will have the same effect as performing a single request.

note

Unit enables you to programmatically review purchase Authorization Requests when they are made, and decide whether to approve the purchase. This capability gives complete control and visibility into card purchases.

Card Resource Types#

There are multiple types of card resources on Unit, each solves for specific use cases. The table below outlines the different types and use cases.

TypeDetails
IndividualVirtualDebitCardA virtual debit card belonging to an Individual customer, including sole proprietors. The details of the card holder (name, email, phone number etc.) will be the individual customer's details.
IndividualDebitCardA physical debit card belonging to an Individual customer, including sole proprietors. The details of the card holder (name, email, phone number etc.) will be the individual customer's details and by default the card will be shipped to the customer's address.
BusinessVirtualDebitCardA virtual debit card belonging to a Business customer. The card holder may be any person associated with the business. The details of the card holder (name, email, phone number etc.) will be provided as part of the card creation request. In order to create the customer token that is needed to reveal the card information, the card holder must be the business contact or an (authorized user)[https://docs.unit.co/customers/#authorized-users].
BusinessDebitCardA physical debit card belonging to a Business customer. The card holder may be any person associated with the business. The details of the card holder (name, email, phone number etc.) and shipping address will be provided as part of the card creation request.
info

Cards issued to sole proprietors are business debit cards for all intents and purposes (BIN, interchange etc.)

Card Statuses#

Cards have a Status Property, which represent their current status and determines what actions can or can't be completed using the card.

StatusDescriptionComments
InactiveThe Card has not been activated by the CustomerCards can be activated using the Activate card or by calling a the toll-free number printed on the card. Virtual cards do not require activation.
ActiveThe Card has been activated and can be used regularly
StolenThe Card has been reported stolen by the CustomerA stolen card cannot be reopened.
LostThe Card has been reported lost by the customerA lost card cannot be reopened.
FrozenThe Card has been frozen
ClosedByCustomerThe Card has been closed by the CustomerA closed card cannot be reopened.
SuspectedFraudThe Card has been flagged due to fraud suspicionCards are defined as "SuspectedFraud" by the card network (e.g. Visa). The network will reach out to the client to confirm recent transactions that were marked as potentially fraudulent and reactivate the card if the transactions are confirmed as not fraudulent. If the transactions are marked as fraudulent or if the client does not respond to the card network, the card will remain in SuspectedFraud status and cannot be used.
info

When a card (physical or virtual) is due to expire, The network will automatically re-issue a new card, with the same card number, different expiry and a new CVV. New physical cards will need to be activated by the customer.

Create Individual Debit Card#

Creates and ships a physical debit card to an individual. You must link the card to an account using the account field under relationships object. The linked account must belong to an individual customer (and not a business).

note

For security reasons, an individual customer's account may have up to one physical card in Active or newly created (Inactive) status. To change this policy in your org, please contact Unit. Cards that were reported lost, reported stolen or closed do not count as active.

VerbPOST
Urlhttps://api.s.unit.sh/cards
Required Scopecards-write
Data TypeindividualDebitCard
Timeout (Seconds)5

Attributes#

NameTypeDescription
shippingAddressAddressOptional. Address to ship the card to. If not specified, the individual address is used.
designstringOptional. You may omit if you only have one card design. Please contact Unit if you need multiple card designs.
additionalEmbossedTextstringOptional, up to 21 characters. Use for a second cardholder name, company name, or other data to be embossed on a card.
idempotencyKeystringOptional. See Idempotency.
tagsobjectOptional. See Tags.
limitsobjectOptional. See Limits (cents).
printOnlyBusinessNamebooleanOptional, default is false. Print the business name on the card instead of the card holder's name. Available only for Sole Proprietorship typed customers that have a DBA property.

Relationships#

NameTypeDescription
accountJSON:API RelationshipLink to the account the card belongs to. Holder of the account must be an individual.
customerJSON:API RelationshipOptional, Link to the customer the card belongs to. Mandatory if the account has more than one customer. Holder of the account must be an individual.
Example Request:
curl -X POST 'https://api.s.unit.sh/cards'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "individualDebitCard",    "attributes": {      "shippingAddress": {        "street": "5230 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      },      "idempotencyKey": "3a1a33be-4e12-4603-9ed0-820922389fb8",      "limits": {        "dailyWithdrawal": 50000,        "dailyPurchase": 50000,        "monthlyWithdrawal": 500000,        "monthlyPurchase": 700000      }    },    "relationships": {      "account": {        "data": {          "type": "depositAccount",          "id": "2"        }      }    }  }}'

Response#

Response is a JSON:API document.

201 Created#

FieldTypeDescription
dataIndividualDebitCardThe target resource after the operation was completed.
Example Response:
{  "data": {    "type": "individualDebitCard",    "id": "8",    "attributes": {      "createdAt": "2020-05-13T09:07:47.645Z",      "last4Digits": "1234",      "expirationDate": "2022-05",      "shippingAddress": {        "street": "5230 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      },      "status": "Inactive"    },    "relationships": {      "account": {        "data": {          "type": "depositAccount",          "id": "2"        }      },      "customer": {        "data": {          "type": "individualCustomer",          "id": "2"        }      }    }  }}

Create Business Debit Card#

Creates and ships a physical debit card to a business card holder. You must link the card to an account using the account field under relationships object. The linked account must belong to a business customer.

note

When creating a business debit card, the card holder's personal information must be provided as part of the request.

note

For security reasons, a card holder of a business debit card may have up to one physical card in Active or newly created (Inactive) status. To change this policy in your org, please contact Unit. Cards that were reported lost, reported stolen or closed do not count as active.

VerbPOST
Urlhttps://api.s.unit.sh/cards
Required Scopecards-write
Data TypebusinessDebitCard
Timeout (Seconds)5

Attributes#

NameTypeDescription
fullNameFullNameFull name of the card holder.
dateOfBirthRFC3339 Date stringDate of birth of the card holder (e.g. "2001-08-15").
addressAddressAddress of the card holder.
shippingAddressAddressOptional. Address to ship the card to. If not specified, card holder address is used.
phonePhonePhone of the card holder.
emailstringEmail address of the card holder.
designstringOptional. You may omit if you only have one card design. Please contact Unit if you need multiple card designs.
additionalEmbossedTextstringOptional, up to 21 characters. Use for a second cardholder name, company name, or other data to be embossed on a card.
idempotencyKeystringOptional. See Idempotency.
tagsobjectOptional. See Tags.
limitsobjectOptional. See Limits (cents).
printOnlyBusinessNamebooleanOptional, default is false. Print the business name on the card instead of the card holder's name.

Relationships#

NameTypeDescription
accountJSON:API RelationshipThe account the card belongs to. Holder of the account must be a business.
Example Request:
curl -X POST 'https://api.s.unit.sh/cards'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "businessDebitCard",    "attributes": {      "shippingAddress": {        "street": "5230 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      },      "fullName": {        "first": "Richard",        "last": "Hendricks"      },      "address": {        "street": "5230 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      },      "dateOfBirth": "2001-08-10",      "email": "richard@piedpiper.com",      "phone": {        "countryCode": "1",        "number": "5555555555"      },      "idempotencyKey": "3a1a33be-4e12-4603-9ed0-820922389fb8",      "limits": {        "dailyWithdrawal": 50000,        "dailyPurchase": 50000,        "monthlyWithdrawal": 500000,        "monthlyPurchase": 700000      }    },    "relationships": {      "account": {        "data": {          "type": "depositAccount",          "id": "1"        }      }    }  }}'

Response#

Response is a JSON:API document.

201 Created#

FieldTypeDescription
dataBusinessDebitCardThe target resource after the operation was completed.
Example Response:
{  "data": {    "type": "businessDebitCard",    "id": "9",    "attributes": {      "createdAt": "2020-05-13T09:42:21.857Z",      "last4Digits": "2074",      "expirationDate": "2022-05",      "shippingAddress": {        "street": "5230 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      },      "address": {        "street": "5230 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      },      "fullName": {        "first": "Richard",        "last": "Hendricks"      },      "phone": {        "countryCode": "1",        "number": "5555555666"      },      "email": "richard@piedpiper.com",      "dateOfBirth": "2001-08-10",      "status": "Inactive"    },    "relationships": {      "account": {        "data": {          "type": "depositAccount",          "id": "1"        }      },      "customer": {        "data": {          "type": "businessCustomer",          "id": "1"        }      }    }  }}

Create Individual Virtual Debit Card#

Creates a debit card for an individual. You must link the card to an account using the account field under relationships object. The linked account must belong to an individual customer (and not a business).

note

For security reasons, an individual customer's account may have up to one virtual card in Active status. To change this policy in your org, please contact Unit. Cards that were reported lost, reported stolen or closed do not count as active.

note

Unlike physical cards, virtual cards are created in Active status and do not require activation.

VerbPOST
Urlhttps://api.s.unit.sh/cards
Required Scopecards-write
Data TypeindividualVirtualDebitCard
Timeout (Seconds)5

Attributes#

NameTypeDescription
idempotencyKeystringOptional. See Idempotency.
tagsobjectOptional. See Tags.
LimitsobjectOptional. See Limits (cents).

Relationships#

NameTypeDescription
accountJSON:API RelationshipLink to the account the card belongs to. Holder of the account must be an individual.
customerJSON:API RelationshipOptional, Link to the customer the card belongs to. Mandatory if the account has more than one customer. Holder of the account must be an individual.
Example Request:
curl -X POST 'https://api.s.unit.sh/cards'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "individualVirtualDebitCard",    "attributes": {      "idempotencyKey": "3a1a33be-4e12-4603-9ed0-820922389fb8",      "limits": {        "dailyWithdrawal": 50000,        "dailyPurchase": 50000,        "monthlyWithdrawal": 500000,        "monthlyPurchase": 700000      }    },    "relationships": {      "account": {        "data": {          "type": "depositAccount",          "id": "2"        }      }    }  }}'

Response#

Response is a JSON:API document.

201 Created#

FieldTypeDescription
dataIndividualVirtualDebitCardThe target resource after the operation was completed.
Example Response:
{  "data": {    "type": "individualVirtualDebitCard",    "id": "8",    "attributes": {      "createdAt": "2020-05-13T09:07:47.645Z",      "last4Digits": "1234",      "expirationDate": "2022-05",      "status": "Active"    },    "relationships": {      "account": {        "data": {          "type": "depositAccount",          "id": "2"        }      },      "customer": {        "data": {          "type": "individualCustomer",          "id": "2"        }      }    }  }}

Create Business Virtual Debit Card#

Creates a debit card for a business. You must link the card to an account using the account field under relationships object. The linked account must belong to a business customer.

note

When creating a business debit card, the card holder's personal information must be provided as part of the request.

note

For security reasons, a card holder of a business debit card may have up to one virtual card in Active or newly created (Inactive) status. To change this policy in your org, please contact Unit. Cards that were reported lost, reported stolen or closed do not count as active.

note

Unlike physical cards, virtual cards are created in Active status and do not require activation.

VerbPOST
Urlhttps://api.s.unit.sh/cards
Required Scopecards-write
Data TypebusinessVirtualDebitCard
Timeout (Seconds)5

Attributes#

NameTypeDescription
fullNameFullNameFull name of the card holder.
dateOfBirthRFC3339 Date stringDate of birth of the card holder (e.g. "2001-08-15").
addressAddressAddress of the card holder.
phonePhonePhone of the card holder.
emailstringEmail address of the card holder.
idempotencyKeystringOptional. See Idempotency.
tagsobjectOptional. See Tags.
limitsobjectOptional. See Limits (cents).

Relationships#

NameTypeDescription
accountJSON:API RelationshipThe account the card belongs to. Holder of the account must be a business.
Example Request:
curl -X POST 'https://api.s.unit.sh/cards'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "businessVirtualDebitCard",    "attributes": {      "fullName": {        "first": "Richard",        "last": "Hendricks"      },      "address": {        "street": "5230 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      },      "dateOfBirth": "2001-08-10",      "email": "richard@piedpiper.com",      "phone": {        "countryCode": "1",        "number": "5555555555"      },      "idempotencyKey": "3a1a33be-4e12-4603-9ed0-820922389fb8",      "limits": {        "dailyWithdrawal": 50000,        "dailyPurchase": 50000,        "monthlyWithdrawal": 500000,        "monthlyPurchase": 700000      }    },    "relationships": {      "account": {        "data": {          "type": "depositAccount",          "id": "1"        }      }    }  }}'

Response#

Response is a JSON:API document.

201 Created#

FieldTypeDescription
dataBusinessVirtualDebitCardThe target resource after the operation was completed.
Example Response:
{  "data": {    "type": "businessVirtualDebitCard",    "id": "9",    "attributes": {      "createdAt": "2020-05-13T09:42:21.857Z",      "last4Digits": "2074",      "expirationDate": "2022-05",      "address": {        "street": "5230 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      },      "fullName": {        "first": "Richard",        "last": "Hendricks"      },      "phone": {        "countryCode": "1",        "number": "5555555666"      },      "email": "richard@piedpiper.com",      "dateOfBirth": "2001-08-10",      "status": "Active"    },    "relationships": {      "account": {        "data": {          "type": "depositAccount",          "id": "1"        }      },      "customer": {        "data": {          "type": "businessCustomer",          "id": "1"        }      }    }  }}

Displaying Sensitive Card Data#

In order to display sensitive card data (card number and CVV2) to a customer, without any of it passing through your systems (which would subject you to PCI compliance requirements), Unit utilizes a 3rd party service called Very Good Security or VGS for short.

info

Displaying card number and CVV2 for physical debit cards is disabled by default for security reasons.

This functionality can be enabled on Unit dashboard at: Settings -> Org Settings -> General -> Show sensitive data for physical cards.

note

Displaying and setting of sensitive card data is only available for active cards.

VGS provides a JavaScript library (as well as iOS and Android SDKs) called VGS Show which allows easy integration with your UI components. VGS Show offloads the PCI compliance burden by enabling the encrypted transmission of sensitive card data from VGS directly to your cardholder.

The VGS Show JavaScript library enables you to securely display sensitive data in a webpage while safely isolating that sensitive data from your systems. VGS Show JavaScript library injects a secure iframe into your HTML. VGS hosts both the iframe, and the data on secure, compliant servers.

Integration steps#

Import the VGS Show library:

<script type="text/javascript" src="https://js.verygoodvault.com/vgs-show/1.5/ACh8JJTM42LYxwe2wfGQxwj5.js"></script>

Initialize the component:

const show = VGSShow.create('tntazhyknp1');

Vault ID:

EnvironmentVault Id
Sandboxtntazhyknp1
Live (including pilot)tnt8w6nrmbu

Provide a valid customer token:

const customerToken = "<CUSTOMER TOKEN>"

For instructions, see Create Customer Token.

caution

The customer token is required to include the cards-sensitive scope. Unit recommends avoiding adding additional scopes to the token.

Provide a valid card identifier

path: '/cards/<CARD ID>/secure-data/cvv2'

  • Note: Timeout for this API is 120 seconds
Example of creating a Customer Token for sensitive data:
curl -X POST 'https://api.s.unit.sh/customers/8/token'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "customerToken",    "attributes": {      "scope": "cards-sensitive",      "verificationToken": "i8FWKLBjXEg3TdeK93G3K9PKLzhbT6CRhn/VKkTsm....",      "verificationCode": "203130"    }  }}'

Additional resources#

SDKs for iOS and Android and additional code examples can be found here.

Example of displaying card number and CVV2:
<!DOCTYPE html><html><head>    <meta charSet="utf-8">    <title></title>    <style>        iframe {            height: 20px;        }    </style></head><body><h2>Sensitive Data example</h2><label>Card Number:</label><div id="cardNumber"></div><label>CVV2:</label><div id="cvv2"></div>
<script type="text/javascript" src="https://js.verygoodvault.com/vgs-show/1.5/ACh8JJTM42LYxwe2wfGQxwj5.js"></script><script type="text/javascript">    const show = VGSShow.create('tntazhyknp1');    const customerToken = "<CUSTOMER TOKEN>";    const cardId = "<CARD ID>";
    const cvv2iframe = show.request({            name: 'data-text',            method: 'GET',            path: '/cards/' + cardId + '/secure-data/cvv2',            headers: {                "Authorization": "Bearer " + customerToken            },            htmlWrapper: 'text',            jsonPathSelector: 'data.attributes.cvv2'        });    cvv2iframe.render('#cvv2');
    const cardNumberIframe = show.request({            name: 'data-text',            method: 'GET',            path: '/cards/' + cardId + '/secure-data/pan',            headers: {                "Authorization": "Bearer " + customerToken            },            htmlWrapper: 'text',            jsonPathSelector: 'data.attributes.pan'        });    cardNumberIframe.render('#cardNumber');</script></body></html>

Activate Card#

In order to activate a new card, without any sensitive data passing through your systems (which requires PCI compliance), Unit utilizes a 3rd party service called Very Good Security or VGS for short.

VGS provides a JavaScript library (as well as iOS and Android SDKs) called VGS Collect which allows easy integration with your UI components. VGS Collect offloads the PCI compliance burden by enabling the encrypted transmission of sensitive card data from VGS directly to the card processor.

VGS Collect JavaScript library injects a secure iframe into your HTML. VGS hosts both the iframe, and the data on secure, compliant servers.

note

Unlike physical cards, virtual cards are created in Active status and do not require activation.

Integration steps#

Import the VGS Collect library:

<script type="text/javascript" src="https://js.verygoodvault.com/vgs-collect/2.12.0/vgs-collect.js"></script>

Initialize the component:

const form = VGSCollect.create("tntazhyknp1", "sandbox", function(state) {});

Parameters:

EnvironmentVault IdEnvironment Name
Sandboxtntazhyknp1sandbox
Live (including pilot)tnt8w6nrmbulive

Provide a valid customer token:

const customerToken = "<CUSTOMER TOKEN>"

For instructions, see Create Customer Token.

caution

The customer token is required to include the cards-sensitive-write scope. Unit recommends avoiding adding additional scopes to the token.

Provide a valid card identifier:

path: '/cards/<CARD ID>/activate'

Required field names and formats:

  • CVV2 field name should be data.attributes.cvv2.
  • Expiration Date field name should be data.attributes.expirationDate.
  • Expiration Date field format should be YYYY-MM.
  • Note: Timeout for this API is 120 seconds

Testing on Sandbox:

  • To simulate the scenario where the customer provides invalid data - send 1970-01 as the expiration date.
  • Sending anything other than that will result in a successful card activation.
Example of creating a Customer Token for Activate Card:
curl -X POST 'https://api.s.unit.sh/customers/8/token'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "customerToken",    "attributes": {      "scope": "cards-sensitive-write",      "verificationToken": "i8FWKLBjXEg3TdeK93G3K9PKLzhbT6CRhn/VKkTsm....",      "verificationCode": "203130"    }  }}'

Additional resources#

SDKs for iOS and Android and additional code examples can be found here.

Example of Activate Card:
<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>VGS Collect Credit Card Example</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script type="text/javascript" src="https://js.verygoodvault.com/vgs-collect/2.12.0/vgs-collect.js"></script>
    <style>        body {            padding: 25px;        }
        span[id*="cc-"] {            display: block;            height: 40px;            margin-bottom: 15px;        }
        span[id*="cc-"] iframe {            height: 100%;            width: 100%;        }
        pre {            font-size: 12px;        }
        .form-field {            display: block;            width: 100%;            height: calc(2.25rem + 2px);            padding: .375rem .75rem;            font-size: 1rem;            line-height: 1.5;            color: #495057;            background-color: #fff;            background-clip: padding-box;            border: 1px solid #ced4da;            border-radius: .25rem;            transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;        }
        .form-field iframe {            border: 0 none transparent;            height: 100%;            vertical-align: middle;            width: 100%;        }
        p {            margin-bottom: 10px;        }    </style></head><body><main>    <div class="row">        <div class="col-md-4"></div>        <div class="col-md-4">            <div class="row card card-outline-secondary">                <div class="card-body">                    <h3 class="text-center">Activate Card</h3>                    <hr>                    <div class="alert alert-info p-2">                        Please fill in and submit the form                    </div>                    <form id="cc-form">                        <div class="form-group">                            <label for="cc-cvv2">CVV2</label>                            <span id="cc-cvv2" class="form-field">                  <!--VGS Collect iframe for CVV2 field will be here!-->                </span>                            <label for="cc-expiration-date">Expiration Date</label>                            <span id="cc-expiration-date" class="form-field">                  <!--VGS Collect iframe for Expiration Date field will be here!-->                </span>                        </div>                        <!--Submit form button-->                        <button type="submit" class="btn btn-success btn-block">Submit</button>                    </form>                </div>            </div>        </div>        <div class="col-md-4"></div>    </div></main>
<!--Include script with VGS Collect form initialization--><script>    // VGS Collect form initialization    const form = VGSCollect.create("tntazhyknp1", "sandbox", function(state) {});    const customerToken = "<CUSTOMER TOKEN>";    const cardId = "<CARD ID>";
    form.field('#cc-cvv2', {        type: 'card-security-code',        name: 'data.attributes.cvv2',        successColor: '#4F8A10',        errorColor: '#D8000C',        placeholder: '123',        maxLength: 3,        validations: ['required', 'validCardSecurityCode']    });
    form.field('#cc-expiration-date', {        type: 'card-expiration-date',        name: 'data.attributes.expirationDate',        successColor: '#4F8A10',        errorColor: '#D8000C',        placeholder: 'MM / YYYY',        validations: ['required', 'validCardExpirationDate'],        // Change to required experiation date format        serializers: [form.SERIALIZERS.replace('(\\d{2})\\s\\/\\s(\\d{4})', '$2-$1')]    });
    // Submit by executing a POST request.    document.getElementById('cc-form')        .addEventListener('submit', function(e) {            e.preventDefault();            form.submit('/cards/' + cardId + '/activate',                {                    // This converts the dot-separated field name strings into a JSON object                    mapDotToObject: 'true',                    headers: {                        "Content-Type": "application/vnd.api+json",                        "Authorization": "Bearer " + customerToken                    }                },

                function(status, data) {                    console.log(data);                });        }, function (errors) {            console.log(errors);        });</script></body></html>

Set PIN#

Cards have a PIN that can be set through the API. The PIN must be set in order to allow cardholders to use their cards with PIN pads and ATMs.

In order to set a new PIN, without any sensitive data passing through your systems (which requires PCI compliance), Unit utilizes a 3rd party service called Very Good Security or VGS for short.

VGS provides a JavaScript library (as well as iOS and Android SDKs) called VGS Collect which allows easy integration with your UI components. VGS Collect offloads the PCI compliance burden by enabling the encrypted transmission of sensitive card data from VGS directly to the card processor.

VGS Collect JavaScript library injects a secure iframe into your HTML. VGS hosts both the iframe, and the data on secure, compliant servers.

info

In some instances, using a virtual card from a mobile wallet requires the user to provide a PIN (typically for larger purchases on a physical store, with a PIN pad). To support that use case, please give your cardholders the option to set their pin on virtual cards.

Integration steps#

Import the VGS Collect library:

<script type="text/javascript" src="https://js.verygoodvault.com/vgs-collect/2.12.0/vgs-collect.js"></script>

Initialize the component:

const form = VGSCollect.create("tntazhyknp1", "sandbox", function(state) {});

Parameters:

EnvironmentVault IdEnvironment Name
Sandboxtntazhyknp1sandbox
Live (including pilot)tnt8w6nrmbulive

Provide a valid customer token:

const customerToken = "<CUSTOMER TOKEN>"

For instructions, see Create Customer Token.

caution

The customer token is required to include the cards-sensitive-write scope. Unit recommends avoiding adding additional scopes to the token.

Provide a valid card identifier:

path: '/cards/<CARD ID>/secure-data/pin'

Required field names and formats:

  • PIN field name should be data.attributes.pin.
  • Note: Timeout for this API is 120 seconds
Example of creating a Customer Token for Set PIN:
curl -X POST 'https://api.s.unit.sh/customers/8/token'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "customerToken",    "attributes": {      "scope": "cards-sensitive-write",      "verificationToken": "i8FWKLBjXEg3TdeK93G3K9PKLzhbT6CRhn/VKkTsm....",      "verificationCode": "203130"    }  }}'

Additional resources#

SDKs for iOS and Android and additional code examples can be found here.

Example of Set PIN:
<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>Set PIN Example</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script type="text/javascript" src="https://js.verygoodvault.com/vgs-collect/2.12.0/vgs-collect.js"></script>
    <style>        body {            padding: 25px;        }
        span[id*="cc-"] {            display: block;            height: 40px;            margin-bottom: 15px;        }
        span[id*="cc-"] iframe {            height: 100%;            width: 100%;        }
        pre {            font-size: 12px;        }
        .form-field {            display: block;            width: 100%;            height: calc(2.25rem + 2px);            padding: .375rem .75rem;            font-size: 1rem;            line-height: 1.5;            color: #495057;            background-color: #fff;            background-clip: padding-box;            border: 1px solid #ced4da;            border-radius: .25rem;            transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;        }
        .form-field iframe {            border: 0 none transparent;            height: 100%;            vertical-align: middle;            width: 100%;        }
        p {            margin-bottom: 10px;        }    </style></head><body><main>    <div class="row">        <div class="col-md-4"></div>        <div class="col-md-4">            <div class="row card card-outline-secondary">                <div class="card-body">                    <h3 class="text-center">Set PIN</h3>                    <hr>                    <div class="alert alert-info p-2">                        Please fill in and submit the form                    </div>                    <form id="cc-form">                        <div class="form-group">                            <label for="cc-pin">PIN</label>                            <span id="cc-pin" class="form-field">                  <!--VGS Collect iframe for PIN field will be here!-->                </span>                        </div>                        <!--Submit form button-->                        <button type="submit" class="btn btn-success btn-block">Submit</button>                    </form>                </div>            </div>        </div>        <div class="col-md-4"></div>    </div></main>
<!--Include script with VGS Collect form initialization--><script>    // VGS Collect form initialization    const form = VGSCollect.create("tntazhyknp1", "sandbox", function(state) {});    const customerToken = "<CUSTOMER TOKEN>";    const cardId = "<CARD ID>";
    // Create VGS Collect field for PIN    form.field('#cc-pin', {        type: 'text',        name: 'data.attributes.pin',        successColor: '#4F8A10',        errorColor: '#D8000C',        placeholder: '1234',        maxLength: 6,        validations: ['required', '/^([0-9]{4,6})$/'],    });
    // Submit by executing a POST request.    document.getElementById('cc-form')        .addEventListener('submit', function(e) {            e.preventDefault();            form.submit('/cards/' + cardId + "/secure-data/pin',                {                    // This converts the dot-separated field name string into a JSON object                    mapDotToObject: 'true',                    headers: {                        "Content-Type": "application/vnd.api+json",                        "Authorization": "Bearer " + customerToken                    }                },

                function(status, data) {                    console.log(data);                });        }, function (errors) {            console.log(errors);        });</script></body></html>

Get PIN Status#

Gets the PIN status for the specified card.

If the returned status is NotSet, use Set PIN, otherwise use Change PIN.

VerbGET
Urlhttps://api.s.unit.sh/cards/{id}/secure-data/pin/status
Required Scopecards or cards-sensitive
Timeout (Seconds)120

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataPinStatusPinStatus resource.
curl -X GET 'https://api.s.unit.sh/cards/5/secure-data/pin/status' \-H "Authorization: Bearer ${TOKEN}"

Change PIN#

In order to change an existing PIN, without any sensitive data passing through your systems (which requires PCI compliance), Unit utilizes a 3rd party service called Very Good Security or VGS for short.

VGS provides a JavaScript library (as well as iOS and Android SDKs) called VGS Collect which allows easy integration with your UI components. VGS Collect offloads the PCI compliance burden by enabling the encrypted transmission of sensitive card data from VGS directly to the card processor.

VGS Collect JavaScript library injects a secure iframe into your HTML. VGS hosts both the iframe, and the data on secure, compliant servers.

note

For security purposes, if there are 3 failed attempts to change a PIN within a 24 hour period the API will display a "Maximum number of attempts reached" error and the ability to change the PIN will be disabled for a 24 hour period.

Integration steps#

Import the VGS Collect library:

<script type="text/javascript" src="https://js.verygoodvault.com/vgs-collect/2.12.0/vgs-collect.js"></script>

Initialize the component:

const form = VGSCollect.create("tntazhyknp1", "sandbox", function(state) {});

Parameters:

EnvironmentVault IdEnvironment Name
Sandboxtntazhyknp1sandbox
Live (including pilot)tnt8w6nrmbulive

Provide a valid customer token:

const customerToken = "<CUSTOMER TOKEN>"

For instructions, see Create Customer Token.

caution

The customer token is required to include the cards-sensitive-write scope. Unit recommends avoiding adding additional scopes to the token.

Provide a valid card identifier:

path: '/cards/<CARD ID>/secure-data/pin'

Required field names and formats:

  • Old PIN field name should be data.attributes.oldPin.
  • New PIN field name should be data.attributes.newPin.
Example of creating a Customer Token for Change PIN:
curl -X POST 'https://api.s.unit.sh/customers/8/token'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "customerToken",    "attributes": {      "scope": "cards-sensitive-write",      "verificationToken": "i8FWKLBjXEg3TdeK93G3K9PKLzhbT6CRhn/VKkTsm....",      "verificationCode": "203130"    }  }}'

Additional resources#

SDKs for iOS and Android and additional code examples can be found here.

Example of Change PIN:
<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>Change PIN Example</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script type="text/javascript" src="https://js.verygoodvault.com/vgs-collect/2.12.0/vgs-collect.js"></script>
    <style>        body {            padding: 25px;        }
        span[id*="cc-"] {            display: block;            height: 40px;            margin-bottom: 15px;        }
        span[id*="cc-"] iframe {            height: 100%;            width: 100%;        }
        pre {            font-size: 12px;        }
        .form-field {            display: block;            width: 100%;            height: calc(2.25rem + 2px);            padding: .375rem .75rem;            font-size: 1rem;            line-height: 1.5;            color: #495057;            background-color: #fff;            background-clip: padding-box;            border: 1px solid #ced4da;            border-radius: .25rem;            transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;        }
        .form-field iframe {            border: 0 none transparent;            height: 100%;            vertical-align: middle;            width: 100%;        }
        p {            margin-bottom: 10px;        }    </style></head><body><main>    <div class="row">        <div class="col-md-4"></div>        <div class="col-md-4">            <div class="row card card-outline-secondary">                <div class="card-body">                    <h3 class="text-center">Change PIN</h3>                    <hr>                    <div class="alert alert-info p-2">                        Please fill in and submit the form                    </div>                    <form id="cc-form">                        <div class="form-group">                            <label for="cc-old-pin">Old PIN</label>                            <span id="cc-old-pin" class="form-field">                  <!--VGS Collect iframe for Old PIN field will be here!-->                </span>                        </div>                        <div class="form-group">                            <label for="cc-new-pin">New PIN</label>                            <span id="cc-new-pin" class="form-field">                  <!--VGS Collect iframe for New PIN field will be here!-->                </span>                        </div>                        <!--Submit form button-->                        <button type="submit" class="btn btn-success btn-block">Submit</button>                    </form>                </div>            </div>        </div>        <div class="col-md-4"></div>    </div></main>
<!--Include script with VGS Collect form initialization--><script>    // VGS Collect form initialization    const form = VGSCollect.create("tntazhyknp1", "sandbox", function(state) {});    const customerToken = "<CUSTOMER TOKEN>"
    // Create VGS Collect field for Old PIN    form.field('#cc-old-pin', {        type: 'text',        name: 'data.attributes.oldPin',        successColor: '#4F8A10',        errorColor: '#D8000C',        placeholder: '1234',        maxLength: 6,        validations: ['required', '/^([0-9]{4,6})$/'],    });
    // Create VGS Collect field for New PIN    form.field('#cc-new-pin', {        type: 'text',        name: 'data.attributes.newPin',        successColor: '#4F8A10',        errorColor: '#D8000C',        placeholder: '1234',        maxLength: 6,        validations: ['required', '/^([0-9]{4,6})$/'],    });
    // Submit by executing a PUT request.    document.getElementById('cc-form')        .addEventListener('submit', function(e) {            e.preventDefault();            form.submit('/cards/10001/secure-data/pin',                {                    method: 'PUT',                    mapDotToObject: 'true',                    headers: {                        "Content-Type": "application/vnd.api+json",                        "Authorization": "Bearer " + customerToken                    }                },

                function(status, data) {                    console.log(data);                });        }, function (errors) {            console.log(errors);        });</script></body></html>

Report Stolen#

Report card as stolen.

VerbPOST
Urlhttps://api.s.unit.sh/cards/:cardId/report-stolen
Required Scopecards-write
Timeout (Seconds)5

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataIndividualDebitCard or BusinessDebitCard or IndividualVirtualDebitCard or BusinessVirtualDebitCardCard resource, distinguished by the type field.
curl -X POST 'https://api.s.unit.sh/cards/6/report-stolen' \-H "Authorization: Bearer ${TOKEN}"

Report Lost#

Report card as lost.

VerbPOST
Urlhttps://api.s.unit.sh/cards/:cardId/report-lost
Required Scopecards-write
Timeout (Seconds)5

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataIndividualDebitCard or BusinessDebitCard or IndividualVirtualDebitCard or BusinessVirtualDebitCardCard resource, distinguished by the type field.
curl -X POST 'https://api.s.unit.sh/cards/5/report-lost' \-H "Authorization: Bearer ${TOKEN}"

Close Card#

Close a card.

VerbPOST
Urlhttps://api.s.unit.sh/cards/:cardId/close
Required Scopecards-write
Timeout (Seconds)5

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataIndividualDebitCard or BusinessDebitCard or IndividualVirtualDebitCard or BusinessVirtualDebitCardCard resource, distinguished by the type field.
curl -X POST 'https://api.s.unit.sh/cards/7/close' \-H "Authorization: Bearer ${TOKEN}"

Freeze Card#

Freeze a card (temporarily block a card).

VerbPOST
Urlhttps://api.s.unit.sh/cards/:cardId/freeze
Required Scopecards-write
Timeout (Seconds)5

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataIndividualDebitCard or BusinessDebitCard or IndividualVirtualDebitCard or BusinessVirtualDebitCardCard resource, distinguished by the type field.
curl -X POST 'https://api.s.unit.sh/cards/8/freeze' \-H "Authorization: Bearer ${TOKEN}"

Unfreeze Card#

Unfreeze a frozen card.

VerbPOST
Urlhttps://api.s.unit.sh/cards/:cardId/unfreeze
Required Scopecards-write
Timeout (Seconds)5

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataIndividualDebitCard or BusinessDebitCard or IndividualVirtualDebitCard or BusinessVirtualDebitCardCard resource, distinguished by the type field.
curl -X POST 'https://api.s.unit.sh/cards/8/unfreeze' \-H "Authorization: Bearer ${TOKEN}"

Replace Card#

Creates and ships a replacement physical debit card to an individual or business card holder. The replacement card has the same card number, expiration date and PIN as the original card, and does not require additional activation.

note

Only cards with statuses Active and Inactive can be replaced.

VerbPOST
Urlhttps://api.s.unit.sh/cards/:cardId/replace
Required Scopecards-sensitive-write or cards-write
Data TypereplaceCard
Timeout (Seconds)5

Attributes#

NameTypeDescription
shippingAddressAddressOptional. Address to ship the replacement card to. If not specified, the address provided during card creation is reused. This will now effect the default shippingAddress
Example Request:
curl -X POST 'https://api.s.unit.sh/cards/8/replace'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "replaceCard",    "attributes": {      "shippingAddress": {        "street": "5230 Newell Rd",        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      }    }  }}'

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataIndividualDebitCard or BusinessDebitCardCard resource, distinguished by the type field.
Example Response:
{  "data": {    "type": "individualDebitCard",    "id": "8",    "attributes": {      "createdAt": "2020-05-13T09:07:47.645Z",      "last4Digits": "1234",      "expirationDate": "2022-05",      "shippingAddress": {        "street": "5230 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94303",        "country": "US"      },      "status": "Inactive"    },    "relationships": {      "account": {        "data": {          "type": "account",          "id": "2"        }      },      "customer": {        "data": {          "type": "customer",          "id": "2"        }      }    }  }}

Get by Id#

Get a card resource by id.

VerbGET
Urlhttps://api.s.unit.sh/cards/{id}
Required Scopecards
Timeout (Seconds)5

Query Parameters#

NameTypeDefaultDescription
includestring(empty)Optional. A comma-separated list of related resources to include in the response. Related resources include: customer, account. See Getting Related Resources

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataIndividualDebitCard or BusinessDebitCard or IndividualVirtualDebitCard or BusinessVirtualDebitCardCard resource, distinguished by the type field.
includedArray of DepositAccount or CustomerArray of resources requested by the include query parameter.
curl -X GET 'https://api.s.unit.sh/cards/1?include=customer' \-H "Authorization: Bearer ${TOKEN}"

List#

List cards. Filtering and paging can be applied.

VerbGET
Urlhttps://api.s.unit.sh/cards
Required Scopecards
Timeout (Seconds)5

Query Parameters#

NameTypeDefaultDescription
page[limit]integer100Optional. Maximum number of resources that will be returned. Maximum is 1000 resources. See Pagination.
page[offset]integer0Optional. Number of resources to skip. See Pagination.
filter[accountId]string(empty)Optional. Filters the results by the specified account id.
filter[customerId]string(empty)Optional. Filters the results by the specified customer id.
filter[tags]Tags (JSON)(empty)Optional. Filter Cards by Tags.
filter[status][]string(empty)Optional. Filter cards by Card Status. Usage example: filter[status][0]=Stolen&filter[status][1]=Lost
includestring(empty)Optional. A comma-separated list of related resources to include in the response. Related resources include: customer, account. See Getting Related Resources
sortstringsort=createdAtOptional. sort=createdAt for ascending order or sort=-createdAt (leading minus sign) for descending order.
curl -X GET 'https://api.s.unit.sh/cards?page[limit]=20&page[offset]=0&include=customer' \-H "Authorization: Bearer ${TOKEN}"

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataArray of IndividualDebitCard or BusinessDebitCard or IndividualVirtualDebitCard or BusinessVirtualDebitCardArray of card resources, distinguished by the type field.
includedArray of DepositAccount or CustomerArray of resources requested by the include query parameter.
Example Response:
{  "data": [    {      "type": "individualDebitCard",      "id": "1",      "attributes": {        "createdAt": "2020-05-10T16:45:02.272Z",        "last4Digits": "1234",        "expirationDate": "2022-05",        "status": "Active"      },      "relationships": {        "account": {          "data": {            "type": "depositAccount",            "id": "1"          }        },        "customer": {          "data": {            "type": "individualCustomer",            "id": "2"          }        }      }    },    {      "type": "businessDebitCard",      "id": "4",      "attributes": {        "createdAt": "2020-05-10T20:37:48.069Z",        "last4Digits": "1234",        "expirationDate": "2022-05",        "shippingAddress": {          "street": "5230 Newell Rd",          "street2": null,          "city": "Palo Alto",          "state": "CA",          "postalCode": "94303",          "country": "US"        },        "address": {          "street": "5230 Newell Rd",          "street2": null,          "city": "Palo Alto",          "state": "CA",          "postalCode": "94303",          "country": "US"        },        "fullName": {          "first": "Richard",          "last": "Hendricks"        },        "phone": {          "countryCode": "1",          "number": "5555555789"        },        "email": "richard@piedpiper.com",        "dateOfBirth": "2001-08-10",        "ssn": "123456789",        "status": "Active"      },      "relationships": {        "account": {          "data": {            "type": "depositAccount",            "id": "1"          }        },        "customer": {          "data": {            "type": "businessCustomer",            "id": "1"          }        }      }    }  ]}

Update#

Update a debit card.

note

Card creation requests are sent to the printer at 3AM ET daily. Therefore updates that are made past this time will only change the records but not the physical card.

VerbPATCH
Urlhttps://api.s.unit.sh/cards/:cardId
Required Scopecards-write
Timeout (Seconds)5

Update Individual Debit Card#

NameTypeDescription
shippingAddressAddressOptional. Address to ship the card to.
To modify or add specify the key and value.
To delete a key specify the key name and null for the value.
designstringOptional. Card design name. To modify or add specify the key and value.
tagsobjectOptional. See Updating Tags.
limitsobjectOptional. See Limits (cents).

Update Business Debit Card#

NameTypeDescription
shippingAddressAddressOptional. Address to ship the card to.
To modify or add specify the key and value.
To delete a key specify the key name and null for the value.
addressAddressOptional. Address of the card holder.
To modify or add specify the new address.
phonePhoneOptional. Phone of the card holder.
To modify or add specify the new phone number.
emailstringOptional. Email address of the card holder.
To modify or add specify the new email address.
designstringOptional. Card design name. To modify or add specify the key and value.
tagsobjectOptional. See Updating Tags.
limitsobjectOptional. See Limits (cents).

Update Individual Virtual Debit Card#

NameTypeDescription
tagsobjectOptional. See Updating Tags.
limitsobjectOptional. See Limits (cents).

Update Business Virtual Debit Card#

NameTypeDescription
addressAddressOptional. Address of the card holder.
To modify or add specify the new address.
phonePhoneOptional. Phone of the card holder.
To modify or add specify the new phone number.
emailstringOptional. Email address of the card holder.
To modify or add specify the new email address.
tagsobjectOptional. See Updating Tags.
limitsobjectOptional. See Limits (cents).

Response#

Response is a JSON:API document.

200 OK#

FieldTypeDescription
dataIndividualDebitCard Or BusinessDebitCard Or
BusinessVirtualDebitCard Or IndividualVirtualDebitCard resource
The target resource after the operation was completed.
Example of update business debit card:
curl -X PATCH 'https://api.s.unit.sh/cards/:cardId'-H 'Content-Type: application/vnd.api+json'-H 'Authorization: Bearer ${TOKEN}'--data-raw '{  "data": {    "type": "businessDebitCard",    "attributes": {      "shippingAddress": {        "street": "5231 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94301",        "country": "US"      },      "address": {        "street": "5231 Newell Rd",        "street2": null,        "city": "Palo Alto",        "state": "CA",        "postalCode": "94301",        "country": "US"      },      "email": "richard@piedpiper.com",      "phone": {        "countryCode": "1",        "number": "5555555556"      }    }  }}'

Limits#

VerbGET
Urlhttps://api.s.unit.sh/cards/:cardId/limits
Timeout (Seconds)5
curl -X GET 'https://api.s.unit.sh/cards/10104/limits' \-H "Authorization: Bearer ${TOKEN}"

Card purchases and withdrawals can be subjected to daily and monthly amount limits. Limits are optional (by default there are no card-specific limits), and can be set when the card is created or updated. The limits are enforced by Unit, and once the limit is reached, the transaction will be rejected.

The daily limits reset at 12:00 a.m. on the timezone of relevant bank. The monthly limits are reset at the same time on the first of each month.

info

Card level limits card-specific are used to allow more granular control and flexibility. The account limits on card activity are enforced in addition to them, across all cards that belong to a certain account combined (i.e. a customer who owns two cards with card ATM Withdrawal Limits of $300 cannot withdraw $300 on both cards if their deposit product ATM Withdrawal Limit is set to $500).

{  "data": {    "type": "limits",    "attributes": {      "limits": {        "dailyWithdrawal": 100000,        "dailyPurchase": 200000,        "monthlyWithdrawal": 5000000,        "monthlyPurchase": 7000000      },      "dailyTotals": {        "withdrawals": 3000,        "deposits": 0,        "purchases": 4000,        "cardTransactions": 50000      },      "monthlyTotals": {        "withdrawals": 120000,        "deposits": 0,        "purchases": 1400000,        "cardTransactions": 6000000      }    }  }}