Get Sandbox Access

Paybase Developer Centre

OverviewAPI GuidesGetting StartedRecipesGig Economy PlatformsSharing Economy PlatformsMarketplacesBlockchain BusinessesEscrowSandboxRolesRulesDue DiligenceCustomersAccountsBank AccountsCardsTransactionsIntroductionInboundGetting money into the systemTransaction ReferenceAccount ReferenceInternalOutboundEscrowSplit PaymentsRefundsStrong Customer Authentication3D Secure AuthenticationIntroductionCreate a cardCreate a transactionDocument UploadStatementsWebhooksErrorsPQLAPI ReferenceAccountCreate an accountRetrieve an accountTransition account statusList all accountsAnnotate an accountDelete annotation from an accountTag an accountDelete tag from an accountBank AccountCreate a bank accountRetrieve a bank accountUpdate a bank accountTransition bank account statusList all bank accountsAnnotate a bank accountDelete annotation from a bank accountTag a bank accountDelete tag from a bank accountCardCreate a cardRetrieve a cardUpdate a cardTransition card statusList all cardsAnnotate a cardDelete annotation from a cardTag a cardDelete tag from a cardCardholderCreate a cardholderRetrieve a cardholderUpdate a cardholderTransition cardholder statusList all cardholdersAnnotate a cardholderDelete annotation from a cardholderTag a cardholderDelete tag from a cardholderCreate an authentication tokenCheckCreate a checkCustomerIndividual CustomerCreate a customerRetrieve a customerUpdate a customerSole TraderCreate a customerRetrieve a customerUpdate a customerOrganisationCreate a CustomerRetrieve a CustomerUpdate a CustomerIncorporated BusinessCreate a customerRetrieve a customerUpdate a customerBusiness PersonAdd a business personRetrieve a business personUpdate a business personDelete a business personRetrieve a customerTransition state of a customerList all customersAnnotate a customerDelete annotation from a customerTag a customerRemove tag from a customerCreate an authentication tokenTouch a customerDocumentCreate a documentRetrieve a documentList Document TypesReferenceRetrieve a referenceStatementRetrieve a statementStatusRetrieve API statusTransactionCreate inbound transactionCreate internal transactionCreate outbound transactionRetrieve a transactionTransition transaction statusList all transactionsAnnotate a transactionDelete annotation from a transactionTag a transactionDelete tag from a transaction
API version: ba085a6

Blockchain Businesses - Exchanges and Wallets

Blockchain Businesses

Blockchain is the future of Fintech and has so many applications across diverse verticals as the Internet of Value. Paybase currently do not use any blockchain functionality or stablecoins in our stack but as fiat experts, we handle the fiat aspect of your products. Our API allows you to open fiat accounts for your customers through a straight-forward onboarding flow that you can design with the ability to build peer-to-peer flows. We understand that most platforms in the space have strong due diligence processes in place and therefore Paybase can give you the option to provide results of your checks to minimise duplication of effort.

There are many sophisticated tools available for transaction monitoring of assets from a blockchain perspective. Indeed, we do not support products that have any obscured ledgers in their ecosystem. Paybase’s award-winning fiat transaction monitoring Rules Engine closes the loop to provide an additional level of protection from AML risk.

Product flows differ from business to business. Below is an example of an exchange or Fintech that is integrated with liquidity exchanges such as Bitstamp or Coinbase and how they can leverage Paybase’s API for instant fiat/crypto interoperability.

Onboarding users

Users refers to the participants on your platform who will buy and sell crypto via an exchange that you’re plugged into, and will use Paybase accounts for fiat transactions. These will typically be individuals.

Given that you control the entire experience of the users on your platform, and Paybase never interacts directly with them, you will need to build flows to collect the necessary information required for performing Due Diligence on the users. You will also need to display a link to the Paybase Terms that the users will need to accept as part of your onboarding flow. Once you collect this information, you will be able to create the user as a Customer on Paybase, and attach an Account and Bank Account to them.

Before you can create a Customer, you will need to know the user’s role and Target CDD Level associated with it. The Target CDD of the Role will determine the minimum information required to complete due diligence on the user for them to be able to start transacting on Paybase. This is set-up by Paybase when your Sandbox account is created and can be found on Console. The Target CDD Level of the role will usually be two.

