Get Sandbox Access

Paybase Developer Centre

OverviewAPI GuidesGetting StartedRecipesGig Economy PlatformsSharing Economy PlatformsMarketplacesBlockchain BusinessesEscrowRolesRulesDue 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: d8b96b6

Gig Economy Platforms

Gig Economy

Unlike traditional goods marketplaces, service marketplaces have a higher rate of network leakage because it is often the case that employer and worker meet in person for the job to be completed and communicate directly. This is a huge monetisation problem for platforms who take commission on jobs completed because every time that users transact in person, the platform loses their potential revenue.

Reduce leakage on your gig economy platform

Paybase has two unique capabilities that dramatically reduce network leakage: instant end-to-end payments and escrow via API.

It is typical for gig economy workers to only receive payment into their bank account 10 days after completing the job. It’s therefore no surprise that workers prefer to bypass platforms and get paid in person. Most gig economy platforms lose the vast majority of their revenue for this reason. Using the Paybase API, an end-to-end payment from a user’s card or invoice to the worker’s bank account can be completed in under 30 seconds! This represents not only a secure and cashless but also onsite and in-person transaction for the job being completed.

Combining lightning-quick payments with Escrow is a second great way to add value and reduce network leakage. You can use our API to construct and protect multiple milestone projects to ensure monetisation of the full relationship - not just the first job. Check out our dedicated recipe for Escrow here.

Onboarding workers

Worker refers to any individuals or businesses that are providing their services via your platform. Given that you control the entire experience of the workers 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 workers. You will also need to display a link to the Paybase Terms that the workers will need to accept as part of your onboarding flow. The Terms will be hosted by Paybase and a URL can be found under the Assets tab on the Console. Once you collect this information, you will be able to create the worker 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 following:

  • Whether the worker is an Individual, Sole Trader or Incorporated Business
  • Worker’s Role and Target CDD Level associated with it. The Target CDD Level of the Role will determine the minimum information required to complete due diligence on the worker for them to be able to start receiving payments. This is set-up by Paybase when your Sandbox account is created and can be found on Console

To create a worker 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 worker:

  • 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: "worker",
  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: "worker",
    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: \"worker\",
      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: "worker",
    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. However, 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.

The Customers page provides further guidance on creating other types of Customers such as Incorporated Businesses and Sole Traders.

Creating accounts for workers

With a worker created, you now need to create an Account for them. An Account on the Paybase platform is an entity that holds balances for Customers as e-money under the regulatory licence of Paybase. This is where your workers will be paid before they can manually or automatically withdraw their balance to their bank account.

When creating accounts and transactions for your customers, you will need to generate and use a separate customer authentication token instead of your normal API token.

With the customer authentication token, use the Create Account API to create a new Account and associate it with the worker 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 workers

Finally, you should add the worker’s bank details for making withdrawals by adding a bank account for them. Provide the worker’s id in the ownerId attribute. You will need to use a customer authentication token for adding bank accounts.

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

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

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "customer/0315c31b-b316-486f-bce1-90325f49286a"
});

