NAV undefined
undefined
bash php java

Introduction

Welcome! Here at Xendit, our mission is to provide payments infrastructure that helps you succeed. We help with both the money in (accepting payments) and money out (disbursing payments). Use cases range from platform business to fintech lending and eCommerce, and everything else in between.

The Xendit API is organized around REST. Our API has predictable, resource-oriented URLs, and uses HTTP response codes to indicate API errors. We use built-in HTTP features and HTTP verbs, which are understood by off-the-shelf HTTP clients. JSON is returned by all API responses, including errors.

Postman Collection

To make it easier to get familiar with our APIs, we've published a Postman Collection so that you can see examples of all of Xendit APIs in one place. See our Postman Guide to get started!

Authentication

To successfully authenticate with Xendit's API, you must append a colon and Base 64 encode the API key you find in the dashboard. For example if your API key is

xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==

First, add a colon at the end

xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==:

Finally, Base64 encode the colon appended key to get

eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==

Xendit API is organized around REST to make it cleaner and easier to understand. All our API responses return JSON. To let you explore our APIs, we provide you API keys for both the development and production environments. All requests made in the development environment will never hit the banking networks and will not cost you anything.

Before using the API, make sure that you have registered and get your account authenticated as API requests without authentication will fail. To authenticate your account, you have to include your secret API key in the request which can be accessed in Xendit Dashboard. You can manage your API keys in the Dashboard > Settings > API Keys. Your development and production keys determine your environment. Your API keys should be kept private so do not share your secret API keys.

All the API requests should be made over HTTPS instead of HTTP (all calls made over plain HTTP will fail). We also provide PHP client libraries to save you time. We’re developing more libraries and plugins in the near future and if you write your own library, we would love to hear about it. Make sure you’ve completed the authentication before using our API.

Balances

Balance is like your wallet since it will tell you how much money is available to you on Xendit. You can retrieve it to see the current balance on your Xendit cash account. Your balance is debited on any money out transaction, e.g. when performing disbursements or Xendit fees are charged. Your balance is credited when money comes into your account, e.g. invoices are paid or you deposit funds. You can assign your money into different accounts according to your business logic (eg: cash account, tax account, escrow account) and each account has its own balance that can be accessed in the dashboard.

Currently, the API only supports querying the balance for your cash account. Anything else related to the account (eg: adding new account, transferring money from accounts besides cash account, etc.) will be done at the beginning in initial configuration with our technical team.

Get balance

Endpoint: Get Invoice

GET https://api.xendit.co/balance

Get Balance allows you to retrieve the balance of your cash account. Some use cases include: deciding when you may need to withdraw funds, determining if you have funds to disburse, and if you just like to check it’s still there :P

Example Get Invoice Request

curl https://api.xendit.co/balance -X GET \
-u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==:
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $response = $xenditPHPClient->getBalance();
  print_r($response);
?>
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Response response = client.target("https://api.xendit.co/balance")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("Authorization", "Basic eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .get();

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));

Get Invoice Response

Example Get Invoice Response

{
  "balance": 1241231
}
Parameter Description
balance The balance remaining in your cash account

Credit Cards

Tokenizing Cards

Javascript Function: createToken

Xendit.card.createToken(tokenData, function (err, data) {
    if (err) {
        //Define error handling
    }

    if (data.status === 'VERIFIED') {
        // Handle success
    } else if (data.status === 'IN_REVIEW') {
        // Handle authentication (3DS)
    } else if (data.status === 'FAILED') {
        // Handle failure
    }
});

Payment details are collected using a front-end web form with Xendit.js. The minimum requirements to tokenize a card are the amount, card number, expiration month, expiration year, and CVN code.

Example tokenData object

{        
    "amount": "10000",        
    "card_number": "4000000000000002",        
    "card_exp_month": "12",        
    "card_exp_year": "2017",        
    "card_cvn": "123"        
}

Example Tokenization Response

{
    "id": "586f0ba2ab70de5d2b409e0d",
    "status": "IN_REVIEW",
    "risk_score": 29,
    "payer_authentication_url": "https://api.xendit.co/credit_card_tokens/586f0ba2ab70de5d2b409e0d/authentication_redirect?api_key=xnd_public_development_key"
}

If fraud detection is enabled, this is also the stage at which Xendit.js will send transaction information to our fraud detection systems. See fraud detection for reference on additional fields to submit to enable fraud detection.

Tokenization Request

Parameter Description
amount string (required)
The charge amount
card_number string (required)
Card number
card_exp_month string (required)
Card expiration month
card_exp_year string (required)
Card expiration year
card_cvn string (required)
Card CVN/CV2 code
xenditResponseHandler function (required)
The response handler function is called after tokenization is attempted, to receive errors and tokenization response

Tokenization Response

Parameter Description
id string (required)
The token ID. This will be used later to Charge the funds from the credit card
status string (required)
Tokenization status. See Tokenization Statuses
risk_score string (optional)
Risk score from Fraud Detection System. See Fraud Detection
payer_authentication_url string (optional)
If status is IN_REVIEW, this contains the URL to the page for users to authenticate themselves using 3DS
failure_reason string (optional)
If status is FAILED, this describes the failure. See Tokenization Failure Reasons.

Tokenization Statuses

Status Description
IN_REVIEW The customer must authenticate their identity. Xendit provides a URL which you should navigate your users to for easily performing 3DS.
VERIFIED The customer successfully authenticated their identity. Therefore, it is safe to send the token to your backend for charging.
FAILED Tokenization can fail for many reasons. See Tokenization Failure Reasons.

Tokenization Failure Reasons

Failure Reason Description
AUTHORIZATION_3DS_FAILED This status means the customer tried to authenticate using 3DS but did not successfully complete the authentication.
HIGH_RISK_TRANSACTION This status means that transaction rejected due to high fraud risk score.

Tokenization Errors

Error Code Description
API_VALIDATION_ERROR
400
Inputs are failing validation. The errors field contains details about which fields are violating validation.
INVALID_JSON_FORMAT
400
The request body is not valid JSON.
ACCOUNT_NUMBER_INVALID_ERROR
400
Credit card number is invalid.
VALIDATION_ERROR
400
Data was passed in the wrong format.
VERIFICATION_TIMEOUT_ERROR
408
The credit card network timed out when trying to tokenize the card.
TEMPORARY_SERVICE_ERROR
503
There was a problem with the credit card network, which prevents tokenization.

