Carbon

Documentation

Welcome to Carbon's docs!

Get Started    

Contacts / KYC API

const axios = require("axios");
const ROOT = process.env.NODE_ENV === "PRODUCTION" ? "https://api.carbon.money" : "https://sandbox.carbon.money";

Contact Transaction Limits

Status
Requirements
Maximum Deposit (Daily)

Unverified

Email Address / General Public Key

$250

Verified

ID/Passport Upload + Selfie with IDs + Face Scan

$2500

1. Create a contact (unverified) (POST)

Contacts are required to interact with Carbon's API. You must use your own authentication scheme to manage contact relationships.

Super JWT Required

Create a contact requirements

You must pass in either emailAddress, publicKey, or both to create a contact.

Usually passing in an emailAddress is preferable from the integration perspective because we have built-in user email notifications. However, some classes of users for certain integrations may not always have an email address readily available. Having these users share their email address may also add great friction to your UX and it may be more ideal to have these users share some other identifier such as a blockchain address, phone number, or something else linked from your side. Hence we added the ability to just specify a 'publicKey' on contact creation. Note that this 'publicKey' does not necessarily have to be a blockchain address.

Email notifications are turned off by default of course if 'emailAddress' is not specified. Even if a contact has an 'emailAddress' specified, you can turn off notifications here if you want to implement or have already implemented your own notification functionality.

Example Request Response

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};

let url = `${ROOT}/v1/contacts/create`;
let data = {
  emailAddress: "[email protected]",
  publicKey: "0xad891621341F2c102c4CbC365cCFD385d7FD46a7"
}

axios.post(url, data, headers).then(result => console.log(result)).catch(err => console.log(err));
{
    "message": "successfully created new contact!",
    "code": 200,
    "details": {
        "contactId": "857b7530-9ab0-4a2d-b786-f525d993a25a",
        "memo": "fa49122ab6e07443ebf6",
        "addresses": {
            "tron": "TMqvG6c3qeRVvAQYf9ES5K3uCBbkVsSAwm",
            "eth": "0xa71b9594AbdA2C451D7eaa8F28492646827Fe86E"
        }
    }
}
// 400
{
  "message": "You must past in at least 'emailAddress' or 'publicKey' to create a contact.",
  "code": 400
}

// 409
{
  "message": "User already exists with same email, testEmail. Please try again with a different email.",
  "code": 409
}

// 409
{
  "message": "User already exists with same publicKey, testPublicKey. Please try again with a different publicKey.",
  "code": 409
}

// 500
{
  "message": "Error creating contact.",
  "code": 500
}
Parameters
Access
Description

emailAddress

required or optional if you pass in publicKey

the email address of your contact

publicKey

required or optional if you pass in emailAddress

the public key of your contact. may be any general identifier on your side for linking that user as well.

2. Get current contact (GET)

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};

let url = `${ROOT}/v1/contacts/current?contactId=857b7530-9ab0-4a2d-b786-f525d993a25a`;

axios.get(url, headers).then(result => console.log(result)).catch(err => console.log(err));
{
  "message":"successfully retrieved contact",
  "code": 200,
  "details":{
    "emailAddress":"[email protected]",
    "dateCreated":"Mon May 27 2019 16:11:27 GMT+0000 (Coordinated Universal Time)",
    "contactId":"9898cdb5-c110-436f-8864-01fe84d5e477",
    "kycStatus":"2",
    "kycStatusStablecoin": true, // whether contact passed our stablecoin stack kyc
    "kycUpdateStablecoin": false, // whether contact already completed stablecoin stack application but needs to resubmit the form or docs
    " kycSentStablecoin": true, // whether contact submitted stablecoin stack kyc
    "remaningWeeklyLimit":"250",
    "kycFormUploaded": false,
      // note that 'documentUploaded' will be true if
      // either 'passportUploaded' is true
      // or 'driverLicenseFrontUploaded' AND 'driverLicenseBackUploaded'
      // are true
    "passportUploaded": false,
    "driverLicenseFrontUploaded": false,
    "driverLicenseBackUploaded": false,
    "documentUploaded": false,
    "livePhotoUploaded": false,
    "submittedCheck": false,
    "setup2FA": false, // contact has registered us as google 2fa provider
    "enabled2FA": false,
    "address": {
      
      
    }
  }
}