const result = await client.createBankAccount({
  ownerId: "customer/0315c31b-b316-486f-bce1-90325f49286a",
  accountNumber: "41406760",
  sortCode: "309457",
  countryISO: "GB",
  currencyISO: "GBP",
  billingAddress: {
    postalCode: "SE6 9YU",
    countryISO: "GB",
    houseNameNumber: "7",
    street: "Brick Lane",
    townCity: "London"
  }
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/bank-accounts",
  data = json.dumps({
    ownerId: "customer/0315c31b-b316-486f-bce1-90325f49286a",
    accountNumber: "41406760",
    sortCode: "309457",
    countryISO: "GB",
    currencyISO: "GBP",
    billingAddress: {
      postalCode: "SE6 9YU",
      countryISO: "GB",
      houseNameNumber: "7",
      street: "Brick Lane",
      townCity: "London"
    }
  }),
  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
17
18
19
20
21
22
23
24
25
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/bank-accounts",
  [
    "body" => "{
      ownerId: \"customer/0315c31b-b316-486f-bce1-90325f49286a\",
      accountNumber: \"41406760\",
      sortCode: \"309457\",
      countryISO: \"GB\",
      currencyISO: \"GBP\",
      billingAddress: {
        postalCode: \"SE6 9YU\",
        countryISO: \"GB\",
        houseNameNumber: \"7\",
        street: \"Brick Lane\",
        townCity: \"London\"
      }
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/bank-accounts"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    ownerId: "customer/0315c31b-b316-486f-bce1-90325f49286a",
    accountNumber: "41406760",
    sortCode: "309457",
    countryISO: "GB",
    currencyISO: "GBP",
    billingAddress: {
      postalCode: "SE6 9YU",
      countryISO: "GB",
      houseNameNumber: "7",
      street: "Brick Lane",
      townCity: "London"
    }
  }'

The response to the API call will be the full bank account object. As the first bank account created for the worker, it will automatically be set as their preferred payment instrument and any automated withdrawals will be made to this bank account. If a customer has multiple bank accounts, any one of them can be set as preferred from the update bank account API.

To reduce friction during onboarding, you do not necessarily need to create bank accounts for workers as part of your initial onboarding flow, and can also do this when they withdraw funds for the first time. However, if you plan to automate withdrawals you will need to create bank accounts at the onboarding stage.

Onboarding users

Users refers to the participants on your platform who will book the services of the workers created above. These will again be individuals or businesses depending on the type of platform you operate.

Similar to onboarding workers, you will need to build flows to collect the necessary information required to allow users to make payments on your platform. This will vary depending on whether you want your users to be able to pay with card or bank transfers.

Users will also be created on Paybase as Customers with the relevant role. If accepting only card payments, the Target CDD Level of the user’s role will have been set to zero. This means that you will only be required to provide the mandatory fields on the create customer API to create a user that can start making card payments. However, if you also wish to allow your users to pay in via bank transfers, Paybase will perform due diligence on the users and therefore the Target CDD Level of the role will usually be two.

In both cases, 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. The Terms will be hosted by Paybase and a URL can be found under the Assets tab on the Console.

Before you can create a user, you will need to know the following:

  • Whether the user is an Individual, Sole Trader or Incorporated Business
  • User’s Role and Target CDD Level associated with it.

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. If the role for your users has been set up to only use cards, their cdd level at creation will already be their target cdd level i.e. 0. If bank transfers are enabled, 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.

The Customers page provides further guidance on creating other types of Customers such as Incorporated Businesses and Sole Traders.

Adding cards for users

Card details should be collected from your users via Paybase Capture to ensure that sensitive card data doesn’t touch your servers and is transmitted securely to Paybase from the user’s device. Paybase Capture also automatically handles 3D Secure for you. Once the card has been added, a card id will be returned which you should store in order to use it for creating transactions.

Creating accounts for users

If your users will pay on your platform via bank transfers, you will also need to create an Account for them. Users will be able to deposit funds into this account and pay workers 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 users paying in via bank transfers, you don’t need to explicitly collect the sending bank details upfront. 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 to process any refunds to the user. You can use the retrieve customer API to get the id of the preferred bank account of the user.

Creating card payments from users to workers

Having created workers and users with cards, you can now start creating transactions from users to workers.

Creating a card payment from the user to the worker will debit the user’s card and credit the worker’s account.

You will need to generate a customer authentication token for the user and call the create inbound transaction API providing the user’s card id and the worker’s account id.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.createInboundTransaction({
  accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
  amount: "500",
  method: "card",
  purpose: "PURCHASE",
  paymentInstrumentId: "card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8"
}, { apiKey: accessToken });

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

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  data = json.dumps({
    accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    amount: "500",
    method: "card",
    purpose: "PURCHASE",
    paymentInstrumentId: "card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8"
  }),
  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
17
18
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  [
    "body" => "{
      accountId: \"account/b98774fc-3c41-43bc-bd03-13e327dcfbbb\",
      amount: \"500\",
      method: \"card\",
      purpose: \"PURCHASE\",
      paymentInstrumentId: \"card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8\"
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/inbound"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    amount: "500",
    method: "card",
    purpose: "PURCHASE",
    paymentInstrumentId: "card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8"
  }'

The response to a successful API request will be the full transaction object. The stateId of the transaction will be EFFECTED indicating that the transaction was successful.

If 3D Secure authentication is activated for transactions on your account, the transaction will be created in a PROCESSING state and the object will return the 3D Secure authentication URL in the requirements attribute. In this case, you will need to handle the 3DS transaction flow.

Creating bank transfer payments from users to workers

Bank transfer payments involve users sending funds to their account on Paybase which are then credited to the worker’s account as a separate transaction.

The first step is to get the user to make a bank transfer 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. Since you provided an account id in your API request, 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 the funds are credited on the user’s account, generate a customer authentication token for the user and create an internal transaction from the user’s account to the worker’s account. You will need to provide their respective account id’s in the fromAccountId and toAccountId attributes.

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"
  }'

The above flow can also be achieved in a single step with Paybase’s Split Payments rule. The rule allows you to create a transaction before the user sends funds, and automatically transfers the funds to the worker’s account as soon as the user’s bank transfer is received in the user’s account. The rule can be configured on your account by Paybase, and you will be given an annotation key for the rule (e.g. split).

To do this, generate a customer authentication token for the user and call the create inbound transaction API providing the user’s accountId and a targetStateId as PENDING. You will also need to provide an annotation with the amount and worker’s accountId (i.e. the final destination of the funds) as in the example below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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.createInboundTransaction({
  accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
  amount: "1000",
  method: "bankAccount",
  purpose: "PURCHASE",
  targetStateId: "PENDING",
  annotations: {
    split_1_accountId: "account/8810a798-3141-11ea-978f-2e728ce88125",
    split_1_amount: "1000"
  }
}, { apiKey: accessToken });

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

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  data = json.dumps({
    accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    amount: "1000",
    method: "bankAccount",
    purpose: "PURCHASE",
    targetStateId: "PENDING",
    annotations: {
      split_1_accountId: "account/8810a798-3141-11ea-978f-2e728ce88125",
      split_1_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
17
18
19
20
21
22
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  [
    "body" => "{
      accountId: \"account/b98774fc-3c41-43bc-bd03-13e327dcfbbb\",
      amount: \"1000\",
      method: \"bankAccount\",
      purpose: \"PURCHASE\",
      targetStateId: \"PENDING\",
      annotations: {
        split_1_accountId: \"account/8810a798-3141-11ea-978f-2e728ce88125\",
        split_1_amount: \"1000\"
      }
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/inbound"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    amount: "1000",
    method: "bankAccount",
    purpose: "PURCHASE",
    targetStateId: "PENDING",
    annotations: {
      split_1_accountId: "account/8810a798-3141-11ea-978f-2e728ce88125",
      split_1_amount: "1000"
    }
  }'

Save the transaction id returned in the response.

Now, call the Retrieve Reference API providing the transaction id from above 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: "tx/a6af0aa7-2749-4715-b652-27c49b923421"
});

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

requests.get(
  "https://api-json.sandbox.paybase.io/v1/reference/tx/a6af0aa7-2749-4715-b652-27c49b923421Params: 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/tx/a6af0aa7-2749-4715-b652-27c49b923421Params: 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/tx/a6af0aa7-2749-4715-b652-27c49b923421Params: 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. Note that this is a single use reference for this particular transaction. It is recommended that you highlight to your users the importance of sending the exact transaction amount 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.

As soon the user sends a bank transfer with the exact amount and transaction reference as generated above, an internal transaction from the user’s account to the worker’s account will automatically be created.

Charging fees on transactions

So far, you have been able to onboard your users and workers, and create transactions from users to workers. The full amount of the transaction was sent to workers, however you might want to charge your platform fees on these transactions.

Paybase’s fee rules allow you to configure how you charge fees at one or multiple stages of the payment flows. For example, you can charge your workers a fee on each payment they receive, charge users a fee on paying-in by card or bank transfer, charge workers for bank withdrawals etc. 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).

In the previous example, say you want to deduct a fee instead of sending the full amount to the worker. 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 inbound transaction API (/references/transaction/create-inbound) providing the user’s accountId and a targetStateId as PENDING. This time you will provide annotations as in the example below to indicate the amount to be sent to the worker, and also the fee that you want to deduct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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.createInboundTransaction({
  accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
  amount: "1000",
  method: "bankAccount",
  purpose: "PURCHASE",
  targetStateId: "PENDING",
  annotations: {
    split_1_accountId: "account/8810a798-3141-11ea-978f-2e728ce88125",
    split_1_amount: "1000",
    split_1_fee: "10"
  }
}, { apiKey: accessToken });

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

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  data = json.dumps({
    accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    amount: "1000",
    method: "bankAccount",
    purpose: "PURCHASE",
    targetStateId: "PENDING",
    annotations: {
      split_1_accountId: "account/8810a798-3141-11ea-978f-2e728ce88125",
      split_1_amount: "1000",
      split_1_fee: "10"
    }
  }),
  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
17
18
19
20
21
22
23
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  [
    "body" => "{
      accountId: \"account/b98774fc-3c41-43bc-bd03-13e327dcfbbb\",
      amount: \"1000\",
      method: \"bankAccount\",
      purpose: \"PURCHASE\",
      targetStateId: \"PENDING\",
      annotations: {
        split_1_accountId: \"account/8810a798-3141-11ea-978f-2e728ce88125\",
        split_1_amount: \"1000\",
        split_1_fee: \"10\"
      }
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/inbound"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    amount: "1000",
    method: "bankAccount",
    purpose: "PURCHASE",
    targetStateId: "PENDING",
    annotations: {
      split_1_accountId: "account/8810a798-3141-11ea-978f-2e728ce88125",
      split_1_amount: "1000",
      split_1_fee: "10"
    }
  }'

As in the previous example, once the incoming bank transfer arrives with the exact amount and transaction reference, an internal transfer excluding fees will be created to the worker’s account, and an adjustment on the transaction will transfer the specified fees to your own account.

Making withdrawals for workers

With workers having been paid in their accounts, they need to be able to withdraw these funds to their bank account.

Generate a customer authentication token for the worker and call the create outbound transaction API providing the worker’s accountId to indicate the source account and the worker’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 worker’s bank account via Faster Payments in the UK.

Automating withdrawals for workers

You can also use Paybase’s Triggered Withdrawal rule to automate withdrawals for your workers. This will result in an outbound payment being executed automatically to the worker’s bank account as soon as they receive funds in their Paybase account from a user.

Once the rule has been configured on your account by Paybase, you don’t need to take any additional steps. Simply create a transaction from the user to the worker’s account as you normally would, and the rule will automatically create an outbound transaction to the worker’s bank account as soon as any funds are credited to their account.

Note that you may still want to consider building UI to handle manual withdrawals if automated withdrawals fail (for e.g. due to incorrect bank details, fraud rules etc.)

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.
  • Handle Refunds to make sure you can provide refunds to your users if required.
  • Uploading Documents - useful for uploading additional verification documents required when automated checks fail. This can also be done manually on the Console.