Fraud Detection

Javascript Function: createToken (with Fraud Detection)

Xendit.card.createToken(tokenData, fraudData, function (err, data) {
    if (err) {
        //Define error handling
    }

    if (data.status === 'VERIFIED') {
        // Handle success
    } else if (data.status === 'IN_REVIEW') {
        // Handle authentication (3DS)
    } else if (data.status === 'FAILED') {
        // Handle failure
    }
});

Example fraudData object

{        
    "billing_first_name": "Giuseppe",
    "billing_last_name": "Contini",
    "billing_street_1": "123 Elm St.",
    "billing_city": "Cambridge",
    "billing_state": "MA",
    "billing_country": "US",
    "billing_postal_code": "02139",
    "billing_email": "joe@xendit.co",
    "billing_customer_id": "joe_16171235678",
    "billing_phone_number": "+16171235678",
    "shipping_first_name": "Joe",
    "shipping_last_name": "Contini",
    "shipping_street_1": "Jl. Casablanca Raya 88",
    "shipping_city": "Jakarta Selatan",
    "shipping_state": "Jakarta",
    "shipping_country": "ID",
    "shipping_postal_code": "12870",
    "shipping_phone_number": "+6281213831234",
    "shipping_method": "JNE",
    "items" : [
       {    
         "product_sku" : "106101100105",
         "product_code" : "Lightsaber_green_02",
         "product_name" : "Electrum Lightsaber",
         "quantity" : "1",
         "unit_price" : "4000000"
       }
    ]
}

Example Response

{
    "id": "586f4a15dd75f9e1722f8488",
    "status": "VERIFIED",
    "risk_score": 29,
    "payer_authentication_url": "https://api.xendit.co/credit_card_tokens/586f4a15dd75f9e1722f8488/authentication_redirect?api_key=xnd_public_development_O4iFfuQhgLOsl8M9eeEYGzeWYNH3otV5w3Dh%2FBFj%2FmHW%2B72nCQR%2F"
}

Fraud detection is performed at the time of tokenization by including an additional fraudData object in the tokenization request.

Fraud Fields

Parameter Description
billing_first_name string (required)
Billing first name
billing_last_name string (required)
Billing last name
billing_street_1 string (required)
Billing address
billing_city string (required)
Billing city
billing_state string (required)
Billing State. Required only for US/Canada.
billing_country string (required)
Billing Country
billing_postal_code string (required)
Billing postal code. Required only for US/Canada.
billing_email string (required)
Billing email
billing_customer_id string (optional)
Billing customer ID
billing_phone_number string (optional)
Billing phone number
shipping_first_name string (optional)
Shipping first name
shipping_last_name string (optional)
Shipping last name
shipping_street_1 string (optional)
Shipping address
shipping_city string (optional)
Shipping city
shipping_state string (optional)
Shipping state. Required for U.S. and Canada
shipping_country string (optional)
Shipping country
shipping_postal_code string (optional)
Shipping postal code. Required only for US/Canada.
shipping_phone_number string (optional)
Shipping phone number
shipping_method string (optional)
Shipping method
items array (optional)
Items in the order. See Fraud Item Fields

Fraud Item Fields

Parameter Description
product_sku string (required)
Product SKU
product_code string (required)
Product code
product_name string (required)
Product name
quantity string (required)
Quantity
unit_price string (required)
Unit price

See Tokenizing Cards for the list of Tokenization Statuses, Tokenization Failure Reasons, and Tokenization Errors that can be returned when using fraud detection.

Charging Cards

Endpoint: Create Charge

POST https://api.xendit.co/credit_card_charges

Example Charge Request

curl https://api.xendit.co/credit_card_charges \
    -X POST \
    -u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==: \
    -d token_id=586f0ba2ab70de5d2b409e0d \
    -d amount=17000 \
    -d external_id=example-123
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $external_id = 'sample-external-id-1475459775872';
  $token_id = 'sample-token-id-1475459775872';
  $amount = 17000;

  $response = $xenditPHPClient->captureCreditCardPayment($external_id, $token_id, $amount);
  print_r($response);
?>

Example Charge Response

{
    "id": "5872f07cdd75f9e1722f850c",
    "external_id": "Xendit",
    "capture_amount": 17000,
    "business_id": "5785e6334d7b410667d355c4",
    "merchant_reference_code": "5785e6334d7b410667d355c4-Xendit",
    "merchant_id": "your_business",
    "created": "2017-01-09T02:07:56.401Z",
    "status": "CAPTURED",
    "eci": "05",
}

Once you have a token, that token can be used to charge a card.

Charge Request

Parameter Description
token_id string (required)
The token ID used to charge the card.
amount number (required)
The charge amount
external_id string (required)
A unique identifier of your choice

Charge Response

Parameter Description
id string (required)
ID of the charge captured.
external_id string (required)
A unique identifier of your choice
capture_amount number (required)
The charge amount
business_id string (required)
The ID of your business in Xendit.
merchant_reference_code string (required)
An ID used to reconcile transactions with the bank.
merchant_id string (required)
Your merchant ID used for processing credit cards with the bank.
created string (required)
An ISO timestamp that tracks when the charge was made
status string (required)
Status of the charge. See Charge Statuses
eci string (optional)
Status of 3DS authentication. See ECI codes
charge_type string (required)
Types of charges. See Charge types
failure_reason string (optional)
If status is FAILED, this describes the failure. See Charge Failure Reasons

Charge Statuses

Status Description
CAPTURED Charge is successful and the funds will be settled according to the settlement schedule.
FAILED Charge failed. See Charge Failure Reasons

Charge Failure Reasons

Failure Reason Description
EXPIRED_CARD The card you are trying to capture is expired. Ask your customer for a different card
CARD_DECLINED The card you are trying to capture has been declined by the bank. Ask your customer for a different card
INSUFFICIENT_BALANCE The card you are trying to capture does not have enough balance to complete the capture
STOLEN_CARD The card you are trying to capture has been marked as stolen. Ask your customer for a different card
INACTIVE_CARD The card you are trying to capture is inactive. Ask your customer for a different card

Charge Errors