KYC Status Legend

Status
Description

0

no kyc submitted

1

submitted kyc but pending

2

submitted and verified successfully

3

errors in application and need to resubmit

4

resubmitted and pending

5

Contact has failed KYC and must contact Carbon again to allow submissions. This only happens in exceptional cases concerning AML or when contacts fails KYC three times or more.

3. Get contact by public key (GET)

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};

let url = `${ROOT}/v1/contacts/query?publicKey=examplePublicKey`;

axios.get(url, headers).then(result => console.log(result)).catch(err => console.log(err));

{ 
  contact: 'asdf adsf',
  contactId: '2b3c13eb-86c8-4fc9-8cf3-b73db8762a7a',
  publicKey: 'examplePublicKey',
  kycStatus: true,
  kycSent: true,
  status: 200,
  data: {
     ...
  }
}

4. Update contact's default currency (PATCH)

Example request/response

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};

let url = `${ROOT}/v1/contacts/updateCurrency`;

let data = {
  currency: "EUR",
  contactId: "ab5bb41b-5979-4a54-b734-23eb9076188e"
}

axios.patch(url, data).then(result => console.log(result)).catch(err => console.log(err));
{
  "message": "successfully updated currency!",
   "code": 200
}
// 401
{
  "message": "please enter a valid currency acronym (3 letter ISO 4217 standard)",
  "code": 401
}
Parameters
Access
Description

contactId

required

currency

required

Default currency used for calculating limits by adjusting our base USD amounts via the 'currency' / USD exchange rate. Must be a valid three-character ISO 4217 currency code. For a list of ISO 4217 codes go to this link: https://www.iban.com/currency-codes and check out our list of supported charge fiat currencies here.

5. Get all contacts (GET)

Example request/response

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};
let url = `${ROOT}/v1/contacts/all`;


axios.get(url, headers)
    .then(resp => {console.log(resp.data)})
    .catch(err => {console.log(err)})

{ message: 'Found contacts.',
  code: 200,
  data: 
   [ { id: '343eecc4-1854-45e2-8730-00198c01e3ec',
       emailAddress: '[email protected]' },
     { id: 'bbef7289-de03-460f-bb92-f5cd648aa30b',
       emailAddress: '[email protected]' },
      ]
}

6. Delete Contact (DELETE)

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};

let contactId = 'exampleContactId';
let url = `${ROOT}/v1/contacts/${contactId}`;


axios.delete(url, headers)
    .then(resp => {console.log(resp.data)})
    .catch(err => {console.log(err)})
// response with no body and status 204 returned
// 401
{
  "message": "Contact not found",
  "code": 401
}

// 500
{
  "message": "Error deleting contact",
  "code": 500
}
  

7. Verify Contact (POST)

Mandatory 2FA

After going through KYC, users will be required to use Google 2FA to secure your account for all purchases above the non-verified daily limit ($250). This is to protect you and Carbon against future fraud. πŸ€—

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};

let url = `${ROOT}/v1/contacts/verify`;

let data = {
  contactId: "ab5bb41b-5979-4a54-b734-23eb9076188e",
  firstName: "satoshi",
  lastName: "nakaomoto",
  dob: "2009-01-01",
  country: "USA",
  buildingNumber: "1234",
  street: "bitcoin bld",
  state: "MN",
  city: "blockchain",
  postalCode: "12345",
  firstName: "satoshi",
  lastName: "nakaomoto", 
  dob: "2009-01-01", 
  country:  "USA",
  buildingNumber: "1234",
  street: "bitcoin bld",
  state: "MN",
  city: "blockchain",
  postalCode: "12345"
}

axios.post(url, data, headers).then(result => console.log(result)).catch(err => console.log(err));
{
  "message":"Successfully verified contact! Below is the contact KYC ID for reference.",
    "code": 200,
  "details":{
    "token": ""
  }
}
// 400
{
  "message": "The following required parameters are missing when country is USA: state",
  "code": 400
}

