Skip to main content

Integration Guide for Platform Users

This integration guide is for you, if you are a Platform that connects cross-border businesses, and is interested in facilitating money movement for exports of goods, services or software. If you are a business looking to get paid out for your exports, then please refer to this Integration Guide for Direct Users. Please note that, at this stage, we are supporting platforms incorporated in India or have an Indian entity that we can onboard. Additionally, we are facilitating money movement only for exports of goods, services and software happening from India, i.e. the Indian business entity is getting paid out.

In this guide, we will first walk you through how you can add and onboard your users/customers who will be referred to as connected users. Through these steps, we will also configure their experience (payout schedule, fees etc.). Post this, we will explain how you can create receivables on behalf of your connected users, receive funds from overseas businesses or buyers, and reconcile funds received with receivables. Lastly, we will look at how your connected users will get paid out with supporting documents that ensure compliance and understand your payouts as a platform facilitating the funds flow.

1. Create and activate a connected user

The first step in setting up your connected users to receive cross-border payments is to create and activate Xflow accounts on their behalf. Your connected users do not have access to these accounts; You will take actions on their behalf through these accounts. We will follow the steps below to cover the creation and activation of a connected user.

  • Create an account for a connected user
  • Add acceptance of Terms of Service (ToS)
  • Add Know Your Business or KYB information (e.g. business legal name, business PAN card)
  • Add Know Your Customers or KYC information (e.g. PAN cards for directors, and business representatives)
  • Add a bank account for payouts to the connected user
  • Configure the experience for the connected users
  • Initiate account activation

You can perform the steps listed above over the API or use our Dashboard. Let's run through these steps.

Create an account for a connected user

You can create an account for a connected user as shown below by passing only a subset of the mandatory information required to activate the account. This will allow you to pass information about the connected user in a staged manner. Before initiating account activation, you will have to provide additional mandatory information. While some of this information is provided by using the update call on the account object, the remaining information is created separately and linked to the account object.

Create Account for a Connected User
curl -L -X POST 'https://api.xflowpay.com/v1/accounts' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Content-Type: application/json' \
-d '{
"business_details": {
"dba": "Marvelous Movies",
"email": "contact@marvelousmovies.com",
"legal_name": "Marvelous Movies India. Pvt. Ltd.",
"physical_address": {
"country": "IN"
},
"type": "company"
},
"type": "user"
}'
Create Account Response
{
"address": "deactivated",
"business_details": {
"dba": "Marvelous Movies",
"email": "contact@marvelousmovies.com",
"ids": null,
"legal_name": "Marvelous Movies India. Pvt. Ltd.",
"physical_address": {
"city": null,
"country": "IN",
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"product_description": null,
"type": "company",
"website": null
},
"created": 1666077553,
"id": "account_F0A_1666077553446_41Hgq_000",
"link": null,
"livemode": true,
"logo_id": null,
"metadata": null,
"nickname": null,
"object": "account",
"parent_account_id": "account_F0A_1651077553223_98Hgq_000",
"supporting_documentation": null,
"status": "draft",
"tos_acceptance": null,
"type": "user"
}

With the call above:

  • You have created an account of type=user. Your account on Xflow is of type=platform and is the parent account for your connected user’s account. See parameter parent_account_id on the connected user's account
  • At this point, the connected user is in status=draft. Further in the guide, you will submit this account for activation and learn about state transitions on the account object
  • The default values for account.fees has are inherited by the connected user from the platform passthrough fees
Note

Xflow does not support nor recommend cross-origin domain requests from the browser. If you make such requests from the browser, Xflow will return the CORS header 'Access-Control-Allow-Origin' missing error code. If you encounter this error, you're likely calling an API endpoint that is not intended to be called by a web frontend. Or rather, it can't be safely called by your frontend, because it requires a secret API key, and you don't want to expose that key to clients. Please fix this issue by calling this API endpoint from your backend instead.


Add acceptance of Terms of Service (ToS)

Xflow needs the connected user to accept Xflow’s ToS that authorize Xflow as a 3rd party to collect and disburse payments on your behalf. Since Xflow does not directly interact with the connected users, you will provide a link to Xflow’s ToS under your own ToS for the connected users. Once the connected user accepts your ToS that includes Xflow’s ToS, you will capture and pass the following parameters from the acceptance event to Xflow: time, ip and user_agent. This information is updated in tos_acceptance hash on the account object. Xflow's ToS link that must be incorporated into your (i.e. the Platform) ToS. We recommend you do this by embedding the below language in the ToS you have with the connected user.

Note

Your use of the [Platform name] processing service to receive export payments is subject to the Xflow Connected User Terms of Service (“Connected User ToS”) available at https://docs.xflowpay.com/connected-user-agreement/, which are hereby incorporated by reference. Please click on the link provided to read the Connected User ToS. In order to use the [Platform name] processing service, you hereby consent to and agree to be bound by the Connected User ToS. The Connected User ToS may be updated from time to time. You are expected to check the link provided above for any such updates. Your continued use of the [Platform name] processing services to receive export payments will be considered as your acceptance of the updated Connected User ToS.


Add KYB information

As part of the KYB information, you will provide Xflow with website, product_description, business_details.ids.business and one of the following business identifiers on behalf of your connected users by updating the account object.

  • GSTIN: You can pass this as a string in business_details.ids.tax_gst. Note that only for business_details.type=partnership, GSTIN is mandatory
  • Business PAN: You can pass this as a string in business_details.ids.tax. Note that for business_details.type=sole_proprietor or business_details.type=individual, you can pass the individual’s PAN number as a string
  • CIN /LLPIN: You can pass this as a string in business_details.ids.business

You also have an option to provide these identifiers as an attachment or document instead of a keyed-in string. To do this, create a file object as below with the image or document of the identity document you want to submit. Xflow accepts jpeg, pdf or png with size less than or equal to 10MB. The purpose of the document will vary by the type of document being uploaded.

  • GST (GST certificate): Set the purpose to tax_document
  • Business PAN (Business PAN card): Set the purpose to identity_document. Note that for business_details.type=sole_proprietor or business_details.type=individual, you will upload the individual’s PAN card
  • CIN/LLPIN (Certificate of Incorporation): Set the purpose to additional_verification

Once created, this file object must be updated in the supporting_documentation.id_document on the connected user's account.

For business_details.type=partnership, you will need to mandatorily provide us with the Partnership deed. This will be provided in supporting_documentation.id_document with purpose of the file being set to identity_document.

Create a File object for uploading Business PAN
curl -L -X POST 'https://api.xflowpay.com/v1/files' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-F file=@"business_pan.pdf" \
-F payload='{
"purpose": "identity_document"
}'
Create File Response
{
"created": 1675153265,
"file_name": "business_pan.pdf",
"id": "file_F0A_1675153265850_IREzx_000",
"livemode": false,
"metadata": null,
"object": "file",
"purpose": "identity_document",
"size": 493117,
"type": "pdf",
"url": "https://api.xflowpay.com/v1/files/file_F0A_1675153265850_IREzx_000/contents"
}
Update Account object with the Business PAN File identifier
curl -L -X POST 'https://api.xflowpay.com/v1/accounts/account_F0A_1666077553446_41Hgq_000' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json' \
-d '{
"supporting_documentation": {
"id_document": "file_F0A_1675153265850_IREzx_000"
}
}'

At this stage, we would like to introduce the header Xflow-Account. The header Xflow-Account indicates the connected user's account for which the call is being made. You should pass this header with the connected user identifier when taking action in the context of that specific connected user.


Add KYC information

To complete the KYC section, you need to provide PAN details for individuals who hold the following position in the business entity being onboarded: Business Directors or Designated Partners, Business Representatives and Ultimate Beneficial Owners (UBO). This is done by creating a person object.

On the person object, you can provide PAN details by providing PAN number (string) in supporting_ids.tax and name on the PAN card (string) in full_name, or by simply uploading the image of the PAN card. To provide information as an image, start by creating a file object with the image of a PAN card as explained in the Add KYB section. Once you have created the file object, create the person object and link the appropriate PAN or Passport file identifier to this person object. This linking is done through supporting_documentation.id_document on the person object. See below for an example.

Create a Person to add a Director
curl -L -X POST 'https://api.xflowpay.com/v1/persons' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json' \
-d '{
"relationship": {
"director": true
},
"supporting_documentation": {
"id_document": "file_F0A_1666078189630_vD3vd_000"
}
}'
Create Person Response
{
"created": 1666078214,
"full_name": "Jean Krats",
"id": "person_F0A_1666078214977_3Du5D_000",
"livemode": true,
"object": "person",
"relationship": {
"director": true,
"owner": false,
"representative": false
},
"status": "unverified",
"supporting_documentation": {
"id_document": "file_F0A_1666078189630_vD3vd_000"
},
"supportings_ids": null
}

The role of the person is indicated through the relationship hash on the person object. One person can have multiple relationships i.e. the same person can be a Business Director and Business Representative.


Add a bank account for the connected user