Error Code Description
API_VALIDATION_ERROR
400
Inputs are failing validation. The errors field contains details about which fields are violating validation.
INVALID_JSON_FORMAT
400
The request body is not valid JSON.
TOKEN_ALREADY_USED_ERROR
400
The the token ID has already been used in a charge.
INVALID_TOKEN_ID_ERROR
400
The token ID format is invalid.
TOKEN_NOT_FOUND_ERROR
404
The token ID was not found in the system.

Charge Types

Status Description
SINGLE_USE_TOKEN Charge created with single-use token.
RECURRING Charge created with recurring capability. See Recurring Charges

ECI Codes

ECI Description
1 Incomplete authentication (MasterCard)
2 Successful authentication (MasterCard)
5 Successful authentication (Visa, AMEX, JCB, Diners Club)
6 Authentication attempted (Visa, AMEX, JCB, Diners Club)
7 Unable to Authenticate

Accept Bank Transfers

Create invoices

Endpoint: Create Invoice

POST https://api.xendit.co/v2/invoices

Before collecting money you have to first start by making invoices. These invoices are the basic building blocks for one-time charges, recurring billing, or some combination of both. You will use this API to create charges that your users will see and need to pay. Our invoicing system will collect all the necessary payment information to complete a payment successfully.

Learn more about invoices in our documentation

Create Invoice Request

Example Create Invoice Request

curl https://api.xendit.co/v2/invoices -X POST \
   -u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==: \
   -d external_id=demo_1475801962607 \
   -d payer_email=sample_email@xendit.co \
   -d description='Trip to Bali' \
   -d amount=230000
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $external_id = 'demo_1475801962607';
  $amount = 230000;
  $payer_email = 'sample_email@xendit.co';
  $description = 'Trip to Bali';

  $response = $xenditPHPClient->createInvoice($external_id, $amount, $payer_email, $description);
  print_r($response);
?>
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Entity payload = Entity.json("{  'external_id': 'invoice_123124123',  'amount': 500000,  'payer_email': 'payer@test.com',  'description': 'Invoice #123124123 for Nike shoes',  'should_exclude_taxes': false,  'should_exclude_fees': false}");
Response response = client.target("https://api.xendit.co/v2/invoices")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("Authorization", "Basic eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .post(payload);

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));
Parameter Description
external_id
required
string ID of your choice (typically the unique identifier of an invoice in your system)
payer_email
required
string Email of the end user you're charging
description
required
string Description of the invoice
amount
required
number Amount on the invoice. The minimum amount to create an invoice is 11000.
should_exclude_taxes
optional
boolean Whether to separate out taxes for this invoice. If you would like to set this up, please talk to our lovely devs
default: false
should_exclude_fees
optional
boolean Whether to exclude additional fees for this invoice. These fees may include agent fees or convenience fees you charge on top of the base price. For most customers, this is irrelevant. If you would like to set this up, please talk to our lovely devs
default: false
callback_virtual_account_id
optional
string To allow payment via Fixed Virtual Account, pass in the id field value from the response when the fixed virtual account was created. See Create Fixed Virtual Accounts

Create Invoice Response

Example Create Invoice Response

{
  "id": "579c8d61f23fa4ca35e52da4",
  "user_id": "5781d19b2e2385880609791c",
  "external_id": "invoice_123124123",
  "is_high": true,
  "status": "PENDING",
  "merchant_name": "Xendit",
  "amount": 50000,
  "billable_amount": 54000,
  "taxable_amount": 4000,
  "received_amount": 47500,
  "payer_email": "albert@xendit.co",
  "description": "This is a description",
  "invoice_url": "http://api.xendit.co/web/invoices/57b56460b65d1a6a0f0014be",
  "xendit_fee_amount": 500,
  "expiry_date": "2016-08-01T11:20:01.017Z",
  "taxes": [
    {
      "percentage": 0.1,
      "name": "VAT"
    },
    {
      "percentage": -0.02,
      "name": "PPH"
    }
  ],
  "fees": [
    {
      "percentage": 0.04,
      "xendit_user_id": "XENDIT_FEES",
      "name": "Agent Fee"
    }
  ],
  "available_banks": [
    {
      "bank_code": "BCA",
      "collection_type": "POOL",
      "bank_account_number": 1000008,
      "transfer_amount": 54000
    },
    {
      "bank_code": "MANDIRI",
      "collection_type": "UNIQUE",
      "bank_branch": "KCP Jkt Mayestik",
      "bank_account_number": "1261006593021",
      "account_holder_name": "SENDIRI DIGITAL INDO",
      "transfer_amount": 54002,
      "identity_amount": 2
    }
  ]
}
Parameter Description
id An invoice ID generated by Xendit
user_id Your Xendit Business ID
external_id An ID of your choice we append to all transactions. Often your unique ID like a phone number, email or transaction ID
is_high Should unique numbers go above or below the amount. If true, it would mean a 100.000 invoice would display a unique amount above, e.g. 100.123
status PENDING the invoice has yet to be paid
COMPLETED the invoice has successfully been paid
merchant_name The name of your company or website
amount Nominal amount for the invoice (without taxes, fees)
billable_amount Total amount for the invoice (inclusive of all taxes, fees)
taxable_amount The amount that is taxable
received_amount Amount attributable to you net of our fees. Thanks for using us :)
payer_email Email of the payer, we get this information from your API call
description Description for the invoice, we get this information from your API call
invoice_url Public URL for this invoice, it’s there in case you want to use our UI
xendit_fee_amount Xendit fees - thanks for supporting a better world of payments :)
expiry_date ISO date and time that the invoice expires. Default is 24 hours. If you'd like to change the default for your account, please contact our support team.
taxes
percentage
name
Any taxes you would like to include on your invoices
The percentage of ‘amount’ charge
Name of the tax that will display on the invoice
fees
percentage
xendit_user_id
name
Any additional fees to include on your invoices
The percentage of ‘amount’ to charge (decimal)
The entity’s Xendit user ID
The entity’s name shown on invoices/dashboard
available_banks
bank_code
collection_type

bank_branch
account_holder_name
bank_account_number
transfer_amount
identity_amount
Available payment methods as per your config
Bank code (see bank codes)
UNIQUE type is unique amounts
POOL type is nonfixed virtual account
Name of the bank branch (if unique amounts)
Name of the bank account (if unique amounts)
Bank account number for users to pay into
Amount the user should transfer
Amount to identify the payment (if unique amounts)

Create Invoice Errors