// 500
{
  "message": "Error submitting verification.",
  "code": 500
}

// 500
{
  "message": "Error verifying contact.",
  "code": 500
}
Parameters
Access
Description

contactId

required

The contactId of the contact you wish to verify. You can obtain this from the /v1/contacts/create response above.

firstName

required

The legal first name of your contact.

lastName

required

The legal last name of your contact.

dob

required

The date of birth of your contact. YYYY-MM-DD format.

country

required

The country of the current residence of your contact. ISO 3 Code. Check here for full list: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3

buildingNumber

required

The number prefix of your building residence.

street

required

The street address (minus the buildingNumber at the beginning)

state

required for USA addresses

ISO 2 Code for the US state. Check here for the full list: https://en.wikipedia.org/wiki/ISO_3166-2:US

city

required

City of your contact's residence address.

postalCode

required

Postal code of your contact's address.

8. Update Contact (PATCH)

This endpoint is focused around resubmitting KYC for a contact rather than 'patching' the contact per se. For that reason you cannot hit this route for a contact before submitting KYC in the /v1/contacts/verify endpoint above.

Example Request/Response

Allowed Types

This is a list of the following fields you are allowed to modify: [ 'firstName', 'lastName', 'dob',
'country', 'buildingNumber', 'street', 'city', 'postalCode', 'state' ]. Note that these are the same request parameters as for the /v1/contacts/verify endpoint.

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};

let url = `${ROOT}/v1/contacts/update`;

let data = {
  contactId: "ab5bb41b-5979-4a54-b734-23eb9076188e",
  firstName: "satoshi",
  lastName: "nakaomoto",
  dob: "2009-01-01",
  country: "USA",
  buildingNumber: "1234",
  street: "bitcoin bld",
  state: "MN",
  city: "blockchain",
  postalCode: "12345"
}

axios.patch(url, data, headers).then(result => console.log(result)).catch(err => console.log(err));
{
  message: 'Successfully updated! Below is the contact KYC ID for reference.',
  code: 200,
  details:
   { 
     token: "", 
     applicantId: "", 
     header: "" 
   }
}
// 403
{
  "message":"Contact has not been through KYC - please submit KYC instead of updating",
  "code": 403
}

{
  "message":"Only certain contact parameters can be modified: firstName',  'lastName', 'dob', 'country', 'buildingNumber', 'street', 'city', 'postalCode', 'state' ",
  "code": 400,
}
  
{
  "message":"The following required parameters are missing when country is USA: state",
  "code": 400,
}
  
  {
  "message":"Error updating contact",
  "code": 500,
}

9. Upload Documents (POST)

Verify your contact before uploading photos!

You must hit the 'Verify Contact' endpoint first at /v1/contacts/verify to submit your contact's KYC/AML information before uploading documents and selfies.

Please only upload one KYC document/photo type at a time

You must upload a combination of a document ('passport' or 'driverLicenseFront' AND 'driverlicenseBack') and a selfie ('livePhoto') of the user with their uploaded KYC doc. The profile picture ('profilePic') is optional. More information is available in the below 'Request Parameters' section.

Example Request/Response

const mime = require('mime');
const fs = require('fs');
const FormData = require('form-data');
let url = `${ROOT}/v1/contacts/upload`;

let file = fs.readFileSync("test_pic.jpg");
let formData = new FormData();

formData.append("file", file, {
  filename: "test_pic.jpg",
  contentType: mime.getType('test_pic.jpg'),
} );

formData.append("fileType", "passport"); 
formData.append("contactId","ab5bb41b-5979-4a54-b734-23eb9076188e"); 