You can add an INR or USD EEFC (Exchange Earner's Foreign Currency) bank account for connected users. Your connected users will be paid out to this bank account. For adding an EEFC account, you will need to provide the bank statement as the supporting document that proves your connected user is the owner of the EEFC account (the business legal name of the connected user must be present on the supporting document that shows ownership of the account). For adding an INR bank account, you do not need to provide any supporting documents.

Payout bank accounts are modeled as an address object of category=user_payout. If you are adding an EEFC account, create a file object first for uploading the supporting document (refer to Add KYB information), and then create the payout address as shown below. For INR accounts, skip the file object creation. Xflow-Ops will activate this payout address once the account has been submitted for activation.

Create Address of category=user_payout
curl -L -X POST 'https://api.xflowpay.com/v1/addresses' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json' \
-d $'{
"bank_account": {
"global_wire": "HDFCINBBXXX",
"number": "102940182401"
},
"billing_details": {
"city": "Bangalore",
"country": "IN",
"line1": "No. 7 MG Road",
"postal_code": "560038",
"state": "Karnataka"
},
"category": "user_payout",
"currency": "USD",
"linked_id": "account_F0A_1666077553446_41Hgq_000",
"linked_object": "account",
"name": "Marvelous Movies India. Pvt. Ltd.",
"supporting_documentation": {
"id_document": "file_F0A_1666078209275_UpCMA_000"
},
"type": "bank_account"
}'
Create Address Response
{
"bank_account": {
"domestic_credit": null,
"domestic_debit": null,
"domestic_fast_credit": null,
"domestic_wire": null,
"global_wire": "HDFCINBBXXX",
"iban": null,
"last4": "2401",
"number": "102940182401"
},
"billing_details": {
"city": "Bangalore",
"country": "IN",
"line1": "No. 7 MG Road",
"line2": null,
"postal_code": "560038",
"state": "Karnataka"
},
"category": "user_payout",
"created": 1666160723,
"currency": "USD",
"id": "address_f0A_1666160723235_fJSRH_000",
"is_reusable": false,
"linked_id": "account_F0A_1666077553446_41Hgq_000",
"linked_object": "account",
"livemode": true,
"metadata": null,
"name": "Marvelous Movies India. Pvt. Ltd.",
"object": "address",
"status": "deactivated",
"supporting_documentation": {
"id_document": "file_F0A_1666078209275_UpCMA_000"
},
"type": "bank_account",
"wallet": null,
}

Configure fees for the connected users

Xflow allows you to configure fees for connected users. All fees, whether for connected users, direct users or platforms, are modeled using the FeePlan object. Let's take a closer look at the FeePlan object and how fees can be configured for connected users.

When a connected user is created, Xflow creates a FeePlan object of type=account_fees for this connected user. Let's pull up a FeePlan for a connected user. We will start by using the List endpoint.

List FeePlan of linked_id=account_identifier and type=account_fees and status=activated
curl -L -X GET 'https://api.xflowpay.com/v1/fee_plans?linked_id=account_F0A_1666077553446_41Hgq_000&type=account_fees&status=activated' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"'
List FeePlan Response
{
"data": [
{
"created": 1666077553,
"deposit": [],
"id": "fee_plan_F0A_1666077553446_a8fQ1_000",
"linked_id": "account_F0A_1666077553446_41Hgq_000",
"linked_object": "account",
"livemode": true,
"metadata": null,
"object": "fee_plan",
"payout": [
{
"destination_currency": "*",
"fixed": "0.00",
"minimum": "0.00",
"source_currency": "*",
"variable": "1.00"
},
{
"destination_currency": "*",
"fixed": "100.00",
"minimum": "0.00",
"source_currency": "AUD",
"variable": "1.00"
},
{
"destination_currency": "*",
"fixed": "100.00",
"minimum": "0.00",
"source_currency": "EUR",
"variable": "1.00"
},
{
"destination_currency": "*",
"fixed": "100.00",
"minimum": "0.00",
"source_currency": "GBP",
"variable": "1.00"
}
],
"payout_fx": [
{
"destination_currency": "*",
"reference_rate": "mid_market",
"source_currency": "*",
"variable": "1.00"
},
{
"destination_currency": "INR",
"reference_rate": "mid_market",
"source_currency": "EUR",
"variable": "0.75"
}
],
"status": "activated",
"transfer_fx": [
{
"destination_currency": "*",
"reference_rate": "mid_market",
"source_currency": "*",
"variable": "1.00",
"variable_off_hours": "2.00"
},
{
"destination_currency": "GBP",
"reference_rate": "mid_market",
"source_currency": "EUR",
"variable": "0.75",
"variable_off_hours": "2.00"
}
],
"type": "account_fees",
"validity": {
"from": 1683072939,
"to": null
}
}
],
"has_next": false,
"object": "list"
}

The above FeePlan object has the following characteristics:

  • Fees are described for payouts, FX related to payouts and FX related to transfers
  • Each of these above fees are described through a combination of source_currency and destination_currency
  • The character * is a wildcard and represents any currency supported by Xflow
  • Xflow selects a fee combination based on the most specific source_currency and destination_currency pair. If there are specific fee combinations with the same priority, then Xflow gives higher precedence to source_currency
  • Fixes fees are always specified in the receivable currecy

The fee values for the default connected user FeePlan (which Xflow creates) are equivalent to the values in the platform's FeePlan object of type=account_fees_passthrough. This behavior is to ensure that at a minimum the connected user fees are equivalent to the fees charged by Xflow (which in turn is represented by the platform's pass_through fees). The platform can always create a new FeePlan object for a connected user once the connected has been created. The platform can do this by using the Create endpoint.

Create FeePlan for a connected user
curl -L -X POST 'https://api.xflowpay.com/v1/fee_plans' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json' \
-d '{
"linked_id": "account_F0A_1666077553446_41Hgq_000",
"linked_object": "account",
"payout": [
{
"destination_currency": "*",
"fixed": "0.00",
"minimum": "0.00",
"source_currency": "*",
"variable": "1.00"
},
{
"destination_currency": "*",
"fixed": "100.00",
"minimum": "0.00",
"source_currency": "AUD",
"variable": "1.00"
}
],
"payout_fx": [
{
"destination_currency": "*",
"reference_rate": "mid_market",
"source_currency": "*",
"variable": "1.00"
},
{
"destination_currency": "INR",
"reference_rate": "mid_market",
"source_currency": "EUR",
"variable": "0.75"
}
],
"transfer_fx": [
{
"destination_currency": "*",
"reference_rate": "mid_market",
"source_currency": "*",
"variable": "1.00",
"variable_off_hours": "2.00"
},
{
"destination_currency": "GBP",
"reference_rate": "mid_market",
"source_currency": "EUR",
"variable": "0.75",
"variable_off_hours": "2.00"
}
],
"type": "account_fees"
}'

Please note the following while creating of a FeePlan for a connected user:

  • The previous FeePlan is automatically expired and validity.to field is populated
  • At a minimum, source_currency=* and destination_currency=* combinations are populated for payout, payout_fx and transfer_fx

Configure settings for the connected users