To create a user who is an individual, use the Create an Individual Customer request with the roleSlug as determined above. You will also need to provide the following details about the user:

  • First Name
  • Last Name
  • Email
  • Phone Number
  • Full Residential Address
  • Date of Birth
  • Version of Paybase Terms accepted and the time of acceptance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const result = await client.createIndividualCustomer({
  roleSlug: "user",
  profile: {
    firstName: "John",
    lastName: "Doe",
    dob: "1986-04-29T00:00:00.000Z",
    email: "john.doe@email.com",
    phoneNumber: "+442078137221",
    residentialAddress: {
      postalCode: "SE6 9YU",
      countryISO: "GB",
      houseNameNumber: "7",
      street: "Brick Lane",
      townCity: "London"
    }
  },
  terms: {
    acceptedAt: "2019-08-16T18:29:02.013Z",
    revision: "2.1"
  }
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/customers/individual",
  data = json.dumps({
    "roleSlug": "user",
    "profile": {
      "firstName": "John",
      "lastName": "Doe",
      "dob": "1986-04-29T00:00:00.000Z",
      "email": "john.doe@email.com",
      "phoneNumber": "+442078137221",
      "residentialAddress": {
        "postalCode": "SE6 9YU",
        "countryISO": "GB",
        "houseNameNumber": "7",
        "street": "Brick Lane",
        "townCity": "London"
      }
    },
    "terms": {
      "acceptedAt": "2019-08-16T18:29:02.013Z",
      "revision": "2.1"
    }
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<X-Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/customers/individual",
  [
    "body" => "{
      \"roleSlug\": \"user\",
      \"profile\": {
        \"firstName\": \"John\",
        \"lastName\": \"Doe\",
        \"dob\": \"1986-04-29T00:00:00.000Z\",
        \"email\": \"john.doe@email.com\",
        \"phoneNumber\": \"+442078137221\",
        \"residentialAddress\": {
          \"postalCode\": \"SE6 9YU\",
          \"countryISO\": \"GB\",
          \"houseNameNumber\": \"7\",
          \"street\": \"Brick Lane\",
          \"townCity\": \"London\"
        }
      },
      \"terms\": {
        \"acceptedAt\": \"2019-08-16T18:29:02.013Z\",
        \"revision\": \"2.1\"
      }
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<X-Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/customers/individual"  -H "Content-Type: application/json" \
  -H "X-Token: <X-Token goes here>" \
  -d '{
    "roleSlug": "user",
    "profile": {
      "firstName": "John",
      "lastName": "Doe",
      "dob": "1986-04-29T00:00:00.000Z",
      "email": "john.doe@email.com",
      "phoneNumber": "+442078137221",
      "residentialAddress": {
        "postalCode": "SE6 9YU",
        "countryISO": "GB",
        "houseNameNumber": "7",
        "street": "Brick Lane",
        "townCity": "London"
      }
    },
    "terms": {
      "acceptedAt": "2019-08-16T18:29:02.013Z",
      "revision": "2.1"
    }
  }'

The response to a successful API call will be the full Customer object including the id, stateId, cddLevel and targetCDDLevel. Store the id in your database as you will need to pass it for any future requests associated with this Customer. The stateId will be returned as ENABLED. However, note that a Customer can only start transacting when their cddLevel equals their targetCDDLevel. The cddLevel will initially be returned as ZERO because the due diligence checks, whilst automated, are performed asynchronously. If automated checks pass, then the cddLevel of the Customer will be updated to the targetCDDLevel shortly after creation. You can monitor asynchronous events using webhooks.

If you have users that are businesses, the Customers page provides further guidance on creating other types of users such as Incorporated Businesses and Sole Traders.

Creating accounts for users

In order to make a crypto purchase, your users will pay in via bank transfers as card payments are not supported for crypto. Therefore, you will need to create an Account for your users into which they will be able to deposit funds and purchase crypto from their balance.

Generate a customer authentication token and use the Create Account API to create a new Account associating it with the user by providing the previously saved Customer id in the ownerId attribute.

1
2
3
4
5
6
7
8
9
10
11
12
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c"
});