Error Code Description
API_VALIDATION_ERROR
400
Inputs are failing validation. The errors field contains details about which fields are violating validation.
INVALID_JSON_FORMAT
400
The request body is not a valid JSON format.
MINIMAL_TRANSFER_AMOUNT_ERROR
400
Could not create invoice because amount is below Rp 10000.
MERCHANT_INVOICE_BANKS_NOT_FOUND_ERROR
404
The virtual account range needs to be configured for your account. Please contact support and we will set this up for you.

Get an invoice

Endpoint: Get an Invoice

GET https://api.xendit.co/v2/invoices/{invoice_id}

Get Invoice Request

Example Get Invoice Request

curl https://api.xendit.co/v2/invoices/{invoice_id} -X GET \
    -u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==:
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $invoice_id = '587cc7b4863f2b462beb31f6';

  $response = $xenditPHPClient->getInvoice($invoice_id);
  print_r($response);
?>
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Response response = client.target("https://api.xendit.co/v2/invoices/{invoice_id}")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("Authorization", "Basic eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .get();

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));
Parameter Description
invoice_id
required
string ID of the invoice to retrieve

Get Invoice Response

Example Get Invoice Response

{
  "id": "579c8d61f23fa4ca35e52da4",
  "user_id": "5781d19b2e2385880609791c",
  "external_id": "invoice_123124123",
  "is_high": true,
  "status": "PENDING",
  "merchant_name": "Xendit",
  "amount": 50000,
  "billable_amount": 54000,
  "taxable_amount": 4000,
  "received_amount": 47500,
  "payer_email": "albert@xendit.co",
  "description": "This is a description",
  "invoice_url": "https://api.xendit.co/web/invoices/57b56460b65d1a6a0f0014be",
  "xendit_fee_amount": 500,
  "expiry_date": "2016-08-01T11:20:01.017Z",
  "taxes": [
    {
      "percentage": 0.1,
      "name": "VAT"
    },
    {
      "percentage": -0.02,
      "name": "PPH"
    }
  ],
  "fees": [
    {
      "percentage": 0.04,
      "xendit_user_id": "XENDIT_FEES",
      "name": "Agent Fee"
    }
  ],
  "available_banks": [
    {
      "bank_code": "BCA",
      "collection_type": "POOL",
      "bank_account_number": 1000008,
      "transfer_amount": 54000
    },
    {
      "bank_code": "MANDIRI",
      "collection_type": "UNIQUE",
      "bank_branch": "KCP Jkt Mayestik",
      "bank_account_number": "1261006593021",
      "account_holder_name": "SENDIRI DIGITAL INDO",
      "transfer_amount": 54002,
      "identity_amount": 2
    }
  ]
}
Parameter Description
id Unique invoice ID generated by Xendit
user_id Your Xendit Business ID
external_id Custom ID set during invoice creation. Our customers often use a phone number, email address, or transaction/order ID
is_high Should unique numbers go above or below the amount. If true, it would mean a 100.000 invoice would display a unique amount above, e.g. 100.123
status PENDING the invoice has yet to be paid
COMPLETED the invoice has successfully been paid
merchant_name The name of your company or website
amount Nominal amount for the invoice (without taxes, fees)
billable_amount Total amount for the invoice (inclusive of all taxes, fees)
taxable_amount The amount that is taxable
received_amount Amount attributable to you net of our fees. Thanks for using us :)
payer_email Email of the payer, we get this information from your API call
description Description for the invoice, we get this information from your API call
invoice_url Public URL for this invoice, it’s there in case you want to use our UI
xendit_fee_amount Xendit fees - thanks for supporting a better world of payments :)
expiry_date ISO date and time that the invoice expires. We reserve unique numbers only for a specific period of time. After that, we’ll reset with a different number. You can configure this expiry time in dashboard.
taxes
percentage
name
Any taxes you would like to include on your invoices
The percentage of ‘amount’ charge
Name of the tax that will display on the invoice
fees
percentage
xendit_user_id
name
Any additional fees to include on your invoices
The percentage of ‘amount’ to charge (decimal)
The entity’s Xendit user ID
The entity’s name shown on invoices/dashboard
available_banks
bank_code
collection_type

bank_branch
account_holder_name
bank_account_number
transfer_amount
identity_amount
Available payment methods as per your config
Code of the banks (we support over 140+)
UNIQUE type is unique amounts
POOL type is nonfixed virtual account
Name of the bank branch (if unique amounts)
Name of the bank account (if unique amounts)
Bank account number they should pay into
Amount the user should transfer
Amount to help identify the payment (if unique amounts)

Get Invoice Errors

Error Code Description
INVALID_JSON_FORMAT
400
The request body is not a valid JSON format.
TAXED_INVOICE_NOT_FOUND_ERROR
404
Could not find taxed invoice by id.

Invoice callback

Endpoint: Invoice Callback

POST https://yourcompany.com/invoice_callback_url
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Entity payload = Entity.json("{
    id: '579c8d61f23fa4ca35e52da4',
    user_id: '5781d19b2e2385880609791c',
    external_id: 'invoice_123124123',
    is_high: true,
    status: 'COMPLETED',
    merchant_name: 'Xendit',
    amount: 50000,
    billable_amount: 54000,
    taxable_amount: 4000,
    received_amount: 47500,
    payer_email: 'albert@xendit.co',
    description: 'This is a description',
    xendit_fee_amount: 500,
    expiry_date: '2016-08-01T11:20:01.017Z',
    taxes: [
        {
            percentage: 0.1,
            name: 'VAT'
        },
        {
            percentage: -0.02,
            name: 'PPH'
        }
    ],
    fees: [
        {
            percentage: 0.04,
            xendit_user_id: 'XENDIT_FEES',
            name: 'Agent Fee'
        }
    ],
    paid_amount: 54000,
    payment_method: 'POOL',
    adjusted_received_amount: 47500,
    adjusted_xendit_fee_amount: 500,
    updated: '2016-10-10T08:15:03.404Z',
    created: '2016-10-10T08:15:03.404Z'
}");
Response response = client.target("https://api.xendit.co/invoice_callback_url")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("X-CALLBACK-TOKEN", "eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .post(payload);

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));

When an invoice is paid, our systems will send a callback to the URL configured in the dashboard. For further instruction about callback please read the invoices documentation