Xflow allows you to configure and personalize the experience of your connected users by making changes to the AccountSettings object.

  • Payout schedule: You can choose between daily, weekly and monthly as the cadence at which your connected users get paid out. You can set this cadence through payouts.interval on the connected user's AccountSettings object. For weekly cadence, you can set a specific day of week (e.g. get paid every Monday) through payouts.weekly_anchor, and for monthly cadence, you can choose a specific day of the month (e.g 5th day of every month) through payouts.monthly_anchor. You may choose to allow your connected users to set these configurations, or just set a uniform configuration as per your needs.
  • Default address availability: You can choose to change the default address availability for a connected user or the connected user's partner by changing the value for the address.connected_user or the address.connected_user_partner (in the platform's AccountSetting) or the address.partner (in the connected user's AccountSetting). Remember, you can also activate an address by using the Activate Address endpoint on an Account object.

Activate the connected user

Once you have completed all the steps above, you will now need to initiate the activation process by hitting the activation endpoint as shown below.

Activate Connected User
curl -L -X POST 'https://api.xflowpay.com/v1/accounts/account_F0A_1666077553446_41Hgq_000/activate' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json'

Post this, the connected user status will change to verifying. At this stage, Xflow will evaluate the information and activate (status changes to activated) or ask for additional information over email (status changes to input_required). The SLA here is 1 business day. You can subscribe to the event account.status.* to receive these updates. Learn more about setting up webhooks.

Activate addresses for the connected user

If you require VBANs for the connected user, then initiate the activation process by hitting the address activation endpoint as shown below.

Activate Connected User Address
curl -L -X POST 'https://api.xflowpay.com/v1/accounts/account_F0A_1666077553446_41Hgq_000/activate_address' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json'

Post this, the connected user address parameter will change to requested and then to processing. Once the VBANs have been allocated, the address parameter value will change to activated. Xflow will also send out the account.address.activated event once the VBANs are available.

2. Operate your connected user’s account

So now that you have created and activated an account of type=user, your connected users can now start using your platform to receive payments from their overseas customers. Your connected user’s customers are called partners on Xflow. In this section, we will explain how to:

  • Add a partner on behalf of the connected user
  • Create a receivable against the partner
  • Get a quote

Add a partner on behalf of the connected user

A partner is modeled as an account of type=partner within Xflow. Use the create endpoint to create an account of type=partner. You need to create the account on behalf of your user and so remember to pass the Xflow-Account header populated with the user’s account identifier.

Add Partner
curl -L -X POST 'https://api.xflowpay.com/v1/accounts' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json' \
-d '{
"business_details": {
"email": "contact@shoedog.com",
"legal_name": "Shoe Dog Inc.",
"physical_address": {
"city": "San Francisco",
"country": "US",
"line1": "185 Berry St",
"postal_code": "94107",
"state": "CA"
},
"type": "company"
},
"nickname": "Shoe Power",
"type": "partner"
}'

Unlike the connected user, activation of a partner is near instantaneous post creation and there is no separate call for activation required. However, given that this is still an asynchronous step, you can listen to the account.status.activated event to determine when the partner account has been activated.

Activate addresses for the partner

If you require VBANs for the partner, then initiate the activation process by hitting the address activation endpoint as shown below.

Activate Partner Address
curl -L -X POST 'https://api.xflowpay.com/v1/accounts/account_F0A_1666184956871_JsBZ2_000/activate_address' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json'

Post this, the partner address parameter will change to requested and then to processing. Once the VBANs have been allocated, the address parameter value will change to activated. Xflow will also send out the account.address.activated event once the VBANs are available.

Create a receivable against the partner

Now that we have an activated partner, let’s create a receivable. A receivable is Xflow’s wrapper around a user’s invoice and is a precursor to accepting funds. In this case, create a receivable on behalf of your connected user to accept funds from an activated partner by using the below curl snippet:

Create Receivable
curl -L -X POST 'https://api.xflowpay.com/v1/receivables' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json' \
-d '{
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"amount_maximum_reconcilable": "100.00",
"currency": "USD",
"invoice": {
"amount": "100.00",
"creation_date": "2023-01-02",
"currency": "USD",
"document": "file_F0A_1666079283600_ffoLd_000",
"due_date": "2023-03-02",
"reference_number": "INV-35/1"
},
"purpose_code": "P0104",
"transaction_type": "services"
}'
Create Receivable Response
{
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"amount_locked": "0.00",
"amount_maximum_reconcilable": "100.00",
"amount_reconcilable": "100.00",
"amount_reconciled": "0.00",
"amount_reconciled_not_settled": "0.00",
"amount_settled_payouts": "0.00",
"created": 1666079293,
"currency": "USD",
"deposit_ids_amount_locked": [],
"description": null,
"id": "receivable_f0A_1666261130393_wuH6k_000",
"invoice": {
"amount": "100.00",
"creation_date": "2023-01-02",
"currency": "USD",
"document": "file_F0A_1666079283600_ffoLd_000",
"due_date": "2023-03-02",
"reference_number": "Invoice FY 2022-23-038"
},
"livemode": true,
"metadata": null,
"object": "receivable",
"purpose_code": "P0104",
"purpose_code_description": "Receipts against export of goods not covered by the GR/PP/SOFTEX/EC copy of shipping bill etc.",
"status": "draft",
"system_message": [],
"transaction_type": "services"
}

Once you confirm a receivable, Xflow will verify and activate the receivable with an SLA of 2 hours. You can subscribe to the receivable.status.activated event to know when a receivable has been activated. For some receivables, Xflow will require additional inputs and will move your receivable to an Input Required status. You will be notified through webhook receivable.status.input_required.


Get a quote

Xflow provides the ability for you to get indicative FX rates (both reference and the marked up user rate). Use the quotes object for this.

Get Quote
curl -L -X GET 'https://api.xflowpay.com/v1/quotes?buy.amount=-100.45&buy.currency=INR&sell.amount=&sell.currency=USD&type=payout_fx' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"'

3. Receive and reconcile funds

Receive funds from the partner at the partner-level

Once the partner has been activated, Xflow creates an address of category=xflow_receive for the specific connected user-partner tuple (Note: you do not require an activated receivable to receive funds from a partner, an activated partner is sufficent) Let’s take a look at the address object in greater detail.

Retrieve Address of category=xflow_receive
{
"bank_account": {
"domestic_credit": "322271627",
"domestic_debit": null,
"domestic_fast_credit": "322271627",
"domestic_wire": "21000021",
"global_wire": "CHASUS33",
"iban": null,
"last4": "9007",
"number": "0552439007"
},
"billing_details": {
"city": null,
"country": "US",
"line1": null,
"line2": null,
"postal_code": null,
"state": null,
},
"category": "xflow_receive",
"created": 1666160723,
"currency": "USD",
"id": "address_f0A_1666160723235_fJSRH_000",
"is_reusable": false,
"linked_id": "account_F0A_1666184956871_JsBZ2_000",
"linked_object": "account",
"livemode": true,
"metadata": null,
"name": "Marvelous Movies India. Pvt. Ltd.",
"object": "address",
"status": "activated",
"supporting_documentation": null,
"type": "bank_account",
"wallet": null,
}

An address object denotes a destination (for a store of value) within Xflow. Every address object has a unique identifier which is denoted by the id parameter. Every address object stores funds of a specific currency which is denoted by the currency parameter.

Xflow configures the address of category=xflow_receive to be externally addressable by the connected user’s partners by providing several prefixes and a unique number (also known as a Virtual Bank Account Number or VBAN) within the bank_account hash:

  • A number that denotes the unique bank account number into which funds can be transferred
  • Prefixes for different types of payment methods over bank rails. These prefixes are denoted by domestic_credit, domestic_debit, domestic_fast_credit, domestic_wire and global_wire

So, if your connected user wants to receive funds from their partner using SWIFT, they can use the prefix global_wire + number. On the other hand, if they want to receive funds using domestic wires (e.g. Fedwire in the United States), they can use the prefix domestic_wire and number. And of course, if the connected user wants to receive funds using local ACH, they can use the prefix domestic_credit and number.

Xflow supports a number of currencies which we've listed below. Xflow will continue to add more currencies in the near-future. Please write-in to us at support@xflowpay.com if you'd like us to add support for a currency that we do not yet support.

Select any option

Remember: category=xflow_receive addresses are created such that they are unique to every connected user-partner tuple to facilitate easy reconciliation, and hence extra caution should be taken by you and the connected user while sharing bank transfer instructions with partners. If you share partner A’s bank transfer instructions with partner B, the funds received from partner A and B will start commingling creating reconciliation issues. If you do make an error and receive inwards from partner B in partner A’s address, reach out to Xflow-Ops at support@xflowpay.com for help.

When a partner sends funds, a deposit object is created (see below). You should subscribe to the deposit.status.completed event to know when a connected user has received a deposit.

Deposit Object for incoming funds
{
"amount": "20.00",
"created": 1666079759,
"currency": "USD",
"from": {
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"address_id": null
},
"id": "deposit_f0A_1666079759165_P3FtR_000",
"livemode": true,
"metadata": null,
"object": "deposit",
"payment_method": "domestic_credit",
"payment_method_details": null,
"reason_code": null,
"statement_descriptor": "Online Payment for Invoice FY 2022-23-038",
"status": "completed",
"to": {
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"address_id": "address_f0A_1666160723235_fJSRH_000"
}
}

The funds sent by the partner are reflected in your partner's pending balance. See the balance object below for the specific partner's account id.

Balance Object
{
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"available": [],
"fee_advance": [],
"livemode": true,
"object": "balance",
"payout_processing": [],
"pending": [
{
"amount": "100.00",
"currency": "AED"
},
{
"amount": "900.00",
"currency": "AUD"
},
{
"amount": "200.00",
"currency": "CHF"
},
{
"amount": "10.00",
"currency": "CZK"
},
{
"amount": "10.00",
"currency": "DKK"
},
{
"amount": "100.00",
"currency": "EUR"
},
{
"amount": "200.00",
"currency": "GBP"
},
{
"amount": "120.00",
"currency": "HKD"
},
{
"amount": "100.00",
"currency": "NOK"
},
{
"amount": "600.00",
"currency": "PLN"
},
{
"amount": "100.00",
"currency": "SEK"
},
{
"amount": "500.00",
"currency": "SGD"
},
{
"amount": "200.00",
"currency": "USD"
}
],
"processing": []
}

Receive funds from the partner at the user-level

Xflow also provides the capability to receive funds from a partner into an address of category=xflow_receive for the specific connected user. This address is created once the user has been activated. In this case, the funds sent by the partner are reflected in your user's pending balance. See the balance object below for the specific user's account id.

Balance Object
{
"account_id": "account_F0A_1666077553446_41Hgq_000",
"available": [
{
"amount": "500.00",
"currency": "AED"
},
{
"amount": "9000.00",
"currency": "AUD"
},
{
"amount": "2000.00",
"currency": "CHF"
},
{
"amount": "410.00",
"currency": "CZK"
},
{
"amount": "5610.00",
"currency": "DKK"
},
{
"amount": "100.00",
"currency": "EUR"
},
{
"amount": "200.00",
"currency": "GBP"
},
{
"amount": "120.00",
"currency": "HKD"
},
{
"amount": "500.00",
"currency": "NOK"
},
{
"amount": "600.00",
"currency": "PLN"
},
{
"amount": "100.00",
"currency": "SEK"
},
{
"amount": "500.00",
"currency": "SGD"
},
{
"amount": "3400.00",
"currency": "USD"
}
],
"fee_advance": [],
"livemode": true,
"object": "balance",
"payout_processing": [
{
"amount": "0.00",
"currency": "AED"
},
{
"amount": "0.00",
"currency": "AUD"
},
{
"amount": "0.00",
"currency": "CHF"
},
{
"amount": "0.00",
"currency": "CZK"
},
{
"amount": "0.00",
"currency": "DKK"
},
{
"amount": "0.00",
"currency": "EUR"
},
{
"amount": "0.00",
"currency": "GBP"
},
{
"amount": "120.00",
"currency": "HKD"
},
{
"amount": "500.00",
"currency": "NOK"
},
{
"amount": "650.00",
"currency": "PLN"
},
{
"amount": "100.00",
"currency": "SEK"
},
{
"amount": "500.00",
"currency": "SGD"
},
{
"amount": "300.00",
"currency": "USD"
}
],
"pending": [
{
"amount": "100.00",
"currency": "AED"
},
{
"amount": "900.00",
"currency": "AUD"
},
{
"amount": "200.00",
"currency": "CHF"
},
{
"amount": "10.00",
"currency": "CZK"
},
{
"amount": "10.00",
"currency": "DKK"
},
{
"amount": "100.00",
"currency": "EUR"
},
{
"amount": "200.00",
"currency": "GBP"
},
{
"amount": "120.00",
"currency": "HKD"
},
{
"amount": "100.00",
"currency": "NOK"
},
{
"amount": "600.00",
"currency": "PLN"
},
{
"amount": "100.00",
"currency": "SEK"
},
{
"amount": "500.00",
"currency": "SGD"
},
{
"amount": "200.00",
"currency": "USD"
}
],
"processing": [
{
"amount": "500.00",
"currency": "AED"
},
{
"amount": "200.00",
"currency": "AUD"
},
{
"amount": "200.00",
"currency": "CHF"
},
{
"amount": "10.00",
"currency": "CZK"
},
{
"amount": "10.00",
"currency": "DKK"
},
{
"amount": "100.00",
"currency": "EUR"
},
{
"amount": "200.00",
"currency": "GBP"
},
{
"amount": "120.00",
"currency": "HKD"
},
{
"amount": "100.00",
"currency": "NOK"
},
{
"amount": "600.00",
"currency": "PLN"
},
{
"amount": "100.00",
"currency": "SEK"
},
{
"amount": "500.00",
"currency": "SGD"
},
{
"amount": "300.00",
"currency": "USD"
}
]
}

Reconcile the receivable

The funds that have been transferred to the connected user’s address of category=xflow_receive now need to be reconciled against a receivable before they become eligible for payout to the connected user. Reconciliation is easy and can be triggered using the reconcile endpoint.

Reconcile Receivable
curl -L -X POST 'https://api.xflowpay.com/v1/receivables/receivable_f0A_1666261130393_wuH6k_000/reconcile' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json' \
-d '{
"amount": "10.00"
}'

Once you reconcile a receivable, you will notice that:

  • Pending balance is decremented by the amount you reconciled the receivable with. The funds from balance.pending at a partner or user-level move to balance.available at a user-level, and immediately after, these funds are moved to balance.payout_processing at a user-level once a payout has been initiated (covered in the next section). Once a payout has been settled, the balance.payout_processing at a user-level is decremented by an amount equal to what has been paid out
  • The field amount_reconcilable on the receivable is decremented by the amount you reconciled the receivable with. This will allow you to keep a track of receivable level pending amounts. See more fields on the Receivable object to understand complete Receivable level accounting.

4. Transfer funds (within Xflow)

Across partners of the same connected user

Xflow enables a platform to transfer funds across partner accounts belonging to the same connected user. This is possible via the creation of a transfer object. Please note that transfers may not complete instantaneously. The transfer.status.completed is emitted by Xflow when a transfer reaches the terminal state completed.

Create a Transfer
curl -L -X POST https://api.xflowpay.com/v1/transfers \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json' \
-d '{
"from": {
"account_id": "account_F0A_1666079163481_A7fje_000",
"amount": "123.40",
"currency": "USD"
},
"to": {
"account_id": "account_f0A_1646922461210_p5A23_000",
"currency": "USD"
},
"type": "internal_transfer"
}'