let formDataToBufferObject = formDataToBuffer( formData );
contentType = formData.getHeaders()['content-type'];

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`,
    'Content-Type': contentType
  }
};

axios.post(url, formDataToBufferObject, headers).then(result => console.log(result)).catch(err => console.log(err));
{
  'message': 'Successful profile pic upload!',
  'code': 200,
  'details': {
    'url': 'https://s3.amazonaws.com/carbon12/profilePics/sandbox/90a6e7b1-a3fd-458e-8217-a6a6c395061d.jpg'
   }
}

// fileType: 'passport'
{
  'message': 'Passport uploaded successfully!',
  'code': 200
}

// fileType: 'driverLicenseFront'
{
  'message': 'Front of drivers license uploaded successfully!',
  'code': 200
}

// fileType: 'driverLicenseBack'
{
  'message': 'Back of drivers license uploaded successfully!',
  'code': 200
}

// fileType: 'livePhoto'
{
  'message': 'Live photo uploaded successfully!',
  'code': 200
}
// 400
{
  'message': 'No files were uploaded.',
  'code': 400
}

// 401
{
  'message': 'Please add KYC personal information first before uploading.',
  'code': 400
}

// 401
{
  'message': 'fileType must be one of the following: passport, profilePic, driverLicenseFront, driverLicenseBack, livePhoto',
  'code': 401
}

// 500
{
  'message': 'Error uploading passport.',
  'code': 500
}

// 500
{
  'message': 'Error uploading front of drivers license.',
  'code': 500
}

// 500
{
  'message': 'Error uploading back of drivers license.',
  'code': 500
}

// 500
{ 
  'message': 'Error uploading live photo.',
  'code': 500
}

Request Parameters (Document Upload File Types)

Parameters
Access
Description

passport

required

The passport document of your user. The full passport page must be visible and clear in the picture. Make sure the photo is clear with no glare.

livePhoto

required

A live selfie of the user submitting the KYC. The face must be in good visibility and lighting without any sunglasses or other changes to the physical appearance. Also make sure the photo is clear with no glare.

driverLicenseFront*

required (if no passport)

The front side of your user's driver's license. Make sure the photo is clear with no glare. Check below for the countries we support for driver's licenses.

driverLicenseBack*

required (if no passport)

The back of your user's driver's license. Make sure the photo is clear with no glare. Check below for the countries we support for driver's licenses.

profilePic

optional

A user can upload a picture as part of their profile. This is not required by any means.

contactId

required

Passport upload is supported globally 🌎.

For driver's licenses, we support:

Albania, Andorra, Argentina, Armenia, Australia, Austria, Azerbaijan, Bahamas, Bangladesh, Barbados, Belarus, Belgium, Bermuda, Bolivia, Bosnia and Herzegovina, Botswana, Brazil, Brunei Darussalam, Bulgaria, Canada, Chile, China, Colombia, Costa Rica, Croatia, Cyprus, Czech Republic, CΓ΄te D'Ivoire, Denmark, Dominican Republic, Ecuador, Egypt, El Salvador, Estonia, Ethiopia, Faroe Islands, Fiji, Finland, France, Georgia, Ghana, Gibraltar, Greece, Guatemala, Guernsey, Hong Kong, Hungary, Iceland, Indonesia, Ireland, Isle of Man, Israel, Italy, Jamaica, Japan, Jersey, Jordan, Kuwait, Latvia, Liechtenstein, Lithuania, Luxembourg, Macao, Macedonia, the Former Yugoslav Republic Of, Malaysia, Malaysia, Malta, Mexico, Moldova, Republic of, Mongolia, Montenegro, Morocco, Namibia, Netherlands, New Zealand, Nigeria, Norway, Oman, Pakistan, Paraguay, Peru, Philippines, Poland, Portugal, Puerto Rico, Qatar, Romania, Russia, Rwanda, Saint Martin, Saudi Arabia, Serbia, Singapore, Slovakia, Slovenia, South Africa, Spain, Sri Lanka, Sweden, Switzerland, Taiwan, Tunisia, Turkey, Uganda, Ukraine, United Arab Emirates, United Kingdom, United States, Uruguay, Venezuela, Bolivarian Republic of, Vietnam, Yemen, Zambia

File Upload Technical Requirements

The valid file types for passport are: jpg, png and pdf.

Valid file types for livePhoto, driverLicenseFront, and driverLicenseBack are jpg and png.

The file size must be between 32KB and 10MB.

example of good passport upload

example of good passport upload

Live Photo Upload Clarification Sandbox

Please upload a true selfie (meaning a photo exposing a human face) for 'livePhoto' uploads. An example you can use for reference is here: https://s3.amazonaws.com/carbon12/profilePics/sandbox/2ca25ae6-2437-4557-959f-33c1fd172c2a.jpg

formDataToBuffer usage

Note that the formDataToBuffer helper function is presented and explained in the Helper Function section below.

Make sure to submit your KYC check!

You will need to hit the following endpoint to finalize your KYC submission and send over to our provider for clearance or rejection. You can subscribe to updates in webhooks or query for your contact's KYC status in the 'Get current contact' endpoint.

10. Submit KYC Check (POST)

Duplicate Check Submissions

Note that a KYC check for a contact cannot be submitted if a check for that contact is already being processed. This endpoint will trigger an error in that case. If KYC fails post-submission, then the KYC check can be re-submitted in particular after any necessary updates to the KYC application are made.

Example Request/Response

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};

let url = `${ROOT}/v1/contacts/submitCheck`;

let data = {
  contactId: "ab5bb41b-5979-4a54-b734-23eb9076188e"
}

axios.post(url, data, headers).then(result => console.log(result)).catch(err => console.log(err));
{
  message: ' Successfully submitted KYC check for contact with id ab5bb41b-5979-4a54-b734-23eb9076188e.',
  code: 200
}
// 429
{
  'message': 'Please first submit KYC form for contact before submitting check. No KYC id is linked to this contact.',
  'code': 429
}

// codes may be 4xx-5xx
{
  'message': 'Error submitting check call',
  'code': 500
}

// 500
{
  'message': 'Error submitting check',
  'code': 500
}

Request Parameters

Parameters
Access
Description

contactId

required

11. KYC Requirements For Transaction Endpoint (POST)

Super JWT and Contact Id Optional

If a contact ID is not passed in, then we will return kyc requirements for a generic contact with the most general rules applying.

Endpoint Interpretation

Note that this endpoint assumes your contact's remaining daily limit is their daily max (right now $250 for unverified contacts, $2500 for verified) so they have not yet transacted since their limits were last reset. The amount parameter is thus more interpreted as the largest possible transaction this contact can make fulfilling certain returned kyc requirements if applicable. Note that this endpoint is not currently necessarily as we have a static KYC process with static KYC tiers (unverified or verified) and (fairly) static transaction amounts limits for all contacts given fulfilled kyc requirements (with some exceptions given for anti-fraud purposes). However this endpoint will be updated to reflect expected dynamic KYC logic for future versions of this API.

KYC Requirements Interpretation

The details.kycRequirements response body param will include the necessary kyc fields that must be fulfilled (i.e. set to true) for the contact for kyc to be considered complete. Right now, this includes submitting a kyc form (/v1/contacts/verify), uploading a document (either a passport or front/back of driver's license at the /v1/contacts/upload endpoint), uploading a selfie (a live photo of a contact with their government id at the /v1/contacts/upload endpoint), and finalizing the kyc submissions at the /v1/contacts/submitCheck endpoint. This kycRequirements object will be an empty object if kyc is not required for a transaction amount for the contact (details.kycRequired is false and details.allowed is true) OR if the transaction amount is not possible for the contact (details.allowed is false and details.kycRequired is false because it is will not exclusively allow the transaction).

KYC Requirements Contact Fields

Note that the kycRequirements response data will include the necessary contact fields that must be set to true for the kyc to be considered complete to allow the transaction amount for a contact. It will only be nonempty if there are any kyc requirements necessary to allow the transaction amount and necessarily whether a certain tier of KYC would enable the transaction. Note that apart from the unverified tier we only have one another KYC tier: verified (where 'kycPassOnfido' is 2.). These fields and others can be read in the /v1/contacts/current endpoint.

let jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJlMzE3YjdlNy0yMzQ1LTQ0MWMtODA0Ni1kYjgxNTkyYmEyN2YiLCJzdXBlclVzZXIiOnRydWUsImNvbnRhY3QiOmZhbHNlLCJlbWFpbCI6ImRhbmllbEBjYXJib24ubW9uZXkiLCJpYXQiOjE1NTczMjc5MTR9.WZnSR5N1FebmT9nMu97PJvku49NY0jk4aKVPKm_1MlM';

let headers = {
  headers: {
    Authorization: `Bearer ${jwtToken}`
  }
};

let url = `${ROOT}/v1/contacts/kycRequirements`;

let data = {
  contactId: "ab5bb41b-5979-4a54-b734-23eb9076188e", // optional
  amount: "100" // case 1. case 2 and case 3 involves amount 1000, 10000 USD respectively
}

axios.post(url, data, headers).then(result => console.log(result)).catch(err => console.log(err));
// case 1: amount is between 0 and our non-verified usd daily limit (right now $250)
// example: 100

{
  message: ' Successfully obtained kyc requirements for contact for transaction amount 100.',
  code: 200,
  details: {
    allowed: true,
    kycRequired: false,
    kycRequirements: {
    } // empty object in this means no requirements necessary for transaction amount
  }
}

// case 2: amount is between our non-verified usd daily limit and verified daily limit (right now $250 and $2500, respectively)
// example: 1000

{
  message: ' Successfully obtained kyc requirements for contact for transaction amount 1000.',
  code: 200,
  details: {
    allowed: true,
    kycRequired: true,
    kycRequirements: {
      kycFormUploaded: true, // whether contact submitted kyc information such as their date of birth and address
      docUploaded: true, // either passport uploaded or front/back of driver's license uploaded
      livePhotoUploaded: true, // selfie of contact holding government id uploaded
      submittedCheck: true, // whether kyc submission was finalized and check requested for kyc clearance or rejection
    }   
  }
}
  
  // case 3: amount exceeds our verified daily limit (right now $2500)
// example: 10000

{
  message: ' Successfully obtained kyc requirements for contact for transaction amount 10000.',
  code: 200,
  details: {
    allowed: false,
    kycRequired: false,
    kycRequirements: {
      // empty object in this case means no requirements would possibly enable transaction for contact
    }   
  }
}
// 400
{
  'message': 'amount must be a number or string able to be interpreted as a number.',
  'code': 400
}

// 400
{
  'message': 'amount must be greater than 0.',
  'code': 400
}

// 500
{
  'message': 'Error obtaining kyc requirements for contact and transaction amount.',
  'code': 500
}
Parameters
Access
Description

contactId

optional

If a contactId is not passed in, then most general rules are assumed.

amount

required

Can be of type string or number. kyc requirements to be obtained for contact transacting at this USD value. Note that unlike in our credit/debit API amount is read in dollars directly. For example '1000' is interpreted as $1000 not $10 as the credit/debit api generally scales input amount data down by 100.

File Upload Helper Functions

Code for formDataToBuffer helper function for serializing formData into a buffer request body & extracting the Content-Type request header. This helper function in our experience is the most consistent and reliable way to send form data involving files of different content types interacting with different servers handling uploaded files in various ways. Feel free to upload files in whatever way works for you with our API.

const FormData = require('form-data');


function formDataToBuffer( formData ) {
  let dataBuffer = new Buffer( 0 );
  let boundary   = formData.getBoundary();
  for( let i = 0, len = formData._streams.length; i < len; i++ ) {

      if( typeof formData._streams[i] !== 'function' ) {

          dataBuffer = bufferWrite( dataBuffer, formData._streams[i] );


          if( typeof formData._streams[i] !== 'string' || formData._streams[i].substring( 2, boundary.length + 2 ) !== boundary ) {
              dataBuffer = bufferWrite( dataBuffer, "\r\n" );
          }
      }
  }

  dataBuffer = bufferWrite( dataBuffer, '--' + boundary + '--' );

  return dataBuffer;
}

function bufferWrite( buffer, data ) {

  let addBuffer;
  if( typeof data === 'string' ) {
      addBuffer = Buffer.from( data );
  }
  else if( typeof data === 'object' && Buffer.isBuffer( data ) ) {
      addBuffer = data;
  }

  return Buffer.concat( [buffer, addBuffer] );
}


Thank you for your feedback

What's Next

2FA

Contacts / KYC API


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.