This example is only used to show the body parameters that is sent from Xendit APIs to your callback URL. If you want to test this callback request, use the test feature in dashboard and go to Settings -> Configuration -> Accept Payments.

Invoice Callback Request

Example Invoice Callback Request

curl --include \
     --request POST \
     --header "X-CALLBACK-TOKEN: eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==" \
     --header "Content-Type: application/json" \
     --data-binary "{
    id: \"579c8d61f23fa4ca35e52da4\",
    user_id: \"5781d19b2e2385880609791c\",
    external_id: \"invoice_123124123\",
    is_high: true,
    status: \"COMPLETED\",
    merchant_name: \"Xendit\",
    amount: 50000,
    billable_amount: 54000,
    taxable_amount: 4000,
    received_amount: 47500,
    payer_email: \"albert@xendit.co\",
    description: \"This is a description\",
    xendit_fee_amount: 500,
    expiry_date: \"2016-08-01T11:20:01.017Z\",
    taxes: [
        {
            percentage: 0.1,
            name: \"VAT\"
        },
        {
            percentage: -0.02,
            name: \"PPH\"
        }
    ],
    fees: [
        {
            percentage: 0.04,
            xendit_user_id: \"XENDIT_FEES\",
            name: \"Agent Fee\"
        }
    ],
    paid_amount: 54000,
    payment_method: \"POOL\",
    adjusted_received_amount: 47500,
    adjusted_xendit_fee_amount: 500,
    updated: \"2016-10-10T08:15:03.404Z\",
    created: \"2016-10-10T08:15:03.404Z\"
}" \
'https://api.xendit.co/invoice_callback_url'
Parameter Description
id An invoice ID generated by Xendit
user_id Your Xendit Business ID
external_id An ID of your choice we append to all transactions. Often your unique ID like a phone number, email or transaction ID
is_high Should unique numbers go above or below the amount. If true, it would mean a 100.000 invoice would display a unique amount above, e.g. 100.123
status COMPLETED the invoice has successfully been paid
merchant_name The name of your company or website
amount Nominal amount for the invoice (without taxes, fees)
billable_amount Total amount for the invoice (inclusive of all taxes, fees)
taxable_amount The amount that is taxable
received_amount Amount attributable to you net of our fees. Thanks for using us :)
payer_email Email of the payer, we get this information from your API call
description Description for the invoice, we get this information from your API call
xendit_fee_amount Xendit fees - thanks for supporting a better world of payments :)
expiry_date ISO date and time that the invoice expires. We reserve unique numbers only for a specific period of time. After that, we’ll reset with a different number. You can configure this expiry time in dashboard.
taxes
percentage
name
Any taxes you would like to include on your invoices
The percentage of ‘amount’ charge
Name of the tax that will display on the invoice
fees
percentage
xendit_user_id
name
Any additional fees to include on your invoices
The percentage of ‘amount’ to charge (decimal)
The entity’s Xendit user ID
The entity’s name shown on invoices/dashboard
paid_amount Total amount paid for the invoice (inclusive of all taxes, fees)
payment_method The way the invoice is being paid
UNIQUE type is unique amounts
POOL type is nonfixed virtual account
adjusted_received_amount Amount attributable to you net of our fees. Thanks for using us :)
adjusted_xendit_fee_amount Xendit fees - thanks for supporting a better world of payments :)

Invoice Callback Errors

Note that in the case where we don't get a response from your servers, we will retry 5 times with a 10 second delay between each retry. After 5 failures, we get internal alerts that a callback has failed. Our team will then contact you to resolve the issue.

Get banks for virtual accounts

Endpoint: Get Available Banks for Virtual Accounts

GET https://api.xendit.co/available_virtual_account_banks

Example Get Banks for Virtual Accounts Request

curl https://api.xendit.co/available_virtual_account_banks -X GET \
   -u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==:
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $response = $xenditPHPClient->getVirtualAccountBanks();
  print_r($response);
?>

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Response response = client.target("https://api.xendit.co/available_virtual_account_banks")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("Authorization", "Basic eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .get();

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));

Get Banks for Virtual Accounts Response

Example Get Banks for Virtual Accounts Response

{
  "name": "Bank Central Asia (BCA)",
  "code": "BCA"
}
Parameter Description
name Full name of the bank
code Code of the bank, relevant during creation of virtual accounts

Create fixed virtual accounts

Endpoint: Create Fixed Virtual Account (FVA)

POST https://api.xendit.co/callback_virtual_accounts

Fixed virtual accounts are dedicated virtual accounts under a name you choose, e.g. 'YourCompany - Becca Salim'. You will receive a callback each time this fixed virtual account is paid. These virtual accounts will take up to 24 hours to be commissioned, and will persist until a change is made. Requests made before 4PM Jakarta time (WIB / GMT+7) will be live by 8AM the next business day. Read more about fixed virtual accounts.

Looking for your virtual accounts to be tied to a transaction rather than a user? Use our invoices API.

Create Fixed Virtual Accounts Request

Example Create Fixed Virtual Accounts Request

curl https://api.xendit.co/callback_virtual_accounts -X POST \
   -u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==: \
   -d external_id=demo_virtual_account_1475459775872 \
   -d bank_code=BCA \
   -d name='Rika Sutanto'
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $external_id = 'demo_1475459775872';
  $bank_code = 'BCA';
  $name = 'Rika Sutanto';

  $response = $xenditPHPClient->createCallbackVirtualAccount($external_id, $bank_code, $name);
  print_r($response);
?>
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Entity payload = Entity.json("{  'external_id': 'user_1212412312',  'bank_code': 'BCA',  'name': 'William Sutanto'}");
Response response = client.target("https://api.xendit.co/callback_virtual_accounts")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("Authorization", "Basic eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .post(payload);

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));
Parameter Description
external_id
required
string ID of the user in your system
bank_code
required
string Bank code of the virtual account you want to create
name
required
string Name of user/virtual account - this will be displayed as is in the UX, e.g. ATM confirmation screens
virtual_account_number
optional
string The virtual account number you want to assign. If you do not send one, one will be picked at random

Create Fixed Virtual Accounts Response

Example Create Fixed Virtual Accounts Response