The above request transfers USD$ 123.40 across two partner accounts.


From a connected user to partner (or vice versa)

It's simple to send money between a connected user and partner. This is possible via the creation of a transfer object (which looks very similar to the request above). Remember, transfers are instantaneous and complete immediately.

Create a Transfer
curl -L -X POST https://api.xflowpay.com/v1/transfers \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Content-Type: application/json' \
-d '{
"from": {
"account_id": "account_F0A_1666077553446_41Hgq_000",
"amount": "123.40",
"currency": "USD"
},
"to": {
"account_id": "account_f0A_1646922461210_p5A23_000",
"currency": "USD"
},
"type": "internal_transfer"
}'

The above request transfers USD$ 123.40 from a connected user to a partner account.


Aross partners of different users

Transferring funds across partners belonging to different connected users is a 3-step process (i) First, transfer funds partner 1 to connected user 1 (ii) Next, transfer funds from connected user 1 to platform (iii) Next, transfer funds from platform to connected user 2 and (iv) Finally, transfer funds from connected user 2 to partner 2. Below is an example of this 4-step process where we move USD$ 123.40 from Partner 1 -> Connected User 1 -> Platform -> Connected User 2 -> Partner 2 . Note the use of the different values of the transfer.type parameter.

Create a Transfer: Move funds from Partner 1 to Connected User 1
curl -L -X POST https://api.xflowpay.com/v1/transfers \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID_1}}"' \
-H 'Content-Type: application/json' \
-d '{
"from": {
"account_id": "account_F0A_1666079163481_A7fje_000",
"amount": "123.40",
"currency": "USD"
},
"to": {
"account_id": "account_F0A_1666077553446_41Hgq_000",
"currency": "USD"
},
"type": "internal_transfer"
}'
Create a Transfer: Move funds from Connected User 1 to Platform
curl -L -X POST https://api.xflowpay.com/v1/transfers \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID_1}}"' \
-H 'Content-Type: application/json' \
-d '{
"from": {
"account_id": "account_F0A_1666077553446_41Hgq_000",
"amount": "123.40",
"currency": "USD"
},
"to": {
"account_id": "account_F0A_1651077553223_98Hgq_000",
"currency": "USD"
},
"type": "platform_credit"
}'
Create a Transfer: Move funds from Platform to Connected User 2
curl -L -X POST https://api.xflowpay.com/v1/transfers \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID_2}}"' \
-H 'Content-Type: application/json' \
-d '{
"from": {
"account_id": "account_F0A_1651077553223_98Hgq_000",
"amount": "123.40",
"currency": "USD"
},
"to": {
"account_id": "account_F0A_1651077598123_aB4r3_000",
"currency": "USD"
},
"type": "platform_debit"
}'
Create a Transfer: Move funds from Connected User 2 to Partner 2
curl -L -X POST https://api.xflowpay.com/v1/transfers \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID_2}}"' \
-H 'Content-Type: application/json' \
-d '{
"from": {
"account_id": "account_F0A_1651077598123_aB4r3_000",
"amount": "123.40",
"currency": "USD"
},
"to": {
"account_id": "account_F0A_1651077599983_cp2qO_000",
"currency": "USD"
},
"type": "internal_transfer"
}'

5. Payouts to the connected user and platform

Payouts to the connected user

As explained above, once reconciliation is complete, Xflow will initiate a payout to the connected user. These payouts can be initiated (i) in INR or USD and (ii) daily, weekly or monthly. INR payouts will be settled to the connected user’s bank account via NEFT and USD payouts will be settled via a SWIFT wire (to a USD EEFC account). Here’s an example of a USD payout that has been settled to the connected user (Xflow will of course send a corresponding payout.status.settled event webhook).

Payout to the Connected User
{
"amount": "4.50",
"arrival_date": 1675143082,
"automatic": true,
"created": 1666160776,
"currency": "USD",
"id": "payout_f0A_1666160776922_Q1hjW_000",
"livemode": true,
"metadata": null,
"object": "payout",
"payment_method": "global_wire",
"payment_method_details": null,
"payout_confirmation": null,
"statement_descriptor": "XFLOW PAYOUT F0A-1666160776922-J2uic-000",
"status": "settled",
"to": {
"account_id": "account_F0A_1666077553446_41Hgq_000",
"address_id": "address_f0A_1666160723235_fJSRH_000"
}
}

Once a payout has been initialized, Xflow will provide an ETA through the arrival_date parameter which will indicate when Xflow expects the funds to reach your connected user’s bank account. The ETA takes into account the payout schedule set on the connected user’s account.

For every INR payout, Xflow will provide a payment advice that will capture details like the amount, purpose code associated with the payout, partner name and address and the ultimate beneficiary name and bank account details. The payment advice will be available 1 business day after the payout has been settled to the connected user. Xflow will make this payment advice available to you by updating the payout.payment_confirmation parameter and sending you the payout.payment_confirmation.updated event. We recommend that you make this payment advice available to your connected user within the platform experience. Please note that there is no payment advice available for payouts made to a USD EEFC account.

Your connected users will likely want to understand their payouts better, more specifically, which receivables funded this particular payout? To explain a payout, you will need to unpack the payout object. This is done by starting with payments of type = payout, and using linked_payments to keep going upstream in the following sequence: payout --> funds_debit (gives Receivable ID) --> reconcile (gives reconcile events). The initial unpacking has been provided below.