const result = await client.createAccount({
  ownerId: "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c",
  currencyISO: "GBP"
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/accounts",
  data = json.dumps({
    "ownerId": "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c",
    "currencyISO": "GBP"
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<Customer Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/accounts",
  [
    "body" => "{
      \"ownerId\": \"customer/82c6fb26-8027-454a-8451-ae37c0f9d72c\",
      \"currencyISO\": \"GBP\"
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/accounts"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    "ownerId": "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c",
    "currencyISO": "GBP"
  }'

The response to the API call will be the full Account object. You should store the account id as this will be required when you create transactions and interact with the account.

Adding bank accounts for users

For paying in via bank transfers, you don’t need to explicitly collect the sending bank details upfront from your users. When the user sends their first payment, a bank account object will automatically be created with the sender’s details and linked to the user as their preferred payment instrument. This bank account can later be used by the user to withdraw their deposits or proceeds of the sale of a crypto asset. You can use the retrieve customer API to get the id of the preferred bank account of the user.

Accepting bank transfers from users

Bank transfers involve users sending funds to their account on Paybase. Call the Retrieve Reference API providing the account id of the user as a URI parameter.

1
2
3
4
5
6
7
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const result = await client.getReference({
  toId: "account/28958679-e8a8-47a8-967c-f979ae8509a2"
});

1
2
3
4
5
6
7
8
9
10
import json
import requests

requests.get(
  "https://api-json.sandbox.paybase.io/v1/reference/account/28958679-e8a8-47a8-967c-f979ae8509a2Params: toId",
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<X-Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
$client = new \GuzzleHttp\Client();
$client->request(
  "get",
  "https://api-json.sandbox.paybase.io/v1/reference/account/28958679-e8a8-47a8-967c-f979ae8509a2Params: toId",
  [
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<X-Token goes here>",
    ]
  ]
);

1
2
3
4
curl -X GET \
"https://api-json.sandbox.paybase.io/v1/reference/account/28958679-e8a8-47a8-967c-f979ae8509a2Params: toId"  -H "Content-Type: application/json" \
  -H "X-Token: <X-Token goes here>" \

The API call will return the bank details to which the user will need to make a transfer. This also includes a reference which will need to be provided on the bank transfer. You should display these bank details to the user instructing them to make a transfer with the reference provided. The reference returned is a static account reference which should be used each time the user wants to deposit funds in their account. It is recommended that you highlight to your users the importance of sending the transfer with the accurate reference as otherwise the transaction will automatically be returned to the sender. Allowing users to easily copy the reference on your UI is often useful.

In Sandbox, you can simulate an incoming bank transfer from your users for testing purposes.

Once a transfer has been made by the user, the funds will be reflected in the user’s account balance.

Buying and selling crypto assets

Since Paybase is not involved in the crypto flow, buying assets will simply involve transferring the fiat amount from a user’s Paybase account to your own Paybase account from where you can make a bank account withdrawal to your crypto liquidity partner. Simultaneously, you will credit the user’s crypto wallet that you manage outside of Paybase.

To start with, use the Create Account API to create a dedicated fiat liquidity account for yourself into which you can transfer funds from users. You will need to provide your tenantId in the ownerId attribute which can be found in the Profile section of the Console. Usually, it is advisable to keep this account separate from any other Paybase account owned by you in which you might collect fees.

When a user places a buy order, generate a customer authentication token for the user and create an internal transaction for the purchase amount from the user’s account to your account created above.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "<- Sending Customer ID ->"
});

const result = await client.createInternalTransaction({
  fromAccountId: "account/46092ec4-83d1-4ed9-845e-a942078e62cf",
  toAccountId: "account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f",
  amount: "1000"
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/internal",
  data = json.dumps({
    "fromAccountId": "account/46092ec4-83d1-4ed9-845e-a942078e62cf",
    "toAccountId": "account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f",
    "amount": "1000"
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<Customer Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/internal",
  [
    "body" => "{
      \"fromAccountId\": \"account/46092ec4-83d1-4ed9-845e-a942078e62cf\",
      \"toAccountId\": \"account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f\",
      \"amount\": \"1000\"
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/internal"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    "fromAccountId": "account/46092ec4-83d1-4ed9-845e-a942078e62cf",
    "toAccountId": "account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f",
    "amount": "1000"
  }'

Similarly, when a user places a sell order you will create an internal transaction for sold amount from your liquidity account to the user’s account.

If your platform facilitates P2P buying and selling of crypto amongst your user base, then instead of creating internal transactions between the user and your account, you will need to create internal transactions between the accounts of the buying and selling users.

Charging fees on transactions

So far, you have been able to onboard your users and handle the fiat side of the crypto asset buying and selling process. You might also want to charge your platform fees on these transactions, or on the spread.

Paybase’s fee rules allow you to configure how you charge fees at one or multiple stages of the payment flows. For example, you want to charge your users a fee when they buy a crypto asset. A fee rule can be configured on your account by Paybase, and you will be given an ‘annotation key’ for the rule (e.g. fee).

Fees are specified at the time of setting up a transaction and therefore the structure of the API call will differ slightly.

Generate a customer authentication token for the user and call the create an internal transaction API to create a transaction from the user to your liquidity account. This time you will provide annotations as in the example below to indicate the amount to be sent to your liquidity account, and also the fee that you want to deduct and transfer to your fee account.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "<- Sending Customer ID ->"
});

const result = await client.createInternalTransaction({
  fromAccountId: "account/46092ec4-83d1-4ed9-845e-a942078e62cf",
  toAccountId: "account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f",
  amount: "1000",
  annotations: {
    fee: "20"
  }
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/internal",
  data = json.dumps({
    "fromAccountId": "account/46092ec4-83d1-4ed9-845e-a942078e62cf",
    "toAccountId": "account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f",
    "amount": "1000",
    "annotations": {
      "fee": "20"
    }
  }),
  headers = {
    "X-Token": "<... snip ...>",
    "Content-Type": "application/json"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/internal",
  [
    "body" => "{
      \"fromAccountId\": \"account/46092ec4-83d1-4ed9-845e-a942078e62cf\",
      \"toAccountId\": \"account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f\",
      \"amount\": \"1000\",
      \"annotations\": {
        \"fee\": \"20\"
      }
    }",
    "headers" => [
      "X-Token" => "<... snip ...>",
      "Content-Type" => "application/json",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/internal"  -H "X-Token: <... snip ...>" \
  -H "Content-Type: application/json" \
  -d '{
    "fromAccountId": "account/46092ec4-83d1-4ed9-845e-a942078e62cf",
    "toAccountId": "account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f",
    "amount": "1000",
    "annotations": {
      "fee": "20"
    }
  }'

An internal transfer excluding fees will be created to your liquidity account, and an adjustment on the transaction will transfer the specified fees to your fee account.

Making withdrawals for users

Your users need to be able to withdraw funds on their accounts back to their bank accounts.

Generate a customer authentication token for the user and call the create outbound transaction API providing the user’s accountId to indicate the source account and the user’s bank account id as the paymentInstrumentId.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "<- Sending Customer ID ->"
});

const result = await client.createOutboundTransaction({
  accountId: "account/8810a798-3141-11ea-978f-2e728ce88125",
  paymentInstrumentId: "bank_account/7ef61227-0087-434f-bf8d-408c552a4efc",
  amount: "990"
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/outbound",
  data = json.dumps({
    "accountId": "account/8810a798-3141-11ea-978f-2e728ce88125",
    "paymentInstrumentId": "bank_account/7ef61227-0087-434f-bf8d-408c552a4efc",
    "amount": "990"
  }),
  headers = {
    "X-Token": "<... snip ...>",
    "Content-Type": "application/json"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/outbound",
  [
    "body" => "{
      \"accountId\": \"account/8810a798-3141-11ea-978f-2e728ce88125\",
      \"paymentInstrumentId\": \"bank_account/7ef61227-0087-434f-bf8d-408c552a4efc\",
      \"amount\": \"990\"
    }",
    "headers" => [
      "X-Token" => "<... snip ...>",
      "Content-Type" => "application/json",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/outbound"  -H "X-Token: <... snip ...>" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "account/8810a798-3141-11ea-978f-2e728ce88125",
    "paymentInstrumentId": "bank_account/7ef61227-0087-434f-bf8d-408c552a4efc",
    "amount": "990"
  }'

The API call will create an outbound payment to the user’s bank account via Faster Payments in the UK.

Webhooks

You should also ensure that you have a webhook set up to listen to some events that will allow you to provide a better experience to your users. Paybase will whitelist the URLs to which you want events to be posted.

In particular, you should consider listening to the following webhooks:

  • customer_cdd_level_changed to keep track of the cdd level of customers and know when they can start transacting
  • check_failed to be notified if any of the automated due diligence checks failed and therefore require additional information
  • alert_action_required if an alert has been raised that requires actioning on the Console before the associated event can be completed (e.g. transaction paused, customer verification failed etc.)
  • transaction_failed to monitor any transactions that failed, particular those that were refunded to sender due to an incorrect reference
  • account_balance_updated to know when a user’s bank transfer has been credited to their Paybase account

Other considerations

  • Strong Customer Authentication - You should ensure that your platform complies with SCA requirements as detailed in the SCA guide.
  • Front End Checklist - Please refer to general guidance on front end requirements that you need to comply with when building your platform.
  • Uploading Documents - useful for uploading additional verification documents required when automated checks fail. This can also be done manually on the Console.