{
   "owner_id":"57b4e5181473eeb61c11f9b9",
   "external_id":"demo-1475804036622",
   "bank_code":"BCA",
   "merchant_code":"02938",
   "name":"Rika Sutanto",
   "account_number":"029382548",
   "id":"57f6fbf26b9f064272622aa6"
}
Parameter Description
owner_id Your user ID
external_id An ID of your choice we append to all transactions. Often your unique ID like a phone number, email or transaction ID
bank_code Bank code for the relevant bank, e.g. BCA
merchant_code 5-digit merchant prefix to the full virtual account number
name Name for the fixed virtual account
account_number Complete virtual account number (including prefix). This is what a user will need to enter into an ATM or their Internet/mobile banking.
id Unique ID for the fixed virtual account. Can be used to create invoices linked to the FVA.

Create Fixed Virtual Accounts Errors

Error Code Description
API_VALIDATION_ERROR
400
Inputs are failing validation. The errors field contains details about which fields are violating validation.
INVALID_JSON_FORMAT
400
The request body is not a valid JSON format.
VIRTUAL_ACCOUNT_NUMBER_OUTSIDE_RANGE
400
The virtual account number you want is outside your range.
BANK_NOT_SUPPORTED_ERROR
400
The bank code is not currently supported.

Fixed virtual account callback

Endpoint: Fixed Virtual Account Callback

POST https://yourcompany.com/virtual_account_paid_callback_url
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Entity payload = Entity.json("{
    id: '57fb4e076fa3fa296b7f5a97',
    payment_id: 'demo-1476087608948_1476087303080',
    callback_virtual_account_id: '57fb4df9af86ce19778ad359',
    owner_id: '57b4e5181473eeb61c11f9b9',
    external_id: 'demo-1476087608948',
    account_number: '1547',
    bank_code: 'BCA',
    amount: 99000,
    transaction_timestamp: '2016-10-10T08:15:03.080Z',
    merchant_code: '02938',
    updated: '2016-10-10T08:15:03.404Z',
    created: '2016-10-10T08:15:03.404Z'
}");
Response response = client.target("https://api.xendit.co/virtual_account_paid_callback_url")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("X-CALLBACK-TOKEN", "eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .post(payload);

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));

When someone pays into your fixed virtual account, our callback APIs will hit your URL that you already set in dashboard. For further information about callbacks please read these docs.

This example is only used to show the body parameters that send from Xendit APIs to your callback URL and cannot be tested in here. If you want to test this callback request, use the test feature in dashboard and go to settings -> configuration -> fixed virtual account.

Fixed Virtual Account Callback Request

Example Fixed Virtual Account Callback Request

curl --include \
     --request POST \
     --header "Content-Type: application/json" \
     --header "X-CALLBACK-TOKEN: eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==" \
     --data-binary "{
    id: \"57fb4e076fa3fa296b7f5a97\",
    payment_id: \"demo-1476087608948_1476087303080\",
    callback_virtual_account_id: \"57fb4df9af86ce19778ad359\",
    owner_id: \"57b4e5181473eeb61c11f9b9\",
    external_id: \"demo-1476087608948\",
    account_number: \"1547\",
    bank_code: \"BCA\",
    amount: 99000,
    transaction_timestamp: \"2016-10-10T08:15:03.080Z\",
    merchant_code: \"02938\",
    updated: \"2016-10-10T08:15:03.404Z\",
    created: \"2016-10-10T08:15:03.404Z\"
}" \
'https://api.xendit.co/virtual_account_paid_callback_url'
Parameter Description
payment_id Our internal system’s payment ID
callback_virtual_account_id The id field value from the response when the fixed virtual account was created. See Create Fixed Virtual Accounts
owner_id Your user ID
external_id An ID of your choice we append to all transactions. Often your unique ID like a phone number, email or transaction ID
account_number This is the complete virtual account number (including the prefix). This works just like a bank account and is what a user will need to enter in their internet banking/ATM to send funds.
bank_code Bank code for the relevant bank, e.g. BCA
amount Nominal amount to transfer
merchant_code The merchant code will be the prefix for the virtual account number, e.g 01234 your_number
id ID of fixed virtual account payment

Fixed Virtual Account Callback Errors

Note that in the case where we don't get a response from your servers, we will retry 5 times with a 10 second delay between each retry. After 5 failures, we get internal alerts that a callback has failed. Our team will then contact you to resolve the issue.

Disburse Funds

Disbursements are objects to instruct Xendit to instantly send money to any bank account across Indonesia on your behalf.

Note: Idempotency can be achieved by sending a header with the key X-IDEMPOTENCY-KEY.

Create a new disbursement

Endpoint: Create Disbursement

POST https://api.xendit.co/disbursements

Create Disbursement Request

Example Create Disbursement Request

curl https://api.xendit.co/disbursements -X POST \
   -u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==: \
   -H "X-IDEMPOTENCY-KEY: unique-id-12345" \
   -d external_id=demo_1475459775872 \
   -d bank_code=BCA \
   -d account_holder_name='Bob Jones' \
   -d account_number='1231241231' \
   -d description='Reimbursement for shoes' \
   -d amount=17000
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $external_id = 'demo_1475459775872';
  $amount = 17000;
  $bank_code = 'BCA';
  $account_holder_name = 'Bob Jones';
  $account_number = '1231241231';

  $response = $xenditPHPClient->createDisbursement($external_id, $amount, $bank_code, $account_holder_name, $account_number);
  print_r($response);
?>
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Entity payload = Entity.json("{  'external_id': 'disbursement_12345',  'amount': 500000,  'bank_code': 'BCA',  'account_holder_name': 'Rizky',  'account_number': '1231241231',  'description': 'Custom description'}");
Response response = client.target("https://api.xendit.co/disbursements")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("Authorization", "Basic eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .post(payload);

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));
Header Description
X-IDEMPOTENCY-KEY
optional
string A unique key to prevent processing duplicate requests. Can be your external_id or any GUID. Must be unique across development & production environments.
Parameter Description
external_id
required
string ID of the disbursement in your system, used to reconcile disbursements after they have been completed
bank_code
required
string Code of the destination bank
account_holder_name
required
string Name of the destination bank account owner
account_number
required
string Number for destination bank account
description
required
string Description to send with the disbursement
amount
required
number Amount to disburse

Create Disbursement Response

Example Create Disbursement Response