List Payment of type=payout
curl -L -X GET 'https://api.xflowpay.com/v1/payments?linked_id=payout_f0A_1666160776922_Q1hjW_000' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"'
List Payment of type=payout Response
{
"created": 1666160776,
"fee_plan_id": "fee_plan_F0A_1666077553446_a8sn4_000",
"from": {
"amount": "4.50",
"currency": "USD"
},
"id": "payment_f0A_1666160776922_DkBou_000",
"is_exchange_rate_applicable": false,
"linked_id": "payout_f0A_1666160776922_Q1hjW_000",
"linked_object": "payout",
"linked_payments": [
{
"account_id": "account_F0A_1666077553446_41Hgq_000",
"payment_id": "payment_f0A_1666160779999_ABCou_000",
"payment_type": "funds_debit"
}
],
"livemode": true,
"object": "payment",
"payout_eligible_at": null,
"to": {
"amount": "4.50",
"currency": "INR"
},
"type": "payout"
}

Once you have retrieved the payments object of type=payout, you will need to look at the linked_payments hash which represents upstream money movements. This hash will tell you three things about the upstream payments object (i) its unique identifier (ii) the account identifier with which it is associated and (iii) the type. Once you know the unique identifier, you can retrieve this upstream payment object by using a GET call (remember to use the appropriate account identifier in the header as required).

Retrieve a linked Payment
curl -L -X GET https://api.xflowpay.com/v1/payments/payment_f0A_1666160779999_ABCou_000 \
-H 'Xflow-Account: "{{Connected_User_Account_ID}}"' \
-H 'Authorization: Bearer sk_your_key'

If your connected user’s bank account can't receive a payout for any reason, the bank sends the funds back to Xflow. Consequently, the associated payout object will move to the failed status. Xflow will ensure that the same amount of funds is returned in case of failure. You will need to reach out to support@xflowpay.com to retry a failed Payout. Also, subsequent payouts will be on hold status for this particular connected user until you reach out to Xflow.


Payouts to the platform

You (the platform) will receive payouts from Xflow in INR or USD if you have added your markup over and above the Xflow pass-through fees. To understand how payouts to the platform work, let’s assume that you have added this markup. Every time your connected user gets a payout, Xflow will deposit your share of the fee (the markup over pass-through fees) into your pending balance. This will accumulate as per the payout schedule that you have set for yourself. You can retrieve your pending balance at any time to see your fees collected. One business day after your payout schedule, Xflow initiates a payout to your INR or USD EEFC bank account. To move these funds, Xflow creates a receivable on your behalf and attaches a payment statement as an invoice to this receivable. This receipt will list out total fees collected from your connected users, Xflow’s deduction (pass-through fees), and your net fees which are being paid out to you. You will also receive a payment advice for INR payouts.

If you would like to programmatically understand the composition of your payouts, you need to look at the transfer object (see below). Xflow maintains a single transfer object of type=platform_fee_payout for a payout cycle during which there is a single payout. During the cycle, the transfer object is in processing state. At the end of payout cycle, the transfer object is moved to completed state, and funds are moved to an Xflow-generated receivable and from there onwards to a payout object.

Retrieve Transfer Response
{
"created": 1645104826,
"description": "Monthly platform fees ",
"from": {
"account_id": null,
"amount": "5.50",
"currency": "USD"
},
"id": "transfer_f0A_1912224718366_laE3R_000",
"livemode": false,
"metadata": null,
"object": "transfer",
"status": "completed",
"to": {
"account_id": "account_F0A_1669103553223_AbC1e_000",
"amount": "5.50",
"currency": "USD"
},
"type": "platform_fee_payout",
"user_declaration": null
}

A transfer object is funded via fees collected by the platform from the connected users. A transfer object is debited for fees collected by Xflow from the platform. We will now explain these fee movements below to help you understand and keep a track of the ledger of payments.

  • Retrieve the current transfer object using a list call with the filters type=platform_fee_payout and status=processing. Remember there is only 1 transfer object per payout cycle. Now that you have the identifier of the transfer object, let’s proceed to unpack this object
  • The transfer is funded via payments of type=transfer which take funds away from the connected user payout and move them to the platform account to model platform fees for payout and FX markup. These underlying payments can be identified using the linked_id parameter pointing to the transfer object being examined. The payments of type=transfer themselves are linked upstream to connected user payments of type=payout_fee (for payout fees) or type=payout (for FX fees)
  • Xflow will deduct fees from the platform by creating payments of type=payout_fee and type=fx_fee. Both fees are linked to upstream payments of type=transfer and are associated with the transfer object being examined

Using the above, you could build your ledger of payments which can model a) payout fees collected from the connected user, b) FX markup collected from the connected user, c) payout fees deducted by Xflow, d) FX markup deducted by Xflow, and e) Platform net earnings. All of these components are brought together by a single transfer object.

6. Platform pre-funding

Xflow supports the use case where a platform is charging its connected user lower fees than what the platform's own passthrough fees are. Xflow enables this by allowing a platform to pre-fund a special address of type xflow_fee_advance. The platform can send funds into this address and if there is a shortfall in the fees owed to Xflow, then Xflow will auto-deduct the balance available in the balance.fee_advance component of the platform. Please note that in the situation where there isn't sufficient balance available in the balance.fee_advance component of the platform, Xflow will pause subsequent payouts to that specific connected user until sufficient funds have been provided in the address of type xflow_fee_advance.

7. Testmode

You can use the testmode to simulate payments to test your integration. In testmode, You will be able to mimic livemode without actually moving real money. The advantage here is that if your integration works in testmode, you can have confidence that livemode will work once you decide to flip the switch. Please note that data from the testmode will not be carried over to the livemode. To make your testmode more productive, we provide additional testmode specific features:

  • User activation: You can add dummy data for creating a connected user. There will be no verification checks for fields like business identifiers and your connected user will be activated instantaneously
  • Partner activation: All partners created will be activated instantaneously
  • Simulating a Deposit: You will be able to simulate payment from a partner to a connected user. You can do this via the API or from the Partner Details page within the context of a connected user. You will see a button Create a Deposit which will instantaneously add funds to the pending balance of the connected user
  • Receivable activation: Receivables once confirmed will immediately auto-transition to activated status
  • Payouts: The initialized payout will auto-transition to settled status (i) immediately if account.payouts.interval=daily (ii) in 5 minutes if account.payouts.interval=weekly or (iii) in 15 minutes if account.payouts.interval=monthly
  • Payouts to the Platform: The transfer object which holds the platform fees will auto-transition to completed status (i) immediately if account.payouts.interval=daily (ii) in 5 minutes if account.payouts.interval=weekly or (iii) in 15 minutes if account.payouts.interval=monthly. Next, a platform receivable will be created by Xflow, reconciled from the transfer object funds and post this, be picked up by a payout 5 minutes after reconciliation. The initialized payout will auto-transition to settled status immediately
  • Platforms can pre-fund balance by creating a deposit in the xflow_fee_advance address to test the platform pre-funding use case
  • Payouts where fees are higher than the actual payout amount and transfers which are negative (due to fees being higher than the incoming funds) will be checked for incoming funds and updated every 5 minutes for 3 hours and subsequently once a week

8. API Versioning

Xflow supports API versioning for breaking changes! This means that you can safely continue to use whatever API version you are on (the API version itself is identified as the moniker yyyy-mm-dd) until you are ready to upgrade. The API docs you are looking at are versioned as well, you can identify which version you are on by looking at the dropdown on the top navigation bar. If you are looking to upgrade your API version, please see details here.




Integration Guide for Direct Users

You can use Xflow to reliably, compliantly and easily accept payments from your customers (called partners in Xflow) outside India and get paid out into your INR or EEFC bank account. Xflow supports payment acceptance in USD and models end-to-end money movement through a set of simple and easy-to-understand REST APIs.

This integration guide will show you how to create a partner, accept funds using your partner-specific bank accounts or PaymentLinks or partner Portal and then get paid out to your bank account. We also cover the basics of logging into the Xflow Dashboard, activating your account and creating livemode keys. We will finally show you how to use testmode, which is a simulation of the real-world using which you can test your integration.

1. Dashboard login

You will get an invite email from Xflow with the details of how to login to your account. Once you login you will reach the of Xflow Dashboard which will be in testmode. The home page of your Xflow dashboard will look something like this.

Dashboard:Homepage

In testmode you can explore the Xflow Dashboard and the REST APIs and create test transactions to understand how Xflow works. You can find more details in the testmode section below. If you are looking to test the REST APIs, you can get your testmode key by clicking the Developers section on the Dashboard (see highlight in the image above).

You will also see an option to Activate your Account on the Dashboard (see highlight in the image above). By clicking on the Activate button and completing this section, you will get livemode access to Xflow capabilities. Using livemode, you can create live transactions and receive money from your partners. To activate your account you will need the following pieces of information - Business Information (like your Business Legal Name, Type of Business), Business Identifiers & Documents (like your CIN or LLP ID, Business PAN) and Bank Account Information (for you to receive money).

Once you provide the above information, Xflow will verify and then activate your account. Once your account is activated, you can create a livemode key in the Developers section on the Dashboard.

2. Create a partner

Before you start collecting funds, you need to define on Xflow who you want to collect money from, i.e. your customer. Xflow models your customer as an Account of type=partner. You can create a partner on the Xflow Dashboard by using the Add Partner option or by using the API. To successfully create a partner, you will need to provide the email address, physical address, account type and a unique nickname. Let's take an example of a partner called Shoe Dog Inc., which is based out of the United States and wants to send funds to you via local payment rails like ACH credit, Fedwire or Real-Time Payments (RTP). The first step is to create an account for Shoe Dog Inc. using the request below.

Create a Partner
curl -L -X POST 'https://api.xflowpay.com/v1/accounts' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Content-Type: application/json' \
-d '{
"business_details": {
"email": "contact@shoedog.com",
"legal_name": "Shoe Dog Inc.",
"physical_address": {
"city": "San Francisco",
"country": "US",
"line1": "185 Berry St",
"postal_code": "94107",
"state": "CA"
},
"type": "company"
},
"nickname": "Shoe Power",
"type": "partner"
}'

Once a partner is created, you can visit the Partner Details page on the Dashboard from the Partner section, which looks something like this.

Dashboard:PartnerDetailsPage

After partner creation, Xflow will verify and activate this partner. Once this is done, Xflow will create a Virtual Bank Account Numbers (VBAN) in USD which will be exclusively assigned to Shoe Dog Inc. VBANs are modeled as an address within Xflow. You can use these VBANs (or addresses) to receive payments from Shoe Dog Inc. created specifically to enable you to receive funds from your partners have category=xflow_receive.

Since these VBANs are exclusive to Shoe Dog Inc., you can easily reconcile funds in these VBANs. Remember: VBANs are unique within and across partners, i.e. each currency for a partner has a unique VBAN. Hence, please ensure that the VBAN shared with a partner is the one for the currency in which they are making the payment. You can pull up VBAN details associated with a partner using the List API for the address object.

List Address
curl -L -X GET 'https://api.xflowpay.com/v1/addresses?account_id=account_F0A_1666184956871_JsBZ2_000&category=xflow_receive' \
-H 'Authorization: Bearer sk_your_key'

Here's what the response looks like.

List Address Response
{
"data": [
{
"bank_account": {
"domestic_credit": "028000024",
"domestic_debit": null,
"domestic_fast_credit": "028000024",
"domestic_wire": "021000021",
"global_wire": "CHASUS33XXX",
"iban": null,
"last4": "8492",
"number": "194248495038492"
},
"billing_details": null,
"category": "xflow_receive",
"created": 1666079069,
"currency": "USD",
"id": "address_f0A_1666184962045_5J7h9_000",
"is_reusable": false,
"linked_id": "account_F0A_1666184956871_JsBZ2_000",
"linked_object": "account",
"livemode": false,
"metadata": null,
"name": null,
"object": "address",
"status": "activated",
"type": "bank_account"
}
],
"has_next": false,
"object": "list"
}

The above response gives you all the VBANs associated with Shoe Dog Inc. through a list of address objects. Let's take a closer look at the bank_account hash. This hash contains several prefixes and account numbers that can combine and provide to your partner using which they can transfer funds over desired payment rails. Take a look at the possible combinations of the prefix + account number parameters mapped to the different payment methods below.

Partner CountryPayment MethodsPrefix parameter on APIAccount Number
USACH Creditdomestic_creditnumber
USRTPdomestic_fast_creditnumber
USFedwiredomestic_wirenumber
USACH Debitdomestic_debitnumber
USSWIFTglobal_wirenumber

Let's take the example of the USD VBAN to explain this further. Shoe Dog Inc. is a US-based entity and wants to send funds to you via Fedwire, which is a payment rail local to the US. All you have to do is provide the bank_account.domestic_wire prefix and the bank_account.number to Shoe Dog Inc. and they can use this information to transfer funds via Fedwire.

3. Receive funds

Now that you have created a partner, the next step is to create a receivable. A receivable models an intent expressed by you to accept funds from a partner. Typically, this intent is expressed by providing an underlying invoice for the desired inflow of funds. A receivable can be created via (i) the API (ii) the Xflow Dashboard using Create Receivable option or (iii) by sending an email to your Xflow assigned email. Let's walk through an example of creating the receivable through the API.

Every receivable must have an underlying invoice that provides proof of why funds are being accepted by the partner. The first step to creating a receivable on Xflow is to upload the underlying invoice. This is easy - use the files API to create a file on Xflow. Provide a digital copy of the invoice while creating this file.

Create a File
curl -L -X POST 'https://api.xflowpay.com/v1/files' \
-H 'Authorization: Bearer sk_your_key' \
-F 'file=@"sample_invoice.pdf"' \
-F 'payload="{
\"purpose\": \"finance_document\"
}"'

You are now ready to create the receivable. In this example, let's create one for accepting $100 from Shoe Dog Inc. Remember to use the file_id from the recently created file object which contains the underlying invoice as an input while creating this receivable.

Create a Receivable
curl -L -X POST 'https://api.xflowpay.com/v1/receivables' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Content-Type: application/json' \
-d '{
"amount_maximum_reconcilable": "100.00",
"currency": "USD",
"invoice": {
"amount": "100.00",
"creation_date": "2022-08-02",
"currency": "USD",
"document": "file_F0A_1666079283600_ffoLd_000",
"due_date": "2022-10-02",
"reference_number": "INV-35/1"
},
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"purpose_code": "P0104",
"transaction_type": "services"
}'

Notice that receivable creation requires the presence of a valid export-related purpose code to facilitate the receiving of funds from outside of India. A full list of purpose codes that Xflow supports can be found here. The most commonly used purpose codes can be found here.

In response to the create action, Xflow will return a receivable object in draft status.

Create Receivable Response
{
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"amount_locked": "0.00",
"amount_maximum_reconcilable": "100.00",
"amount_reconcilable": "100.00",
"amount_reconciled": "200.00",
"amount_reconciled_not_settled": "200.00",
"amount_settled_payouts": "0.00",
"created": 1666079293,
"currency": "USD",
"deposit_ids_amount_locked": [],
"description": "Shoe Dog Invoice Payment",
"id": "receivable_f0A_1666261130393_wuH6k_000",
"invoice": {
"amount": "100.00",
"creation_date": "2022-10-08",
"currency": "USD",
"document": "file_F0A_1666079283600_ffoLd_000",
"due_date": "2022-11-07",
"reference_number": "Invoice FY 2022-23-038"
},
"livemode": false,
"metadata": null,
"object": "receivable",
"purpose_code": "P0104",
"purpose_code_description": "Receipts against export of goods not covered by the GR/PP/SOFTEX/EC copy of shipping bill etc.",
"status": "activated",
"system_message": [],
"transaction_type": "services"
}

There are 6 amount fields on every receivable object.

  1. amount_maximum_reconcilable refers to the maximum funds that can be reconciled on this receivable. The upper limit for this value is invoice.amount but may be lower than the invoice.amount if you expect to receive funds lower than what is stated on the invoice

  2. amount_reconcilable is a calculated field and refers to the remaining amount that can be reconciled against this receivable

  3. amount_reconciled is a calculated field and refers to the amount that has been reconciled against this receivable.

  4. amount_reconciled_not_settled is a calculated field and refers to the amount that has not been paid out.

  5. amount_settled_payouts is a calculated field and refers to the amount that has been paid out to you already (more about this in the Payouts section below)

  6. amount_locked is a calculated field and refers to any amount that has been locked on this receivable. Locked amounts are not available for reconciliation. Amounts are locked when Xflow expects incoming funds against a receivable in the future, e.g. when an ACH debit is initiated via a PaymentLink.

Once you create a receivable, you can edit any of the parameters using the update endpoint. When the receivable updates are complete, you will need to finalize the receivable using the confirm endpoint.

Confirm a Receivable
curl -L -X POST 'https://api.xflowpay.com/v1/receivables/receivable_f0A_1666261130393_wuH6k_000/confirm' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Content-Type: application/json'

The receivable will now move from draft to verifying status. At this point, Xflow will confirm that the receivable details match what is present in the underlying invoice. Once this is done, the receivable will move from verifying to activated status. The SLA for the receivable to move to the activated state is 120 minutes during 8am-8pm IST on business days (if the receivable is confirmed outside business hours, the SLA for it to move to activated status is by 10am IST the next business day). Note: Receivables moving to activated status within the SLA is subject to complete & correct information being provided including a valid matching invoice. On the Dashboard, every receivable will have a receivable Details Page. It will look something like this.

Dashboard:Receivable Details Page

Once activated, the receivable is now ready to receive funds from your partner Shoe Dog Inc. There are two approaches to receiving funds (i) via partner-initiated bank transfers and (ii) via PaymentLinks. Let's look at both approaches below.

Approach #1: Receive funds via partner-initiated bank transfers

Using this approach, the partner, Shoe Dog Inc, initiates a $50 bank transfer using Fedwire. Once the funds arrive within Xflow, a deposit object is created and your pending balance on the balance object is incremented by the same amount. You can retrieve the deposit object on the API or look at the partner Details page on the Dashboard (see below). You can go to the Partner Details page by clicking on any partner from the Partner section. You can see your overall pending balance from the balance page on the Dashboard.

Deposit Object
{
"amount": "100.00",
"created": 1666079759,
"currency": "USD",
"from": {
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"address_id": "address_f0A_1666079747608_NDR0g_000"
},
"id": "deposit_f0A_1666079759165_P3FtR_000",
"livemode": false,
"metadata": null,
"object": "deposit",
"payment_method": "domestic_debit",
"reason_code": null,
"statement_descriptor": "Online Payment for Invoice FY 2022-23-038",
"status": "completed",
"to": {
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"address_id": "address_f0A_1666079171474_6jxOu_000"
}
}

At this stage, we would like to introduce you to the payments object. The payments object is a lower-level primitive which models the real-world money movement and is associated with a top-level object like a deposit. Thus every time there is money movement, Xflow will create an appropriate payment object to represent the money movement, and associate it with a top-level object. Consider the case of a deposit, which represents money moving into Xflow. This is modeled using a payment of type=funds_credit. Let's retrieve the payment object associated with the deposit above.

Retrieve a Payment
curl -L -X GET 'https://api.xflowpay.com/v1/payments?linked_id=deposit_f0A_1666079759165_P3FtR_000' \
-H 'Authorization: Bearer sk_your_key'

The payment object associated with the deposit will look like this.

Payment of type=funds_credit
{
"created": 1666079759,
"fee_plan_id": "fee_plan_F0A_1666077553446_a8sn4_000",
"from": {
"amount": "100.00",
"currency": "USD"
},
"id": "payment_f0A_1666079759165_1QdMP_000",
"is_exchange_rate_applicable": false,
"linked_id": "deposit_f0A_1666079759165_P3FtR_000",
"linked_object": "deposit",
"linked_payments": [],
"livemode": false,
"object": "payment",
"payout_eligible_at": null,
"to": {
"amount": "100.00",
"currency": "USD"
},
"type": "funds_credit"
}

Now that you are familiar with the deposit and the underlying payment object, let's move on to reconciliation. At this point, the funds paid by the partner are not mapped to any receivable, therefore you will need to first reconcile the funds against an activated receivable. Reconciling is simple - use the reconcile endpoint to carry out this action. This action will decrement your pending balance and increment the available balance on the balance object by the same amount. Remember that every time there is money movement, Xflow creates a payment object. Since the reconcile action moves funds from pending to available balance, Xflow creates a payment object of type=reconcile. This Payment is also linked to a top-level object, in this case, a receivable. Note that, you could do multiple reconciles on a receivable, and each reconcile action will create a payment object of type=reconcile.

Reconcile a Receivable
curl -L -X POST 'https://api.xflowpay.com/v1/receivables/receivable_f0A_1666261130393_wuH6k_000/reconcile' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Content-Type: application/json' \
-d '{
"amount": "100.00"
}'

Once the funds move to the available balance, they are ready to be paid out to you. While the actual payout particulars will be covered in the Payout section below, let's understand the money movement that precedes the actual payout. Every time your available balance is processed for a payout, money moves from the receivable object to the payout object. This is captured by the Payment of type=funds_debit. The payment object of type=funds_debit is linked to the payment object of type=reconcile which represents the upstream money movement. This linkage is represented by providing the Payment identifier of the reconcile object in the linked_payments array of the funds_debit object (the rule of thumb for linking is that the child object will contain the link to the parent object).

An alternative to receiving payments via bank transfer is to create PaymentLinks which allows your partners to pay for your invoices online using bank debits. This is a convenient and secure way to pay for your partners and is a far better experience for you as well. At present, PaymentLinks are available only for partners in the US and for receivables with currency=USD. You can create PaymentLinks from i) the Receivable Details page on the Dashboard or ii) using the API. Let's look at an example and see how you can create a PaymentLinks object for your previously created invoice and it's associated receivable.

Create a PaymentLink
curl -L -X POST 'https://api.xflowpay.com/v1/payment_links' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Content-Type: application/json' \
-d '{
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"receivable_ids": [
"receivable_f0A_1666261130393_wuH6k_000"
],
"type": "receivable"
}'

The response to the above call contains the PaymentLinks object in the link parameter.

PaymentLinks Object
{
"account_id": "account_F0A_1666184956871_JsBZ2_000",
"created": 1666079629,
"expires_at": 1671263628,
"id": "pl_F0A_1666079628988_OkOfM_000",
"link": "https://checkout.xflowpay.com/checkout/pl_F0A_1666079628988_OkOfM_000",
"livemode": false,
"metadata": null,
"object": "payment_link",
"receivable_ids": [
"rec_f0A_1912224718366_laE3R_000"
],
"status": "activated",
"type": "receivable"
}

Once the PaymentLink is generated, you should share this link with your partner. Please note that there is no authentication required to access PaymentLinks, and hence you should not share these links with unauthorized individuals. A single PaymentLink can be associated with a single receivable, and the link expires within 60 days. While you can create multiple active PaymentLinks for a single receivable, it's advisable to keep a single link by deactivating stray links using the deactivate endpoint for easier link management.

Note: PaymentLinks is built in collaboration with Plaid which is a world-class fintech company that has simplified bank debit experience. Your partners will likely see Plaid branding on certain pages of our checkout experience.

Similar to bank transfers, when your partner pays via PaymentLink, a deposit object and an associated payment object of type=reconcile is created. With PaymentLink, you get the additional advantage of not having to reconcile funds manually, since Xflow auto-reconciles the funds to the receivable attached to PaymentLink. Thus you will see that the available balance on the balance object is incremented by the deposit amount. Note that even for auto-reconcile, a payment object of type=reconcile is created. Funds received via PaymentLinks are immediately processed for payout and should reach you within 4 business days after the partner completes the payment. On payout, your available balance is immediately decremented, and the pending balance is incremented by the same amount. This is covered in the Payout section. Similar to the bank transfer flow, a payment object of type=funds_debit is created when the payout is processed.

Note: Instead of creating PaymentLinks for each receivable individually, you could also activate the Partner Portal, and share a single link with your partners. All your activated receivables automatically appear on the Partner Portal. Your partners can select multiple receivables and pay online using bank debits in one go. The post-payment workflow remains the same as PaymentLink. Partner Portal can be activated using the below API request or from the Partner Details page on the Dashboard.

Activate Partner Portal
curl -L -X POST 'https://api.xflowpay.com/v1/accounts/account_F0A_1666184956871_JsBZ2_000/activate_partner_portal' \
-H 'Authorization: Bearer sk_your_key' \
-H 'Content-Type: application/json'

4. Get paid out

Xflow currently supports payouts to India in INR and USD. By default, payouts are enabled to an INR bank account. If you want to be paid out in other currencies, contact support@xflowpay.com. Remember, you will require a valid EEFC account if you want Xflow to pay you out in a foreign currency. You won't receive your first payout until 7–14 days after processing your first successful payment. Subsequent payouts will reach you in T+1 business days.

INR payouts will be made from Xflow's partner bank to your bank account via NEFT. Xflow will provide you with a valid Payment Advice which will capture details like the purpose code, partner name and address and the ultimate beneficiary name and bank account details. The Payment Advice will help your bank initiate the process for closing the EDPMS entry if applicable. The narration on the NEFT will also contain a similar set of details to what is present in the Payment Advice.

Foreign currency payouts will require you to work with your bank such that they complete the final transfer to your EEFC bank account and further to your INR bank account should you so desire.

Every payout on Xflow is captured as a payout object, and the underlying money movement from Xflow to your bank account is captured by a Payment of type=payout which is linked to at least 1 Payment of type=funds_debit. Here's what the payout for the $100 receivable against Shoe Dog Inc. looks like. Once the payout has been processed, Xflow will provide an ETA through the arrival_date parameter which will indicate when Xflow expects the funds to reach your bank account.

Payout Object
{
"amount": "8210.90",
"arrival_date": null,
"automatic": true,
"created": 1666160776,
"currency": "INR",
"id": "payout_f0A_1666160776922_Q1hjW_000",
"livemode": true,
"metadata": {
"internal_reference": "ABC-1234"
},
"object": "payout",
"payment_method": "domestic_credit",
"payout_confirmation": null,
"statement_descriptor": "XFLOW PAYOUT F0A-1666160776922-J2uic-000",
"status": "processing",
"to": {
"account_id": "account_F0A_1666077553446_41Hgq_000",
"address_id": "address_f0A_1666160723235_fJSRH_000"
}
}

On the Dashboard, you can go to the Payouts section and view all your payouts. You can click on any payout, and go to the Payout Details page. The Payout Details Page looks something like this.

Dashboard:PayoutDetailsPage

Every payout object can be broken down into the source receivables and amounts, i.e. how a payout object has been funded. This is done by starting with Payment of type=payout, and using linked_payments to keep going upstream in the following sequence - payout > funds_debit (gives receivable identifier) > reconcile (gives reconcile events). The initial unpacking has been provided below.

Retrieve the Payment of type=payout associated with the payout.

Retrieve Payment of type=payout
curl -L -X GET 'https://api.xflowpay.com/v1/payments?linked_id=payout_f0A_1666160776922_Q1hjW_000' \
-H 'Authorization: Bearer sk_your_key'

The response for the above call is as follows.

Payment of type=payout
{
"created": 1666160776,
"from": {
"amount": "100.00",
"currency": "USD"
},
"id": "payment_f0A_1666160776922_DkBou_000",
"is_exchange_rate_applicable": true,
"linked_id": "payout_f0A_1666160776922_Q1hjW_000",
"linked_object": "payout",
"linked_payments": [
{
"account_id": "account_f0A_2396160776922_Q1hjW_000",
"payment_id": "payment_f0A_1666127191112_z6ou8_000",
"payment_type": "funds_debit"
}
],
"livemode": true,
"object": "payment",
"payout_eligible_at": null,
"to": {
"amount": "8210.90",
"currency": "INR"
},
"type": "payout"
}

Once you have retrieved the payment object of type=payout associated with your payout, you will need to look at linked_payments which represents upstream money movements. You can simply retrieve the linked payment object with a GET call as follows.

Retrieve Payment
curl -L -X GET https://api.xflowpay.com/v1/payments/pay_f0A_1646924718366_ljD9R_000 \
-H 'Authorization: Bearer sk_your_key'

The payment object will be of type=funds_debit which models money movement from receivable to the payout object. Thus the linked Payment for a funds_debit will be a receivable and linked_id will be the source receivable identifier. We now know the source receivable and the amount which makes up a part of the payout that you received.

If your bank account can't receive a payout for any reason, your bank sends the funds back to Xflow. Consequently, the associated payout object will move to failed status. Xflow will ensure that the exact amount of funds sent out will be what is returned to you in case of failure. At this time, reach out to the Xflow Support team by emailing support@xflowpay.com so that we can help you to retry this payout. Until you do so, the remaining payouts will be on hold status and will not be sent out of Xflow.

5. Testmode

You can use testmode to simulate payments to test your integration. In testmode, You will be able to mimic livemode without actually moving real money. The advantage here is that if your integration works in testmode, you can have confidence that livemode will work once you decide to flip the switch.

In testmode you can simulate both the offline payments flow & the online payments flow.

Offline payments flow

In this flow, your partner will deposit money into the bank account provided by you. You can use the steps described above in the integration guide to test this flow. Some testmode particulars to keep in mind:

a. You can create a deposit (testmode only feature) by clicking on Create a Deposit button on the Partner Details page. This step is required to simulate incoming funds from a partner.

b. Receivables once confirmed will auto-transition to activated status in 5 minutes

c. Receivables once reconciled will be picked up by a payout 5 minutes after reconciliation

d. The initialized payout will auto-transition to settled status (i) immediately if account.payouts.interval=daily (ii) in 5 minutes if account.payouts.interval=weekly or (iii) in 15 minutes if account.payouts.interval=monthly

Online payments flow

In this flow, your partner pays via the PaymentLink that you share with them. Steps to create and use the PaymentLinks have been described in the integration guide. Once you have generated a PaymentLink, within testmode, you can simulate your partner's online payment experience, and observe the changes to your Dashboard or key objects like receivables, balances, and payouts.

a. On the Receivable Details page, local the PaymentLinks widget, and click on Create button. On the next screen, click on the Preview Link button to open the PaymentLinks and view your partner's experience.

b. Here you will see an option to Add a bank account. Once you click this - you will see a selection menu of 2 bank accounts 1) Success and 2) Failure. Choose the account marked as Success and click on the Pay button

c. You will receive an email saying your partner has made a payment. On the partner Detail page, you will see an entry under the Payment history with type=Bank Debit, and on the Receivable Details page, you will see an entry in the Transaction history table of type=Auto Reconciled.

d. The failure bank account has been provided for simulating the failed scenario. When you choose this type of bank account, none of the above changes take place. We inform your partner about the payment failure and ask them to retry.

Note: The data from testmode does not carry into livemode. Once you get access to livemode, you can always flip to testmode by using the testmode specific key in case you need to test a feature.


Footnotes
  1. The most commonly used export-related purpose codes are provided in the table below:
Purpose CodesDescriptionCategory
P0102Realisation of export bills (in respect of goods) sent on collection (full invoice value)goods
P0103Advance receipts against export contracts, which will be covered later by GR/PP/SOFTEX/SDFgoods
P0104Receipts against export of goods not covered by the GR/PP/SOFTEX/EC copy of shipping bill etc.goods
P0105Export bills (in respect of goods) sent on collection.goods
P0807Off site Software Exportsgoods
P0801Hardware consultancy/implementationservices
P0803Data base, data processing chargesservices
P0808Telecommunication services including electronic mail services and voice mail servicesservices
P0902Receipts for use, through licensing arrangements, of produced originals or prototypes (such as manuscripts and films)services
P1004Legal servicesservices
P1005Accounting, auditing, book keeping and tax consulting servicesservices
P1006Business and management consultancy and public relations servicesservices
P1008Research and Development servicesservices
P1014Engineering Servicesservices
P1016Market research and public opinion polling serviceservices
P1022Other Technical Services including scientific/space servicesservices
P0802Software consultancy/implementation (other than those covered in SOFTEX form)services
P0804Repair and maintenance of computer and softwaresoftware
P0806Other information services- Subscription to newspapers, periodicals, etc.software
P0807Off site Software Exportssoftware

Webhooks

Xflow uses webhooks to notify the platform or the direct user when an event happens in your account. You can register up to 16 webhook endpoints with Xflow. All platform integrations should establish a webhook endpoint to listen for the platform and connected user events. There are two different types of webhooks:

  • Platform webhooks (platform=true on the WebhookEndpoint object) are for activity on your account (for example, most requests made using your API keys and without authenticating as another Xflow account)
  • Non-Platform webhooks (platform=false on the WebhookEndpoint object) are for activity on any connected user account

Please note that livemode events are received only on livemode webhook endpoints and testmode events are received only on testmode webhook endpoints (for both platform and connected user accounts).

Here are some things to keep in mind as you setup webhooks on Xflow.

1. Event types

Your webhook endpoints should be configured to receive only the types of events required by your integration. This can be done by specifying the event types to listen to in the enabled_events parameter while creating or updating a webhook endpoint. Listening for extra events (or all events) will put undue strain on your server and is not recommended.

2. Retry logic

In livemode and testmode, Xflow attempts to deliver your webhooks for up to 1 day with an exponential back off. If your endpoint has been disabled or deleted when we attempt a retry, future retries of that event will be prevented. However, if you disable and then re-enable a webhook endpoint before we’re able to retry, you should still expect to see future retry attempts. Webhooks cannot be manually retried.

3. Disable logic

In livemode and testmode, Xflow will disable a misconfigured endpoint if that endpoint has not responded with a 2xx HTTP status code for multiple days in a row. You can always reactivate a disabled endpoint.

4. Handle duplicate events

Webhook endpoints might occasionally receive the same event more than once. We advise you to guard against duplicated event receipts by making your event processing idempotent. One way of doing this is logging the events you’ve processed (Xflow passes back the event identifier in the webhook), and then not processing already-logged events.

5. Order of events

Xflow does not guarantee delivery of events in the order in which they are generated. Your endpoint shouldn’t expect delivery of these events in this order and should handle this accordingly.

6. CSRF protection

If you’re using Rails, Django, or another web framework, your site might automatically check that every POST request contains a CSRF token. This is an important security feature that helps protect you and your users from cross-site request forgery attempts. However, this security measure might also prevent your site from processing legitimate events. If so, you might need to exempt the webhooks route from CSRF protection.

7. Source IP Addresses

In case your webhook receiving endpoint is behind a firewall or NAT, you may need to allow traffic from the below IP addresses. This is the full list of IP addresses that webhooks may originate from:

54.216.8.72
54.173.54.49
52.215.16.239
52.55.123.25
52.6.93.106
63.33.109.123
44.228.126.217
50.112.21.217
52.24.126.164
54.148.139.208

8. Receive events with an HTTPS server

To receive events, your server must be correctly configured to support HTTPS with a valid server certificate. Xflow webhooks currently support TLS v1.2 and v1.3.

9. Verify events are sent from Xflow

Each webhook call includes three headers with additional information that are used for verification:

  • Webhook-Id the unique message identifier for the webhook message. This identifier is unique across all messages, but will be the same when the same webhook is being resent (e.g. due to a previous failure)
  • Webhook-Timestamp timestamp in seconds since epoch
  • Webhook-Signature the base64 encoded list of signatures (space delimited)

Constructing the signed content

The content to sign is composed by concatenating the id, timestamp and payload, separated by the full-stop character (.). In code, it will look something like:

String toSign = String.format("%s.%s.%s", Webhook-Id, Webhook-Timestamp, body);

Where body is the raw body of the request. The signature is sensitive to any changes, so even a small change in the body will cause the signature to be completely different. This means that you should not change the body in any way before verifying. One of the common errors we've seen is users using a prettified JSON request of the body - please do not do this!

Determining the expected signature

Xflow uses an HMAC with SHA-256 to sign its webhooks. So to calculate the expected signature, you should HMAC the toSign from above using the webhook secret. For example, given the secret webhook_secret_f0A_1646921929908_lO60W_000 you will want to use webhook_secret_f0A_1646921929908_lO60W_000 as the key.

The generated signature should match one of the ones sent in the Webhook-Signature header. The Webhook-Signature header is composed of a list of space delimited signatures and their corresponding version identifiers. The signature list is most commonly of length one (though there could be any number of signatures). Make sure to remove the version prefix and delimiter (e.g. v1,) before verifying the signature.

Verify timestamp

As mentioned above, Xflow also sends the timestamp of the attempt in the Webhook-Timestamp header. You should compare this timestamp against your system timestamp and make sure it's within your tolerance in order to prevent timestamp attacks.