{
  "user_id": "5785e6334d7b410667d355c4",
  "external_id": "12345",
  "amount": 1000,
  "bank_code": "BCA",
  "account_holder_name": "RAIDY WIJAYA",
  "disbursement_description": "Refunds for shoes",
  "status": "PENDING",
  "id": "57f1ce05bb1a631a65eee662"
}
Parameter Description
user_id
required
string Your Xendit Business ID
external_id
required
string Custom ID of your choice, to identify the transaction. Our customers often use a phone number, email address, or transaction/order ID
amount
required
number Amount to disburse
bank_code
required
string Destination bank code. See bank codes
account_holder_name
required
string Bank account name as per the bank's records. Used for verification and error/customer support scenarios
disbursement_description
required
This is the description you give us :)
status
required
string
PENDING Transfer is initiated but not yet completed by bank.
id
required
string Unique disbursement ID

Create Disbursement Errors

Error Code Description
API_VALIDATION_ERROR
400
Inputs are failing validation. The errors field contains details about which fields are violating validation.
INVALID_JSON_FORMAT
400
The request body is not a valid JSON format.
DISBURSEMENT_DESCRIPTION_NOT_FOUND_ERROR
400
Disbursement description is not set in the dashboard settings.
DIRECT_DISBURSEMENT_BALANCE_INSUFFICIENT_ERROR
400
Not enough balance to disburse.
DUPLICATE_TRANSACTION_ERROR
400
Request failed because idempotency key has been seen before.

Get disbursement

Endpoint: Get Disbursement

GET https://api.xendit.co/disbursements/{disbursement_id}

This endpoint queries the current status of a disbursement. This is often used for checking the status of a transaction.

Get Disbursement Request

Example Get Disbursement Request

curl https://api.xendit.co/disbursements/57c9010f5ef9e7077bcb96b6 -X GET \
  -u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==:
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $disbursement_id = '587cc7ea77535fb94bb4e8eb';

  $response = $xenditPHPClient->getDisbursement($disbursement_id);
  print_r($response);
?>
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Response response = client.target("https://api.xendit.co/disbursements/{disbursement_id}")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("Authorization", "Basic eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .get();

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));
Parameter Description
disbursement_id
required
string ID of the disbursement to retrieve

Get Disbursement Response

Example Get Disbursement Response

{
  "user_id": "5785e6334d7b410667d355c4",
  "external_id": "disbursement_12345",
  "amount": 500000,
  "bank_code": "BCA",
  "account_holder_name": "Rizky",
  "disbursement_description": "Custom description",
  "status": "PENDING",
  "id": "57c9010f5ef9e7077bcb96b6"
}
Parameter Description
user_id Your Xendit Business ID
external_id Custom ID set at disbursement creation. Our customers often use a phone number, email address, or transaction/order ID
amount Amount to disburse
bank_code Destination bank code. See bank codes
account_holder_name Bank account name as per the bank's records. Used for verification and error/customer support scenarios
disbursement_description This is the description you give us :)
status PENDING Transfer is initiated but not yet completed by bank.
COMPLETED Bank has confirmed transmission of funds.
FAILED Bank rejected disbursement. We will not retry.
id Unique disbursement ID

Get Disbursement Errors

Error Code Description
INVALID_JSON_FORMAT
400
The request body is not a valid JSON format.
INVALID_PERMISSION_ERROR
403
Could not access that disbursement.
DIRECT_DISBURSEMENT_NOT_FOUND_ERROR
404
Could not find direct disbursement.

Disbursement callback

Endpoint: Disbursement Callback

POST https://yourcompany.com/disbursement_callback_url
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Entity payload = Entity.json("{  'id': '57e214ba82b034c325e84d6e',  'user_id': '57c5aa7a36e3b6a709b6e148',  'external_id': 'disbursement_123124123',  'amount': 150000,  'bank_code': 'BCA',  'xendit_fee_amount': 0,  'xendit_fee_user_id': 'XENDIT_FEES',  'account_holder_name': 'XENDIT',  'transaction_id': '57ec8b7e906aa2606ecf8ffc',  'transaction_sequence': '1799',  'disbursement_id': '57ec8b8130d2d0243f438e11',  'disbursement_description': 'Xendit disbursement',  'failure_code': 'INVALID_DESTINATION',  'is_instant': false,  'status': 'FAILED',  'updated': '2016-10-10T08:15:03.404Z',  'created': '2016-10-10T08:15:03.404Z'}");
Response response = client.target("https://api.xendit.co/disbursement_callback_url")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .post(payload);

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));

When a disbursement transaction is successful, our callback APIs will hit your URL that you already set in dashboard. For further information about callbacks please read these docs.

This example is only used to show the body parameters that send from Xendit APIs to your callback URL. If you want to test this callback request, use the test feature in dashboard and go to Settings -> Configuration -> Disbursement.

Disbursement Callback Request

Example Disbursement Callback Request

curl --include \
     --request POST \
     --header "Content-Type: application/json" \
     --header "X-CALLBACK-TOKEN: eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==" \
     --data-binary "{
    \"id\": \"57e214ba82b034c325e84d6e\",
    \"user_id\": \"57c5aa7a36e3b6a709b6e148\",
    \"external_id\": \"disbursement_123124123\",
    \"amount\": 150000,
    \"bank_code\": \"BCA\",
    \"xendit_fee_amount\": 0,
    \"xendit_fee_user_id\": \"XENDIT_FEES\",
    \"account_holder_name\": \"XENDIT\",
    \"transaction_id\": \"57ec8b7e906aa2606ecf8ffc\",
    \"transaction_sequence\": \"1799\",
    \"disbursement_id\": \"57ec8b8130d2d0243f438e11\",
    \"disbursement_description\": \"Xendit disbursement\",
    \"failure_code\": \"INVALID_DESTINATION\",
    \"is_instant\": false,
    \"status\": \"FAILED\",
    \"updated\": \"2016-10-10T08:15:03.404Z\",
    \"created\": \"2016-10-10T08:15:03.404Z\"
}" \
'https://api.xendit.co/disbursement_callback_url'
Parameter Description
is_instant Indicates whether the disbursement is being disbursed instantly
user_id Your Xendit Business ID
external_id Custom ID set at disbursement creation. Our customers often use a phone number, email address, or transaction/order ID
amount Amount to disburse
bank_code Destination bank code. See bank codes
account_holder_name Bank account name as per the bank's records. Used for verification and error/customer support scenarios
disbursement_description This is the description you give us :)
status COMPLETED Bank has confirmed transmission of funds.
FAILED Bank rejected disbursement. We will not retry.
id Unique disbursement ID

Disbursement Callback Errors

Note that in the case where we don't get a response from your servers, we will retry 5 times with a 10 second delay between each retry. After 5 failures, we get internal alerts that a callback has failed. Our team will then contact you to resolve the issue.

Get available disbursement banks

Endpoint: Get Available Disbursement Banks

GET https://api.xendit.co/available_disbursements_banks

Example Get Available Disbursement Banks Request

curl https://api.xendit.co/available_disbursements_banks -X GET \
    -u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==:
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $response = $xenditPHPClient->getAvailableDisbursementBanks();
  print_r($response);
?>
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Response response = client.target("https://api.xendit.co/available_disbursements_banks")
  .request(MediaType.APPLICATION_JSON_TYPE)
  .header("Authorization", "Basic eG5kX2RldmVsb3BtZW50X1A0cURmT3NzME9DcGw4UnRLclJPSGphUVlOQ2s5ZE41bFNmaytSMWw5V2JlK3JTaUN3WjNqdz09Og==")
  .get();

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));

This API endpoint will provide you the current list of banks we support for disbursements. The current list of supported banks is >140 just for Indonesia, including some BPDs and BPRs.

Get Available Disbursement Banks Response

Get Available Disbursement Banks Response

[
  {
    "name": "Bank Central Asia (BCA)",
    "code": "BCA"
  },
  {
    "name": "Bank Mandiri",
    "code": "MANDIRI"
  },
  {
    "name": "Bank Rakyat Indonesia (BRI)",
    "code": "BRI"
  }
]
Parameter Description
name Full name of the bank
code Code of the bank, relevant during creation of virtual accounts

Name Validator

Endpoint: Validate Name

POST https://api.xendit.co/bank_account_data_requests

Example Name Validator Request

curl https://api.xendit.co/bank_account_data_requests \
    -X POST \
    -u xnd_development_O46JfOtygef9kMNsK+ZPGT+ZZ9b3ooF4w3Dn+R1k+2fT/7GlCAN3jg==: \
    -d bank_account_number="1550001953382" \
    -d bank_code="MANDIRI"
<?php
  require 'vendor/autoload.php';

  $options['secret_api_key'] = 'xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk+R1l9Wbe+rSiCwZ3jw==';

  $xenditPHPClient = new XenditClient\XenditPHPClient($options);

  $bank_account_number = '1550001953382';
  $bank_code = 'MANDIRI';

  $response = $xenditPHPClient->validateBankAccountHolderName($bank_account_number, $bank_code);
  print_r($response);
?>

The Name Validator can be used to look up the name of an account holder for any bank account in Indonesia. Rather than returning the result in the POST response, a Name Validator request will trigger a callback with the data. Be sure to have a callback URL configured for Bank account data in the Xendit Dashboard Configuration.

Name Validator Request

Parameter Description
bank_account_number string (required)
The bank account number
bank_code string (required)
The bank code. See Bank Codes

Name Validator Response

Name Validator Response (Uncached)

{}

If this bank account number has not been previously cached by Xendit, the response will be a blank JSON object. If the account number has been validated previously, the name will be cached and the response will instead include the contents of the callback payload.

Example Name Validator Callback Payload

{
    "bank_code":"BCA",
    "bank_account_number":"1234567899",
    "bank_name":"Bank Central Asia",
    "bank_account_holder_name":"Bob Jones",
    "status":"SUCCESS"
}

Name Validator Callback Payload

Parameter Description
bank_code string (required)
The bank code. See Bank Codes
bank_account_number string (required)
The bank account number
bank_name string (required)
The bank name. See Bank Codes
bank_account_holder_name string (required)
Account Holder Name
status string (required)
Status of the Name Validation request.

Errors

Below are some of most common errors across all our endpoints. Specific errors are located under each endpoint. If you have any questions please contact us.

Error code Meaning
400 Bad request, e.g. validation error
401 Unauthorised access, e.g. the wrong API key
403 Forbidden access, e.g. API key does not have permission for this endpoint
404 Page or data not found
500 Unhandled error - contact us when this happens

Postman Collection

Postman is a HTTP client for testing web services which makes it easy to test APIs by providing a simple interface for making API requests & viewing responses. To make integrating with our API's easier, we've created a Postman collection of our APIs for easy testing.

To use our APIs, you'll need a free Xendit Dashboard account. To follow this guide, be sure to have Postman installed.

Import Collection

  1. Download API-Xendit.postman_collection.json and save it to your computer.
  2. In postman, click the Import button in the upper left.
  3. Select the API-Xendit.postman_collection.json collection that was just saved.
  4. The Collections tab should now show the Xendit collection!

Create Authorization Header

The Xendit Collection is built to take advantage of Postman Environments, which allows for easy switching between Development and Live keys. To use Environments, you'll first need to convert your Xendit Secret Key to a Base64-encoded Authorization header. Postman provides an easy way to do this:

Example API Key

xnd_development_NIiBfOQn1+T8wJVpK+UaHTGABCPwp4EauXTo+R1k9GLW/rakCgZzgw==
  1. In Xendit Dashboard, navigate to Settings > API Keys
  2. Under Development Keys > Secret Key, click 'Copy Key'
  3. In Postman, make a new tab in and click the Authorization tab
  4. Choose Basic Auth from the Type dropdown
  5. Paste your Secret Key into the Username
  6. Click the orange Update Request button
  7. Click the Headers tab
  8. Copy the newly created value from the Authorization key

Example Authorization Header

Basic eG5kX2RldmVsb3BtZW50X05JaUJmT1FuMStUOHdKVnBLK1VhSFRHQUJDUHdwNEVhdVhUbytSMWs5R0xXL3Jha0NnWnpndz09Og==

Creating Environment

  1. In Postman on the upper right, click the gear icon > Manage Environments.
  2. Click the Add button to create a new environment.
  3. Give it a name like Xendit Testing
  4. Add Authorization and api-xendit headers (see table below)
  5. Click the Add button to save
  6. In the upper right, select your new Xendit Testing environment. Image
key value
Authorization Your Authorization header
api-xendit https://api.xendit.co

Image

Testing

If you can query your balance successfully, you've got it all set up! See our implementation docs for more testing on: