navigation hamburger icon
Trolley Logo

API Documentation

Table of Contents

curl Ruby Python Javascript PHP C# Java

Partner API Introduction

The Partner API allows support for sub-merchant accounts, connected to a parent Merchant account.

If your business is a software platform that has multiple business customers that require specific or unique settings for each, such as white-label branding in your customer’s brand, payout methods, processing settings, fee cover, or US tax form settings, our Partner API may be suitable to use to achieve this.

Create sub-merchants with the parent API key. Then authenticate later partner operations with that sub-merchant’s API key to configure payout methods, processing settings, fee cover, white-label branding, the recipient widget, and webhooks.

Sub-merchants

Create and configure sub-merchant accounts with the parent API key.

Create Sub-merchant

This endpoint is for creating a new sub-merchant. The new sub-merchant is bound to the parent merchant used to create it. This endpoint requires that the Partner API (Sub-merchant feature) be enabled for your merchant account. Contact support for more information.

Example Request

curl \
-H "Authorization: prsign ${PARENT_TROLLEY_ACCESS_KEY}:${PARENT_TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X POST 'https://api.trolley.com/v1/profile/submerchant' \
-d '{
  "merchant": {
    "name": "Acme Sandbox Merchant",
    "currency": "USD",
    "country": "US",
    "website": "https://example.com"
  },
  "onboarding": {
    "businessLegalName": "Acme Sandbox Merchant LLC",
    "businessAsName": "Acme Sandbox Merchant",
    "businessTaxId": "12-3456789",
    "businessPhone": "+14165551212",
    "businessWebsite": "https://example.com",
    "businessCategory": "business_service",
    "businessCountry": "US",
    "businessCity": "New York",
    "businessAddress": "123 Example Street",
    "businessZip": "10001",
    "businessRegion": "NY",
    "businessTotalMonthly": "10000",
    "businessPpm": "100",
    "businessIntlPercentage": "25",
    "expectedPayoutCountries": "US,CA"
  }
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->post('/v1/profile/submerchant', [
    "merchant" => [
        "name" => "Acme Sandbox Merchant",
        "currency" => "USD",
        "country" => "US",
        "website" => "https://example.com"
    ],
    "onboarding" => [
        "businessLegalName" => "Acme Sandbox Merchant LLC",
        "businessAsName" => "Acme Sandbox Merchant",
        "businessTaxId" => "12-3456789",
        "businessPhone" => "+14165551212",
        "businessWebsite" => "https://example.com",
        "businessCategory" => "business_service",
        "businessCountry" => "US",
        "businessCity" => "New York",
        "businessAddress" => "123 Example Street",
        "businessZip" => "10001",
        "businessRegion" => "NY",
        "businessTotalMonthly" => "10000",
        "businessPpm" => "100",
        "businessIntlPercentage" => "25",
        "expectedPayoutCountries" => "US,CA"
    ]
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.post("/v1/profile/submerchant", {"merchant":{"name":"Acme Sandbox Merchant","currency":"USD","country":"US","website":"https://example.com"},"onboarding":{"businessLegalName":"Acme Sandbox Merchant LLC","businessAsName":"Acme Sandbox Merchant","businessTaxId":"12-3456789","businessPhone":"+14165551212","businessWebsite":"https://example.com","businessCategory":"business_service","businessCountry":"US","businessCity":"New York","businessAddress":"123 Example Street","businessZip":"10001","businessRegion":"NY","businessTotalMonthly":"10000","businessPpm":"100","businessIntlPercentage":"25","expectedPayoutCountries":"US,CA"}});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('POST', '/v1/profile/submerchant', {"merchant" => {"name" => 'Acme Sandbox Merchant', "currency" => 'USD', "country" => 'US', "website" => 'https://example.com'}, "onboarding" => {"businessLegalName" => 'Acme Sandbox Merchant LLC', "businessAsName" => 'Acme Sandbox Merchant', "businessTaxId" => '12-3456789', "businessPhone" => '+14165551212', "businessWebsite" => 'https://example.com', "businessCategory" => 'business_service', "businessCountry" => 'US', "businessCity" => 'New York', "businessAddress" => '123 Example Street', "businessZip" => '10001', "businessRegion" => 'NY', "businessTotalMonthly" => '10000', "businessPpm" => '100', "businessIntlPercentage" => '25', "expectedPayoutCountries" => 'US,CA'}})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("POST", "/v1/profile/submerchant", {"merchant":{"name":"Acme Sandbox Merchant","currency":"USD","country":"US","website":"https://example.com"},"onboarding":{"businessLegalName":"Acme Sandbox Merchant LLC","businessAsName":"Acme Sandbox Merchant","businessTaxId":"12-3456789","businessPhone":"+14165551212","businessWebsite":"https://example.com","businessCategory":"business_service","businessCountry":"US","businessCity":"New York","businessAddress":"123 Example Street","businessZip":"10001","businessRegion":"NY","businessTotalMonthly":"10000","businessPpm":"100","businessIntlPercentage":"25","expectedPayoutCountries":"US,CA"}})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.post("/v1/profile/submerchant", "{\"merchant\":{\"name\":\"Acme Sandbox Merchant\",\"currency\":\"USD\",\"country\":\"US\",\"website\":\"https://example.com\"},\"onboarding\":{\"businessLegalName\":\"Acme Sandbox Merchant LLC\",\"businessAsName\":\"Acme Sandbox Merchant\",\"businessTaxId\":\"12-3456789\",\"businessPhone\":\"+14165551212\",\"businessWebsite\":\"https://example.com\",\"businessCategory\":\"business_service\",\"businessCountry\":\"US\",\"businessCity\":\"New York\",\"businessAddress\":\"123 Example Street\",\"businessZip\":\"10001\",\"businessRegion\":\"NY\",\"businessTotalMonthly\":\"10000\",\"businessPpm\":\"100\",\"businessIntlPercentage\":\"25\",\"expectedPayoutCountries\":\"US,CA\"}}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("POST", "/v1/profile/submerchant", @"{""merchant"":{""name"":""Acme Sandbox Merchant"",""currency"":""USD"",""country"":""US"",""website"":""https://example.com""},""onboarding"":{""businessLegalName"":""Acme Sandbox Merchant LLC"",""businessAsName"":""Acme Sandbox Merchant"",""businessTaxId"":""12-3456789"",""businessPhone"":""+14165551212"",""businessWebsite"":""https://example.com"",""businessCategory"":""business_service"",""businessCountry"":""US"",""businessCity"":""New York"",""businessAddress"":""123 Example Street"",""businessZip"":""10001"",""businessRegion"":""NY"",""businessTotalMonthly"":""10000"",""businessPpm"":""100"",""businessIntlPercentage"":""25"",""expectedPayoutCountries"":""US,CA""}}");

Console.WriteLine(response);

Request Schema

{
  // required data:
  merchant: {
    name: string; // Sub-merchants business name
    currency: string; // 3 letter currency code in ISO 4217
  };

  // also required
  onboarding: {
    businessWebsite: string;
    businessLegalName: string;
    businessAsName: string;
    businessTaxId: string;  
    businessCategory: string;
    businessCountry: string; // 2 letter country ISO 3166-1 alpha-2
    businessCity: string;
    businessAddress: string;
    businessZip: string; // optional if country has postal code
    businessRegion: string; // state or province - ideally 2 letter code
    businessTotalMonthly: string; // Expected total value of payouts per month in USD (e.g. "10000" or "500000" etc)
    businessPpm: string; // Expected number of payouts per month; (e.g. "15000" or "520000" etc).
    businessIntlPercentage: string; // The percentage (%) of payment volume that will be sent internationally; (e.g. "15" or "52" etc).
    expectedPayoutCountries: string // 2 letter country ISO 3166-1 alpha-2. Accepts multiple country input
  };
}

Response (200 Ok)

{
  "ok": true,
  "merchant": {
    "id": "M-1a2B3c4D5e6F7g8H9i0J1k",
    "accessKey": "AK-1a2B3c4D5e6F7g8H9i0J1k",
    "secretKey": "SK-1a2B3c4D5e6F7g8H9i0J1k"
  }
}

Response Schema

{
  ok: boolean;
  merchant: {
    id: string;
    accessKey: string;
    secretKey: string;
  }
}

This endpoint will return a sub-merchant id (alphanumeric guid) and an API key. The API key is important if you’re going to access the sub-merchant programmatically as this is the only time you will be able to get it - so make sure you store it in the right place.

HTTP Request

POST /v1/profile/submerchant

Fields Description
merchant.name
required
string
Name of the sub-merchant
merchant.website
required
string
Website URL
merchant.country
required
string
2 letter country code in ISO 3166-1
merchant.currency
required
string
3 letter currency code in ISO 4217
onboarding.businessLegalName
required
string
Legal name of the sub-merchant
onboarding.businessAsName
required
string
Doing business name of the sub-merchant
onboarding.businessTaxId
required
string
Tax ID of the sub-merchant
onboarding.businessPhone
required
string
Phone number
onboarding.businessWebsite
required
string
Website URL
onboarding.businessCategory
required
string
The category of the business. Expected one of the allowed values, as defined below.
onboarding.businessCountry
required
string
2 letter country code in ISO-3166-1
onboarding.businessCity
required
string
City of the sub-merchant
onboarding.businessAddress
required
string
Address of the sub-merchant
onboarding.businessZip
required
string
Postal code for the sub-merchant
onboarding.businessRegion
required
string
Region of the sub-merchant (e.g. state/province)
onboarding.businessTotalMonthly
required
string
Expected total value of payouts per month in USD (e.g. “10000” or “500000” etc).
onboarding.businessPpm
required
string
Expected number of payouts per month; (e.g. “15000” or “520000” etc).
onboarding.businessIntlPercentage
required
string
The percentage (%) of payment volume that will be sent internationally; (e.g. “15” or “52” etc).
onboarding.expectedPayoutCountries
required
string
Expected countries the sub-merchant will payout to

If any of the required “onboarding” fields are missing then onboarding will not be complete. Following are more details about accepted parameters for some of the required attributes as defined above.

Allowed Values for Business Category

Category Allowed Value
Online Market online_market
App Store app_store
Affiliate Platform affiliate_platform
Ad Network ad_network
Crowd Funding crowdfunding
Crowd Sourcing crowdsourcing
Share Economy share_economy
E-Commerce e-commerce
Charity charity
Surveys surveys
Rebates rebates
Startup startup
Publishing publishing
Entertainment entertainment
Travel travel
Education education
Manufacturing manufacturing
Business Service business_service
Influencer Platform influencer_platform
Online Gambling online_gambling
Adult Entertainment adult_entertainment
Multi Level Marketing multi_level_marketing
Firearms firearms
Money Transmitter Service money_transmitter_service
Credit Card Processing credit_card_processing
Other other

This endpoint will return a sub-merchant id (alphanumeric guid) and an API key. The API key is important if you’re going to access the sub-merchant programmatically as this is the only time you will be able to get it - so make sure you store it in the right place.

HTTP Code Description
200 Submerchant successfully created
401 Invalid API key
404 Recipient not found
500 Internal error

Errors

This table lists the expected errors that this method could return. However, other errors can be returned in the case where the service is down or other unexpected factors affect processing. Callers should always check the value of the ok params in the response.

Error Code Description
not_found Object doesn’t exist
invalid_api_key Invalid API key
internal_server_error Internal server errors

Modify Onboarding Information

With a sub-merchant’s key, you can modify onboarding information for an existing sub-merchant. You need to be using the sub-merchant’s key.

HTTP Request

POST https://api.trolley.com/v1/onboarding/update

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X POST 'https://api.trolley.com/v1/onboarding/update' \
-d '{
  "businessTotalMonthly": "10000",
  "businessPpm": "100"
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->post('/v1/onboarding/update', [
    "businessTotalMonthly" => "10000",
    "businessPpm" => "100"
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.post("/v1/onboarding/update", {"businessTotalMonthly":"10000","businessPpm":"100"});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('POST', '/v1/onboarding/update', {"businessTotalMonthly" => '10000', "businessPpm" => '100'})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("POST", "/v1/onboarding/update", {"businessTotalMonthly":"10000","businessPpm":"100"})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.post("/v1/onboarding/update", "{\"businessTotalMonthly\":\"10000\",\"businessPpm\":\"100\"}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("POST", "/v1/onboarding/update", @"{""businessTotalMonthly"":""10000"",""businessPpm"":""100""}");

Console.WriteLine(response);

Request Schema

{
businessTotalMonthly?: string; // should be a number of USD (eg: "10000" or "500000" etc..)
businessPpm?: string
}

Response (200 Ok)

{
  "ok": true
}

Response Schema

{
  ok: boolean;
}

Request

Fields Description
businessTotalMonthly
optional
string
Expected total value of payouts per month in USD (e.g. “10000” or “500000” etc), expressed as a JSON string
businessPpm
optional
string
Number of expected payments per month.
HTTP Code Description
200 Submerchant successfully created
401 Invalid API key
404 Recipient not found
500 Internal error

Errors

This table lists the expected errors that this method could return. However, other errors can be returned in the case where the service is down or other unexpected factors affect processing. Callers should always check the value of the ok params in the response.

Error Code Description
not_found Object doesn’t exist
invalid_api_key Invalid API key
internal_server_error Internal server errors

Get Funding Information

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/balances/info'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/balances/info');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/balances/info");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/balances/info')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/balances/info")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/balances/info");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/balances/info");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "info": [
    {
      "accountAddress": "100 King St W",
      "accountCity": "Toronto",
      "accountCountryCode": "CA",
      "accountCurrency": "USD",
      "accountName": "Trolley Funding Account",
      "accountNum": "*****1234",
      "accountPostalCode": "M5X1C9",
      "accountRegion": "ON",
      "bankAddress": "100 King St W",
      "bankCity": "Toronto",
      "bankCountry": "Canada",
      "bankCountryCode": "CA",
      "bankName": "Example Bank",
      "bankPostalCode": "M5X1C9",
      "bankRegion": "ON",
      "institution": "001",
      "referenceMemo": "merchant-reference-123",
      "routingNumber": "021000021"
    }
  ]
}

Response Schema

{
  info: {
    accountAddress: string;
    accountCity: string;
    accountCountryCode: string;
    accountCurrency: string;
    accountName: string;
    accountNum: string;
    accountPostalCode: string;
    accountRegion: string;
    bankAddress: string;
    bankCity: string;
    bankCountry: string;
    bankCountryCode: string;
    bankName: string;
    bankPostalCode: string;
    bankRegion: string;
    institution: string;
    // IMPORTANT: must be put in the memo field
    referenceMemo: string;
    routingNumber: string;
    // potentially other fields depending on the bank
  }[],
}

HTTP Request

GET https://api.trolley.com/v1/balances/info

Returns the bank account information needed in order to fund the sub-merchant account by bank wire or bank transfer. Note that the referenceMemo information must be included in the memo field of the bank wire or bank transfer. This is used to route the the incoming funds to the correct sub-merchant account balance.

HTTP Code Description
200 Funding information returned
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
internal_server_error Internal server errors

Sandbox Merchant

Sandbox Create

This endpoint works identically to the “create sub-merchant” endpoint but creates sandbox sub-merchants instead.

This endpoint is for creating a new sub-merchant. The new sub-merchant is bound to the parent sandbox merchant used to create it. This endpoint requires that the sub-merchant feature be enabled for your merchant.

Request

curl \
-H "Authorization: prsign ${PARENT_TROLLEY_ACCESS_KEY}:${PARENT_TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X POST 'https://api.trolley.com/v1/profile/sandbox' \
-d '{
  "apikey": true,
  "merchant": {
    "name": "Docs Sandbox Submerchant",
    "currency": "USD"
  }
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->post('/v1/profile/sandbox', [
    "apikey" => true,
    "merchant" => [
        "name" => "Docs Sandbox Submerchant",
        "currency" => "USD"
    ]
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.post("/v1/profile/sandbox", {"apikey":true,"merchant":{"name":"Docs Sandbox Submerchant","currency":"USD"}});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('POST', '/v1/profile/sandbox', {"apikey" => true, "merchant" => {"name" => 'Docs Sandbox Submerchant', "currency" => 'USD'}})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("POST", "/v1/profile/sandbox", {"apikey":True,"merchant":{"name":"Docs Sandbox Submerchant","currency":"USD"}})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.post("/v1/profile/sandbox", "{\"apikey\":true,\"merchant\":{\"name\":\"Docs Sandbox Submerchant\",\"currency\":\"USD\"}}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("POST", "/v1/profile/sandbox", @"{""apikey"":true,""merchant"":{""name"":""Docs Sandbox Submerchant"",""currency"":""USD""}}");

Console.WriteLine(response);

Request Schema

{
  // optional - true to generate API keys for the sandbox account
  apikey: boolean;
  // optional data -- copied from master merchant if not provided
  merchant: {
    name: string; // Sub-merchants business name
    currency: string; // 3 letter currency code in ISO 4217
  };
}

This endpoint will return a sub-merchant id (alphanumeric guid) and an API key. The API key is important if you’re going to access the sub-merchant programmatically as this is the only time you will be able to get it to make sure you store it in the right place.

Response (200 Ok)

{
  "ok": true,
  "merchant": {
    "id": "M-1a2B3c4D5e6F7g8H9i0J1k",
    "accessKey": "AK-1a2B3c4D5e6F7g8H9i0J1k",
    "secretKey": "SK-1a2B3c4D5e6F7g8H9i0J1k"
  }
}

Response Schema

{
  ok: boolean; // true if everything is good
  merchant: {
    id: string;
    accessKey: string; // present if apikey is provided
    secretKey: string; // present if apikey is provided
  }
}

HTTP Request

POST /v1/profile/sandbox

Fields Description
apikey
required
boolean
Value of true to generate API keys for the sandbox account
merchant.name
required
string
Sub-merchant business name
merchant.currency
required
string
3 letter currency code in ISO 4217
HTTP Code Description
200 Submerchant successfully created
401 Invalid API key
403 Invalid parameter
500 Internal error

Errors

Error Code Description
invalid_field Invalid field value
invalid_api_key Invalid API key
internal_server_error Internal server errors

Sandbox Delete

To delete a sandbox sub merchant that you’ve created you need to provide the id of the sub-merchant to delete.

DELETE /v1/profile/sandbox

Request

curl \
-H "Authorization: prsign ${PARENT_TROLLEY_ACCESS_KEY}:${PARENT_TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X DELETE 'https://api.trolley.com/v1/profile/sandbox' \
-d '{
  "id": "M-1a2B3c4D5e6F7g8H9i0J1k"
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->delete('/v1/profile/sandbox', null, [
    "id" => "M-1a2B3c4D5e6F7g8H9i0J1k"
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.remove("/v1/profile/sandbox");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('DELETE', '/v1/profile/sandbox', {"id" => 'M-1a2B3c4D5e6F7g8H9i0J1k'})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("DELETE", "/v1/profile/sandbox", {"id":"M-1a2B3c4D5e6F7g8H9i0J1k"})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.delete("/v1/profile/sandbox", "{\"id\":\"M-1a2B3c4D5e6F7g8H9i0J1k\"}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("DELETE", "/v1/profile/sandbox", @"{""id"":""M-1a2B3c4D5e6F7g8H9i0J1k""}");

Console.WriteLine(response);

Request Schema

{
  // required -- the sandbox id to delete
  id: string;
}

Response (200 Ok)

{
  "ok": true
}

Response Schema

{
  ok: boolean;
}
Fields Description
id
required
string
Id of sandbox sub-merchant to delete
HTTP Code Description
200 Submerchant successfully created
403 Invalid parameter
401 Invalid API key
500 Internal error

Errors

This table lists the expected errors that this method could return. However, other errors can be returned in the case where the service is down or other unexpected factors affect processing. Callers should always check the value of the ok params in the response.

Error Code Description
not_found Object doesn’t exist
invalid_api_key Invalid API key
internal_server_error Internal server errors
invalid_field

Payout Methods

Enable and configure payout methods on a sub-merchant.

List Payout Methods

Authenticate with the sub-merchant API key. This endpoint returns the payout methods available on that sub-merchant: bank-transfer, paypal, check, venmo, debit-card, and mobile-wallet. There is no paging; the array contains at most those six records. The payload still includes a meta object with page, pages, and records.

HTTP Request

GET https://api.trolley.com/v1/payout-methods

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/payout-methods'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/payout-methods');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/payout-methods");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/payout-methods')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/payout-methods")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/payout-methods");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/payout-methods");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "payoutMethods": [
    {
      "integration": "bank-transfer",
      "settings": {}
    }
  ],
  "meta": {
    "page": 1,
    "pages": 1,
    "records": 6
  }
}

Response Schema

{
  ok: boolean;
  payoutMethods: {
    integration: string;
    enabled: boolean;
    status: string; // `pending` or `approved`; meaningful for bank-transfer only
    suspended: boolean;
    approvedAt: string | null;
    updatedAt: string;
    enabledCountries?: string[];
    settings: object;
  }[];
  meta: {
    page: number;
    pages: number;
    records: number;
  };
}

Each item uses the same top-level fields as Retrieve a Payout Method. status is pending or approved, and is only meaningful for bank transfer — every other method is approved. Method-specific fields are inside settings.

HTTP Code Description
200 Payout methods returned
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
internal_server_error Internal server errors

Retrieve a Payout Method

Authenticate with the sub-merchant API key. :payoutMethod is one of bank-transfer, paypal, check, venmo, debit-card, or mobile-wallet.

HTTP Request

GET https://api.trolley.com/v1/payout-methods/:payoutMethod

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/payout-methods/bank-transfer'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/payout-methods/bank-transfer');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/payout-methods/bank-transfer");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/payout-methods/bank-transfer')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/payout-methods/bank-transfer")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/payout-methods/bank-transfer");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/payout-methods/bank-transfer");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "payoutMethod": {
    "integration": "bank-transfer",
    "settings": {}
  }
}

Response Schema

{
  ok: boolean;
  payoutMethod: {
    integration: string;
    enabled: boolean;
    status: string;
    suspended: boolean;
    approvedAt: string | null;
    updatedAt: string;
    enabledCountries?: string[];
    settings: object;
  }
}

Credentials and bank account numbers are never returned, not even masked. Use settings.credentialsConfigured or settings.bankAccountConfigured to see whether they are set. You can GET a payout method, change one field on the payoutMethod object, and PATCH that object back without overwriting stored secrets.

Fields Description
payoutMethod
required
string
Payout method slug. Allowed values are bank-transfer, paypal, check, venmo, debit-card, and mobile-wallet
HTTP Code Description
200 Payout method returned
400 Invalid payout method
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_field Invalid payout method
invalid_api_key Invalid API key
internal_server_error Internal server errors

Update a Payout Method

Authenticate with the sub-merchant API key. Send only the fields you are changing. :payoutMethod is one of bank-transfer, paypal, check, venmo, debit-card, or mobile-wallet.

HTTP Request

PATCH https://api.trolley.com/v1/payout-methods/:payoutMethod

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X PATCH 'https://api.trolley.com/v1/payout-methods/bank-transfer' \
-d '{
  "enabledCountries": ["US", "CA"]
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->patch('/v1/payout-methods/bank-transfer', [
    "enabledCountries" => ["US", "CA"]
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.patch("/v1/payout-methods/bank-transfer", {"enabledCountries":["US","CA"]});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('PATCH', '/v1/payout-methods/bank-transfer', {"enabledCountries" => ['US', 'CA']})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("PATCH", "/v1/payout-methods/bank-transfer", {"enabledCountries":["US","CA"]})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.patch("/v1/payout-methods/bank-transfer", "{\"enabledCountries\":[\"US\",\"CA\"]}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("PATCH", "/v1/payout-methods/bank-transfer", @"{""enabledCountries"":[""US"",""CA""]}");

Console.WriteLine(response);

Request Schema

{
  enabled?: boolean;
  enabledCountries?: string[];
  mode?: string;
  account?: string;
  clientId?: string;
  secret?: string;
  webhookID?: string;
  allowRestrictedCountries?: boolean;
  branchId?: string;
  accountNum?: string;
  signatory?: string;
  startingCheck?: string;
  verification1?: string;
  verification2?: string;
  mailing?: object;
  supportedNetworks?: string[];
  enabledMobileWalletPayerIds?: number[];
}

Response (200 Ok)

{
  "ok": true,
  "payoutMethod": {
    "integration": "bank-transfer",
    "enabled": true,
    "status": "approved",
    "suspended": false,
    "updatedAt": "2026-08-10T09:00:00.000Z",
    "enabledCountries": ["US", "CA"],
    "settings": {}
  }
}

Each payout method accepts a different subset of body fields. A field that belongs to a different payout method is rejected with 400. A misspelled field that belongs to no payout method is ignored, and the request can still return 200.

Request

Fields Description
enabled
optional
boolean
Enable or disable the payout method
enabledCountries
optional
array
Bank transfer only. ISO 3166-1 alpha-2 allow list. [] allows every supported country
mode
conditional
string
PayPal and Venmo. Allowed values are live and sandbox. Required when setting credentials for the first time
account
conditional
string
PayPal and Venmo REST merchant account
clientId
conditional
string
PayPal and Venmo REST client ID
secret
conditional
string
PayPal and Venmo REST secret. Write-only
webhookID
optional
string
PayPal and Venmo webhook ID. Write-only
allowRestrictedCountries
optional
boolean
PayPal only
branchId
optional
string
Check routing / branch ID. Write-only
accountNum
optional
string
Check account number. Write-only
signatory
optional
string
Check signatory name
startingCheck
optional
string
Starting check number, up to 6 digits
verification1
conditional
string
Check micro-deposit amount. Send with verification2, and not with other bank-detail fields
verification2
conditional
string
Check micro-deposit amount. Send with verification1, and not with other bank-detail fields
mailing
optional
object
Check mailing address. country must be US. Values over the field limits return 400
mailing.name
optional
string
Mailing name, max 40 characters
mailing.street1
optional
string
Address line 1, max 100 characters
mailing.street2
optional
string
Address line 2, max 100 characters
mailing.city
optional
string
City, max 100 characters
mailing.region
optional
string
State, max 100 characters
mailing.postal
optional
string
Postal code, max 100 characters
mailing.country
optional
string
Must be US
supportedNetworks[]
optional
string[]
Debit card. Allowed values are visa and mastercard
enabledMobileWalletPayerIds[]
optional
number[]
Mobile wallet payer IDs from /v1/mobile-wallet-payers. [] clears the list

bank-transfer

Accepts enabled and enabledCountries. The response includes enabledCountries at the top level and settings.onboardedCountry (null for sandbox merchants).

A new sub-merchant’s bank transfer stays at status: "pending" until it is approved. Enabling before then returns 400. Poll this payout method and retry once status is approved.

Some countries cannot receive bank transfer. Sending one of them returns 400 naming the country, for example Bank transfer is not supported in: CU.

paypal

Accepts enabled, mode, account, clientId, secret, webhookID, and allowRestrictedCountries.

Configure REST credentials: account, clientId, and secret. On first-time setup the full set is required, and mode is required so Trolley can verify the credentials. Later updates can send a subset once credentials already exist.

The response includes mode, account, apiUsername, clientId, credentialsConfigured, allowRestrictedCountries, and restrictedCountries. secret and webhookID are never returned.

venmo

Same REST credential fields as PayPal (mode, account, clientId, secret, webhookID), without allowRestrictedCountries. The response includes a fixed allowedCountries list (["GU", "PR", "US", "VI"]) that cannot be changed.

check

Accepts enabled, branchId, accountNum, signatory, startingCheck, verification1, verification2, and a partial mailing object.

The response includes signatory, startingCheck, verified, bankAccountConfigured, and mailing. branchId and accountNum are never returned.

Send both verification amounts or neither. A verification request must not include other bank-detail fields.

debit-card

Accepts enabled and supportedNetworks. The response includes settings.supportedNetworks, settings.allowedCountries (always ["US"]), and read-only enabledCountries (always ["US"]). Sending enabledCountries on write returns 400.

mobile-wallet

Accepts enabled and enabledMobileWalletPayerIds. The response includes settings.enabledMobileWalletPayerIds. Sending enabledCountries returns 400.

Unknown payer IDs are dropped with no error: [123, 456] where 456 does not exist returns 200 with [123].

Prerequisites are checked on every PATCH, including a payer-ID-only update. Bank transfer must already be approved. If it is not, the error tells you to poll GET /v1/payout-methods/bank-transfer.

HTTP Code Description
200 Payout method updated
400 Invalid request
401 Invalid API key
410 PayPal Mass Payments is not supported
500 Internal error

Errors

Response (400 Bad Request)

{
  "ok": false,
  "errors": [
    {
      "code": "invalid_field",
      "field": "enabledCountries",
      "message": "Bank transfer is not supported in: CU"
    }
  ]
}

Response (410 Gone)

{
  "ok": false,
  "errors": [
    {
      "code": "deprecated_functionality_error",
      "message": "PayPal Mass Payments credentials (apiUsername, apiPassword, apiSignature) are no longer supported. Configure REST credentials instead: account, clientId and secret."
    }
  ]
}
Error Code Description
invalid_field Invalid field value. Common cases: payout method not yet approved; country not supported; field not valid for this payout method; incomplete PayPal or Venmo credentials; debit card or mobile wallet not available
deprecated_functionality_error PayPal Mass Payments credentials are not supported
invalid_api_key Invalid API key
internal_server_error Internal server errors

Fees & Revenue Share

Read and update the sub-merchant fee schedule and merchant cover.

Get Fees

Authenticate with the sub-merchant API key. Returns the sub-merchant’s payout fee schedule and the merchant cover configuration.

Amounts are JSON strings in both directions (for example "0.50", not 0.50).

The top level of each fee row is Trolley’s fee. general.merchantCoverAmount is how much of that fee the merchant pays on the recipient’s behalf, in the same shape. Cover is one amount per payout route per currency.

fxMargin (keyed by rate group A, B, and C) and fxMarginMobileWallet are Trolley’s markup on the exchange rate for cross-currency payouts. They are included so you can calculate payout cost. No cover can be applied to FX margin.

A base fee may include minimumFee (a floor on what Trolley charges). A cover may include maximumFee (a ceiling on what the merchant contributes).

HTTP Request

GET https://api.trolley.com/v1/fees

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/fees'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/fees');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/fees");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/fees')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/fees")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/fees");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/fees");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "fees": [
    {
      "currencyCode": "USD",
      "gateway": { "paypal": "2.00", "check": "1.50", "venmo": "1.50" },
      "bankTransfer": {
        "ach": "1.00",
        "eft": "1.00",
        "sepa": "4.00",
        "iach": "4.00",
        "wire": "10.00",
        "wire_no_fx": "25.00",
        "fps": "4.00",
        "npp": "4.00",
        "becs": "4.00",
        "fpshk": "4.00"
      },
      "debitCard": {
        "domestic": { "amount": "1.00", "minimumFee": "1.50" },
        "international": { "amount": "1.00", "minimumFee": "4.00" }
      },
      "mobileWallet": { "amount": "1.00", "amountType": "percentage", "minimumFee": "4.00" },
      "fxMargin": { "A": "2.00", "B": "2.95", "C": "2.00" },
      "fxMarginMobileWallet": "2.00",
      "general": {
        "merchantCoverAmount": {
          "gateway": { "paypal": "0.75", "check": "0.00", "venmo": "0.00" },
          "bankTransfer": {
            "ach": "0.50",
            "eft": "0.00",
            "sepa": "0.00",
            "iach": "0.00",
            "wire": "0.00",
            "wire_no_fx": "0.00",
            "fps": "0.00",
            "npp": "0.00",
            "becs": "0.00",
            "fpshk": "0.00"
          },
          "debitCard": {
            "domestic": { "amount": "0.00", "amountType": "percentage", "maximumFee": null },
            "international": { "amount": "0.00", "amountType": "percentage", "maximumFee": null }
          },
          "mobileWallet": { "amount": "0.00", "amountType": "percentage", "maximumFee": null }
        }
      }
    }
  ]
}
Query Param Description
currency
optional
string
ISO 4217 currency code. Omit it to return every currency the merchant has a fee row for
HTTP Code Description
200 Fees returned
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
internal_server_error Internal server errors

Update Fee Cover

Authenticate with the sub-merchant API key. Sets the merchant cover for exactly one payout route in one currency. The response is the updated fee row for that currency.

By default a payout fee comes out of the recipient’s payout: a $100 ACH transfer with a $1.00 fee delivers $99.00. A merchant cover of $0.50 on the ach route means Trolley takes $0.50 from the merchant and $0.50 from the recipient, so the recipient receives $99.50.

Amounts are JSON strings in both directions. An unquoted number such as 0.50 returns 400.

HTTP Request

PATCH https://api.trolley.com/v1/fees

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X PATCH 'https://api.trolley.com/v1/fees' \
-d '{
  "currency": "USD",
  "integration": "bankTransfer",
  "type": "ach",
  "amount": "0.50"
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->patch('/v1/fees', [
    "currency" => "USD",
    "integration" => "bankTransfer",
    "type" => "ach",
    "amount" => "0.50"
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.patch("/v1/fees", {"currency":"USD","integration":"bankTransfer","type":"ach","amount":"0.50"});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('PATCH', '/v1/fees', {"currency" => 'USD', "integration" => 'bankTransfer', "type" => 'ach', "amount" => '0.50'})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("PATCH", "/v1/fees", {"currency":"USD","integration":"bankTransfer","type":"ach","amount":"0.50"})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.patch("/v1/fees", "{\"currency\":\"USD\",\"integration\":\"bankTransfer\",\"type\":\"ach\",\"amount\":\"0.50\"}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("PATCH", "/v1/fees", @"{""currency"":""USD"",""integration"":""bankTransfer"",""type"":""ach"",""amount"":""0.50""}");

Console.WriteLine(response);

Request Schema

{
  currency: string;
  integration: string;
  type: string;
  amount: string;
  amountType?: string;
  maximumFee?: string | null;
}

Response (200 Ok)

{
  "ok": true,
  "fee": {
    "currencyCode": "USD",
    "gateway": { "paypal": "2.00", "check": "1.50", "venmo": "1.50" },
    "bankTransfer": {
      "ach": "1.00",
      "eft": "1.00",
      "sepa": "4.00",
      "iach": "4.00",
      "wire": "10.00",
      "wire_no_fx": "25.00",
      "fps": "4.00",
      "npp": "4.00",
      "becs": "4.00",
      "fpshk": "4.00"
    },
    "debitCard": {
      "domestic": { "amount": "1.00", "minimumFee": "1.50" },
      "international": { "amount": "1.00", "minimumFee": "4.00" }
    },
    "mobileWallet": { "amount": "1.00", "amountType": "percentage", "minimumFee": "4.00" },
    "fxMargin": { "A": "2.00", "B": "2.95", "C": "2.00" },
    "fxMarginMobileWallet": "2.00",
    "general": {
      "merchantCoverAmount": {
        "gateway": { "paypal": "0.75", "check": "0.00", "venmo": "0.00" },
        "bankTransfer": {
          "ach": "0.50",
          "eft": "0.00",
          "sepa": "0.00",
          "iach": "0.00",
          "wire": "0.00",
          "wire_no_fx": "0.00",
          "fps": "0.00",
          "npp": "0.00",
          "becs": "0.00",
          "fpshk": "0.00"
        },
        "debitCard": {
          "domestic": { "amount": "0.00", "amountType": "percentage", "maximumFee": null },
          "international": { "amount": "0.00", "amountType": "percentage", "maximumFee": null }
        },
        "mobileWallet": { "amount": "0.00", "amountType": "percentage", "maximumFee": null }
      }
    }
  }
}

Cover slots:

integration Valid type values Cover style
gateway paypal, check, venmo Fixed only
bankTransfer ach, eft, sepa, iach, wire, wire_no_fx, fps, npp, becs, fpshk Fixed only
debitCard domestic, international Fixed or percentage
mobileWallet mobileWallet Fixed or percentage

mobileWallet still requires "type": "mobileWallet" so every request has the same shape.

Fixed-only slots accept currency, integration, type, amount, and optionally amountType (omit it or send fixed) and maximumFee (omit it or send null). amountType: "percentage" and a non-null maximumFee are rejected.

Debit card and mobile wallet require amountType (fixed or percentage). A percentage amount must be between 0 and 100. maximumFee is the only field with partial-update semantics: omitting it keeps the stored cap; sending null clears it.

Request

Fields Description
currency
required
string
ISO 4217 currency code
integration
required
string
Allowed values are gateway, bankTransfer, debitCard, and mobileWallet
type
required
string
Route for that integration. See the cover slot table
amount
required
string
Cover amount as a JSON string. Must not be negative
amountType
conditional
string
Allowed values are fixed and percentage. Required for debitCard and mobileWallet. For gateway and bank transfer, omit it or send fixed
maximumFee
optional
string
Percentage cover cap as a JSON string. Omit to keep the stored cap; send null to clear it. Not valid on gateway or bank transfer
HTTP Code Description
200 Fee cover updated
400 Invalid request
401 Invalid API key
500 Internal error

Errors

Response (400 Bad Request)

{
  "ok": false,
  "errors": [
    {
      "code": "invalid_field",
      "field": "amountType",
      "message": "Integration 'gateway' only supports a fixed cover amount"
    }
  ]
}
Error Code Description
invalid_field Invalid field value. Common cases: type not valid for integration; negative amount; unquoted number; percentage cover on a fixed-only slot; amountType missing on debit card or mobile wallet; percentage amount above 100
invalid_api_key Invalid API key
internal_server_error Internal server errors

Processing Settings

Control batch validation and upcoming-payment visibility for a sub-merchant.

Get Processing Settings

Authenticate with the sub-merchant API key. Returns the two settings that govern what happens to a payment before it is processed: whether invalid payments are accepted, and upcoming-payment visibility.

HTTP Request

GET https://api.trolley.com/v1/processing-settings

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/processing-settings'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/processing-settings');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/processing-settings");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/processing-settings')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/processing-settings")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/processing-settings");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/processing-settings");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "processingSettings": {
    "acceptInvalidPayments": false,
    "upcomingPaymentVisibilityEnabled": false
  }
}

Response Schema

{
  ok: boolean;
  processingSettings: {
    acceptInvalidPayments: boolean;
    upcomingPaymentVisibilityEnabled: boolean;
  }
}
HTTP Code Description
200 Processing settings returned
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
internal_server_error Internal server errors

Update Processing Settings

Send either field or both. At least one is required. Omitted fields are left unchanged.

A 200 means the recognised fields were applied. Unrecognised fields are ignored. If the body contains only unrecognised fields (for example a typo such as acceptInvalidPayment), the request is rejected with the same 400 as an empty body.

HTTP Request

PATCH https://api.trolley.com/v1/processing-settings

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X PATCH 'https://api.trolley.com/v1/processing-settings' \
-d '{
  "acceptInvalidPayments": true
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->patch('/v1/processing-settings', [
    "acceptInvalidPayments" => true
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.patch("/v1/processing-settings", {"acceptInvalidPayments":true});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('PATCH', '/v1/processing-settings', {"acceptInvalidPayments" => true})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("PATCH", "/v1/processing-settings", {"acceptInvalidPayments": True})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.patch("/v1/processing-settings", "{\"acceptInvalidPayments\":true}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("PATCH", "/v1/processing-settings", @"{""acceptInvalidPayments"":true}");

Console.WriteLine(response);

Request Schema

{
  acceptInvalidPayments?: boolean;
  upcomingPaymentVisibilityEnabled?: boolean;
}

Response (200 Ok)

{
  "ok": true,
  "processingSettings": {
    "acceptInvalidPayments": true,
    "upcomingPaymentVisibilityEnabled": false
  }
}

Request

Fields Description
acceptInvalidPayments
conditional
boolean
When true (recommended, and the default for newly created sub-merchants), a batch that contains invalid payments is accepted and the valid payments are processed. When false, the whole batch is blocked until the invalid payments are fixed. At least one of this field or upcomingPaymentVisibilityEnabled is required
upcomingPaymentVisibilityEnabled
conditional
boolean
Whether a recipient can see a payment in the portal or widget before it has been processed. Requires entitlement on that sub-merchant
HTTP Code Description
200 Processing settings updated
400 Invalid request
401 Invalid API key
500 Internal error

Errors

Response (400 Bad Request)

{
  "ok": false,
  "errors": [
    {
      "code": "invalid_field",
      "field": "upcomingPaymentVisibilityEnabled",
      "message": "This merchant is not entitled to upcoming payment visibility"
    }
  ]
}
Error Code Description
invalid_field Invalid field value, including a missing upcoming payment visibility entitlement or a non-boolean acceptInvalidPayments
invalid_data Neither acceptInvalidPayments nor upcomingPaymentVisibilityEnabled was sent
invalid_api_key Invalid API key
internal_server_error Internal server errors

White Label

Configure white-label branding, icon, and DNS for a sub-merchant.

Get White Label Settings

Authenticate with the sub-merchant API key. Returns branding, custom sending-email configuration, and notification toggles for the sub-merchant.

A brand-new sub-merchant’s first GET returns seeded values, not a 404. businessName and website are populated from the merchant record and everything else is at defaults. Nothing is persisted until the first write.

completed is true only when the full sending-email setup is present: color, businessName, email, icon, domain, subdomain, address, country, website, and one of supportUrl or supportEmail. Branding alone leaves completed as false.

Of the notification toggles, only recipientVerificationStatusUpdated defaults to true.

HTTP Request

GET https://api.trolley.com/v1/white-label

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/white-label'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/white-label');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/white-label");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/white-label')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/white-label")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/white-label");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/white-label");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "whiteLabelSettings": {
    "businessName": "Acme Payments",
    "website": "https://acme.com",
    "color": "#FF6B00",
    "icon": "https://s3.amazonaws.com/example/logo.png",
    "widgetUrl": "https://pay.acme.com",
    "supportEmail": "help@acme.com",
    "supportUrl": "",
    "domain": "acme.com",
    "subdomain": "mail",
    "email": "noreply@mail.acme.com",
    "address": "1 Example Street, Toronto",
    "country": "CA",
    "bcc": ["archive@acme.com"],
    "completed": true,
    "paymentSent": true,
    "paymentMethod": true,
    "paymentReturned": true,
    "taxRequired": true,
    "tinValidationFailed": true,
    "dac7TaxEoyAvailable": true,
    "taxEoyAvailable": true,
    "expiringTaxForms": true,
    "ticketCreated": true,
    "recipientAccountChanged": true,
    "recipientAccountInactive": true,
    "recipientVerificationStatusUpdated": true,
    "upcomingPayment": true,
    "enableEmailOverride": false
  }
}

Response Schema

{
  ok: boolean;
  whiteLabelSettings: {
    businessName: string;
    website: string;
    color: string;
    icon: string;
    widgetUrl: string;
    supportEmail: string;
    supportUrl: string;
    domain: string;
    subdomain: string;
    email: string;
    address: string;
    country: string;
    bcc: string[];
    completed: boolean;
    paymentSent: boolean;
    paymentMethod: boolean;
    paymentReturned: boolean;
    taxRequired: boolean;
    tinValidationFailed: boolean;
    dac7TaxEoyAvailable: boolean;
    taxEoyAvailable: boolean;
    expiringTaxForms: boolean;
    ticketCreated: boolean;
    recipientAccountChanged: boolean;
    recipientAccountInactive: boolean;
    recipientVerificationStatusUpdated: boolean;
    upcomingPayment: boolean;
    enableEmailOverride: boolean;
  }
}
HTTP Code Description
200 White label settings returned
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
internal_server_error Internal server errors

Update White Label Settings

All fields are optional. The write covers branding, sending-email configuration, and notification toggles. icon and completed are not writable here — upload the logo with Update White Label Icon, and completed is derived.

The response is the full settings object, so no follow-up read is required.

HTTP Request

PATCH https://api.trolley.com/v1/white-label

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X PATCH 'https://api.trolley.com/v1/white-label' \
-d '{
  "businessName": "Acme Payments",
  "color": "#FF6B00",
  "supportEmail": "help@acme.com",
  "paymentSent": true
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->patch('/v1/white-label', [
    "businessName" => "Acme Payments",
    "color" => "#FF6B00",
    "supportEmail" => "help@acme.com",
    "paymentSent" => true
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.patch("/v1/white-label", {"businessName":"Acme Payments","color":"#FF6B00","supportEmail":"help@acme.com","paymentSent":true});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('PATCH', '/v1/white-label', {"businessName" => 'Acme Payments', "color" => '#FF6B00', "supportEmail" => 'help@acme.com', "paymentSent" => true})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("PATCH", "/v1/white-label", {"businessName":"Acme Payments","color":"#FF6B00","supportEmail":"help@acme.com","paymentSent":True})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.patch("/v1/white-label", "{\"businessName\":\"Acme Payments\",\"color\":\"#FF6B00\",\"supportEmail\":\"help@acme.com\",\"paymentSent\":true}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("PATCH", "/v1/white-label", @"{""businessName"":""Acme Payments"",""color"":""#FF6B00"",""supportEmail"":""help@acme.com"",""paymentSent"":true}");

Console.WriteLine(response);

Request Schema

{
  businessName?: string;
  website?: string;
  color?: string;
  widgetUrl?: string;
  supportEmail?: string;
  supportUrl?: string;
  domain?: string;
  subdomain?: string;
  email?: string;
  address?: string;
  country?: string;
  bcc?: string[];
  paymentSent?: boolean;
  paymentMethod?: boolean;
  paymentReturned?: boolean;
  taxRequired?: boolean;
  tinValidationFailed?: boolean;
  dac7TaxEoyAvailable?: boolean;
  taxEoyAvailable?: boolean;
  expiringTaxForms?: boolean;
  ticketCreated?: boolean;
  recipientAccountChanged?: boolean;
  recipientAccountInactive?: boolean;
  recipientVerificationStatusUpdated?: boolean;
  upcomingPayment?: boolean;
  enableEmailOverride?: boolean;
}

Response (200 Ok)

{
  "ok": true,
  "whiteLabelSettings": {
    "businessName": "Acme Payments",
    "website": "https://acme.com",
    "color": "#FF6B00",
    "icon": "https://s3.amazonaws.com/example/logo.png",
    "widgetUrl": "https://pay.acme.com",
    "supportEmail": "help@acme.com",
    "supportUrl": "",
    "domain": "acme.com",
    "subdomain": "mail",
    "email": "noreply@mail.acme.com",
    "address": "1 Example Street, Toronto",
    "country": "CA",
    "bcc": ["archive@acme.com"],
    "completed": true,
    "paymentSent": true,
    "paymentMethod": true,
    "paymentReturned": true,
    "taxRequired": true,
    "tinValidationFailed": true,
    "dac7TaxEoyAvailable": true,
    "taxEoyAvailable": true,
    "expiringTaxForms": true,
    "ticketCreated": true,
    "recipientAccountChanged": true,
    "recipientAccountInactive": true,
    "recipientVerificationStatusUpdated": true,
    "upcomingPayment": true,
    "enableEmailOverride": false
  }
}

Request

Fields Description
businessName
optional
string
Non-empty business name
website
optional
string
Non-empty website URL
color
optional
string
# followed by exactly six hex digits, for example #FF6B00
widgetUrl
optional
string
Widget URL, maximum 255 characters
supportEmail
optional
string
Support email. Mutually exclusive with supportUrl
supportUrl
optional
string
Support URL, maximum 255 characters. Mutually exclusive with supportEmail
domain
optional
string
Sending domain, 3–191 characters. Send with subdomain to register a sending domain
subdomain
optional
string
Sending subdomain, 1–63 characters. Send with domain to register a sending domain
email
optional
string
From address. The host must match subdomain.domain
address
optional
string
Physical address used in sent mail
country
optional
string
ISO 3166-1 alpha-2 country code
bcc
optional
array
BCC email addresses
paymentSent
optional
boolean
Notify when a payment is sent
paymentMethod
optional
boolean
Notify for payout method events
paymentReturned
optional
boolean
Notify when a payment is returned
taxRequired
optional
boolean
Notify when tax information is required
tinValidationFailed
optional
boolean
Notify when TIN validation fails
dac7TaxEoyAvailable
optional
boolean
Notify when DAC7 year-end tax forms are available
taxEoyAvailable
optional
boolean
Notify when year-end tax forms are available
expiringTaxForms
optional
boolean
Notify when tax forms are expiring
ticketCreated
optional
boolean
Notify when a ticket is created
recipientAccountChanged
optional
boolean
Notify when a recipient account changes
recipientAccountInactive
optional
boolean
Notify when a recipient account is inactive
recipientVerificationStatusUpdated
optional
boolean
Notify when recipient verification status changes. Defaults to true
upcomingPayment
optional
boolean
Notify for upcoming payments
enableEmailOverride
optional
boolean
Allow email override
HTTP Code Description
200 White label settings updated
400 Invalid request
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_field Invalid field value
invalid_api_key Invalid API key
internal_server_error Internal server errors

Update White Label Icon

Authenticate with the sub-merchant API key. Uploads the sub-merchant logo. Send a full data URI or a bare base64 string. The response is the full white-label settings object with icon set to the hosted URL.

Maximum decoded size is 512,000 bytes. Supported types are PNG, JPEG, GIF, and SVG; the file contents must match a supported image type. One logo per sub-merchant; send the same request again to replace it.

HTTP Request

PATCH https://api.trolley.com/v1/white-label/icon

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X PATCH 'https://api.trolley.com/v1/white-label/icon' \
-d '{
  "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->patch('/v1/white-label/icon', [
    "image" => "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.patch("/v1/white-label/icon", {"image":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('PATCH', '/v1/white-label/icon', {"image" => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("PATCH", "/v1/white-label/icon", {"image":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.patch("/v1/white-label/icon", "{\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==\"}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("PATCH", "/v1/white-label/icon", @"{""image"":""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==""}");

Console.WriteLine(response);

Request Schema

{
  image: string; // data URI or bare base64
}

Response (200 Ok)

{
  "ok": true,
  "whiteLabelSettings": {
    "businessName": "Acme Payments",
    "website": "https://acme.com",
    "color": "#FF6B00",
    "icon": "https://s3.amazonaws.com/example/logo.png",
    "widgetUrl": "https://pay.acme.com",
    "supportEmail": "help@acme.com",
    "supportUrl": "",
    "domain": "acme.com",
    "subdomain": "mail",
    "email": "noreply@mail.acme.com",
    "address": "1 Example Street, Toronto",
    "country": "CA",
    "bcc": ["archive@acme.com"],
    "completed": false,
    "paymentSent": true,
    "paymentMethod": true,
    "paymentReturned": true,
    "taxRequired": true,
    "tinValidationFailed": true,
    "dac7TaxEoyAvailable": true,
    "taxEoyAvailable": true,
    "expiringTaxForms": true,
    "ticketCreated": true,
    "recipientAccountChanged": true,
    "recipientAccountInactive": true,
    "recipientVerificationStatusUpdated": true,
    "upcomingPayment": true,
    "enableEmailOverride": false
  }
}

Request

Fields Description
image
required
string
Logo as a data URI (data:image/png;base64,...) or a bare base64 string. Maximum 512,000 bytes decoded. PNG, JPEG, GIF, or SVG
HTTP Code Description
200 White label icon updated
400 Invalid image
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_field Invalid image
invalid_api_key Invalid API key
internal_server_error Internal server errors

Get White Label DNS Records

Authenticate with the sub-merchant API key. Returns the CNAME records to publish in your DNS for the custom sending domain.

A 404 means no sending domain is configured yet. Call Update White Label Settings with domain and subdomain first.

HTTP Request

GET https://api.trolley.com/v1/white-label/dns-records

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/white-label/dns-records'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/white-label/dns-records');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/white-label/dns-records");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/white-label/dns-records')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/white-label/dns-records")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/white-label/dns-records");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/white-label/dns-records");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "dnsRecords": {
    "mailCname": {
      "type": "cname",
      "host": "mail.acme.com",
      "data": "u123.wl.sendgrid.net"
    },
    "dkim1": {
      "type": "cname",
      "host": "s1._domainkey.acme.com",
      "data": "s1.domainkey.u123.wl.sendgrid.net"
    },
    "dkim2": {
      "type": "cname",
      "host": "s2._domainkey.acme.com",
      "data": "s2.domainkey.u123.wl.sendgrid.net"
    }
  }
}

Response Schema

{
  ok: boolean;
  dnsRecords: {
    mailCname: { type: string; host: string; data: string };
    dkim1: { type: string; host: string; data: string };
    dkim2: { type: string; host: string; data: string };
  }
}
HTTP Code Description
200 DNS records returned
401 Invalid API key
404 Sending domain not configured
500 Internal error

Errors

Error Code Description
not_found Sending domain is not configured
invalid_api_key Invalid API key
internal_server_error Internal server errors

Verify White Label DNS Records

Asks whether the published CNAME records are live. The body is empty. Results are per record so you can see which record is wrong.

DNS propagation can take from a few minutes up to 48 hours. Poll with backoff between calls.

If no sending domain is configured, this endpoint returns 200 with valid: false and each record reason "No sending domain configured" — not a 404.

HTTP Request

POST https://api.trolley.com/v1/white-label/dns-records/verify

The request body is empty.

Fields Description
(none) This request has no body fields

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X POST 'https://api.trolley.com/v1/white-label/dns-records/verify'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->post('/v1/white-label/dns-records/verify', []);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.post("/v1/white-label/dns-records/verify", {});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('POST', '/v1/white-label/dns-records/verify', {})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("POST", "/v1/white-label/dns-records/verify", {})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.post("/v1/white-label/dns-records/verify", "{}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("POST", "/v1/white-label/dns-records/verify", "{}");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "valid": false,
  "records": {
    "mailCname": { "valid": true, "reason": null },
    "dkim1": { "valid": false, "reason": "Expected CNAME to match u123.wl.sendgrid.net" },
    "dkim2": { "valid": false, "reason": "Record not found" }
  }
}

Response Schema

{
  ok: boolean;
  valid: boolean;
  records: {
    mailCname: { valid: boolean; reason: string | null };
    dkim1: { valid: boolean; reason: string | null };
    dkim2: { valid: boolean; reason: string | null };
  }
}
HTTP Code Description
200 DNS verification result
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
internal_server_error Internal server errors

Delete White Label Email Domain

Authenticate with the sub-merchant API key. Removes the custom sending domain. It clears domain, subdomain, email, address, country, bcc, supportEmail, and supportUrl. Clearing the support contact also sets completed to false. The response is the full white-label settings object.

Configuring the same domain again means publishing DNS records and waiting for propagation a second time.

HTTP Request

DELETE https://api.trolley.com/v1/white-label/email

The request body is empty.

Fields Description
(none) This request has no body fields

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X DELETE 'https://api.trolley.com/v1/white-label/email'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->delete('/v1/white-label/email');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.remove("/v1/white-label/email");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('DELETE', '/v1/white-label/email')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("DELETE", "/v1/white-label/email")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.delete("/v1/white-label/email");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("DELETE", "/v1/white-label/email");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "whiteLabelSettings": {
    "businessName": "Acme Payments",
    "website": "https://acme.com",
    "color": "#FF6B00",
    "icon": "https://s3.amazonaws.com/example/logo.png",
    "widgetUrl": "https://pay.acme.com",
    "supportEmail": "",
    "supportUrl": "",
    "domain": "",
    "subdomain": "",
    "email": "",
    "address": "",
    "country": "",
    "bcc": [],
    "completed": false,
    "paymentSent": true,
    "paymentMethod": true,
    "paymentReturned": true,
    "taxRequired": true,
    "tinValidationFailed": true,
    "dac7TaxEoyAvailable": true,
    "taxEoyAvailable": true,
    "expiringTaxForms": true,
    "ticketCreated": true,
    "recipientAccountChanged": true,
    "recipientAccountInactive": true,
    "recipientVerificationStatusUpdated": true,
    "upcomingPayment": true,
    "enableEmailOverride": false
  }
}
HTTP Code Description
200 Sending domain removed
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
internal_server_error Internal server errors

Widget

Configure the recipient widget.

Get Widget Configuration

Authenticate with the sub-merchant API key. Returns the recipient widget and portal configuration for that sub-merchant.

If no configuration exists yet, this endpoint creates one with defaults and returns it. The first GET is not a 404.

The response is the nested iframeConfig object. Branding fields such as icon, businessName, businessURL, supportEmail, supportURL, and colors.brandColor come from white-label settings and are not writable here.

HTTP Request

GET https://api.trolley.com/v1/iframe/config

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/iframe/config'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/iframe/config');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/iframe/config");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/iframe/config')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/iframe/config")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/iframe/config");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/iframe/config");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "iframeConfig": {
    "colors": {
      "primary": "#0095FF",
      "success": "#009C4B",
      "error": "#FA0021",
      "warning": "#FA8100",
      "info": "#0044CC",
      "border": "#E1E4E7",
      "heading": "#4C4C4C",
      "text": "#4C4C4C",
      "inputText": "#4C4C4C",
      "inputBorder": "#E1E4E7",
      "subText": "#8695A5",
      "background": "#FFFFFF",
      "brandColor": "#FF6B00"
    },
    "style": {
      "borderRadius": "4",
      "buttonBorderRadius": "4"
    },
    "enabled": true,
    "usTax": false,
    "allowedDomains": "*",
    "faq": false,
    "faqLink": "",
    "privacy": false,
    "privacyLink": "",
    "dobRequirement": "optional",
    "allowedDomainsEnabled": false,
    "taxHelpText": "",
    "taxFormUpload": false,
    "showFees": true,
    "showLanguage": false,
    "showBorders": true,
    "showPayoutMethods": true,
    "showPayments": false,
    "showOfflinePayments": false,
    "portalEnabled": true,
    "portalDomain": null,
    "portalURL": null,
    "icon": null,
    "businessName": "Acme Payments",
    "businessURL": "https://acme.com",
    "supportEmail": "help@acme.com",
    "supportURL": "",
    "enabledCountries": [],
    "showVerificationHistory": true
  }
}

Response Schema

{
  ok: boolean;
  iframeConfig: {
    colors: {
      primary: string;
      success: string;
      error: string;
      warning: string;
      info: string;
      border: string;
      heading: string;
      text: string;
      inputText: string;
      inputBorder: string;
      subText: string;
      background: string;
      brandColor: string;
    };
    style: {
      borderRadius: string;
      buttonBorderRadius: string;
    };
    enabled: boolean;
    usTax: boolean;
    allowedDomains: string;
    faq: boolean;
    faqLink: string;
    privacy: boolean;
    privacyLink: string;
    dobRequirement: string | null;
    allowedDomainsEnabled: boolean;
    taxHelpText: string;
    taxFormUpload: boolean;
    showFees: boolean;
    showLanguage: boolean;
    showBorders: boolean;
    showPayoutMethods: boolean;
    showPayments: boolean;
    showOfflinePayments: boolean;
    portalEnabled: boolean;
    portalDomain: string | null;
    portalURL: string | null;
    icon: string | null;
    businessName: string;
    businessURL: string;
    supportEmail: string;
    supportURL: string;
    enabledCountries: string[];
    showVerificationHistory: boolean;
  }
}
HTTP Code Description
200 Widget configuration returned
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
internal_server_error Internal server errors

Update Widget Configuration

Authenticate with the sub-merchant API key. Merges recipient widget and portal settings for that sub-merchant. Every body field is optional — send only what is changing. The response is the full nested iframeConfig object, so no follow-up read is required.

You can implement the Recipient Widget for sub-merchant accounts in order to capture recipient information directly into each sub-merchant account. Use the sub-merchant API key when you integrate the widget.

Branding fields (icon, businessName, businessURL, supportEmail, supportURL, and colors.brandColor) are not writable here. Set those with the white-label endpoints.

HTTP Request

POST https://api.trolley.com/v1/iframe/config

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X POST 'https://api.trolley.com/v1/iframe/config' \
-d '{
  "colors": {
    "primary": "#112233",
    "success": "#22AA66",
    "error": "#CC3344",
    "warning": "#CC9900",
    "border": "#CCCCCC",
    "heading": "#111111",
    "text": "#222222",
    "inputText": "#222222",
    "inputBorder": "#DDDDDD",
    "subText": "#666666",
    "background": "#FFFFFF"
  }
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->post('/v1/iframe/config', [
    "colors" => [
        "primary" => "#112233",
        "success" => "#22AA66",
        "error" => "#CC3344",
        "warning" => "#CC9900",
        "border" => "#CCCCCC",
        "heading" => "#111111",
        "text" => "#222222",
        "inputText" => "#222222",
        "inputBorder" => "#DDDDDD",
        "subText" => "#666666",
        "background" => "#FFFFFF"
    ]
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.post("/v1/iframe/config", {"colors":{"primary":"#112233","success":"#22AA66","error":"#CC3344","warning":"#CC9900","border":"#CCCCCC","heading":"#111111","text":"#222222","inputText":"#222222","inputBorder":"#DDDDDD","subText":"#666666","background":"#FFFFFF"}});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('POST', '/v1/iframe/config', {"colors" => {"primary" => '#112233', "success" => '#22AA66', "error" => '#CC3344', "warning" => '#CC9900', "border" => '#CCCCCC', "heading" => '#111111', "text" => '#222222', "inputText" => '#222222', "inputBorder" => '#DDDDDD', "subText" => '#666666', "background" => '#FFFFFF'}})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("POST", "/v1/iframe/config", {"colors":{"primary":"#112233","success":"#22AA66","error":"#CC3344","warning":"#CC9900","border":"#CCCCCC","heading":"#111111","text":"#222222","inputText":"#222222","inputBorder":"#DDDDDD","subText":"#666666","background":"#FFFFFF"}})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.post("/v1/iframe/config", "{\"colors\":{\"primary\":\"#112233\",\"success\":\"#22AA66\",\"error\":\"#CC3344\",\"warning\":\"#CC9900\",\"border\":\"#CCCCCC\",\"heading\":\"#111111\",\"text\":\"#222222\",\"inputText\":\"#222222\",\"inputBorder\":\"#DDDDDD\",\"subText\":\"#666666\",\"background\":\"#FFFFFF\"}}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("POST", "/v1/iframe/config", @"{""colors"":{""primary"":""#112233"",""success"":""#22AA66"",""error"":""#CC3344"",""warning"":""#CC9900"",""border"":""#CCCCCC"",""heading"":""#111111"",""text"":""#222222"",""inputText"":""#222222"",""inputBorder"":""#DDDDDD"",""subText"":""#666666"",""background"":""#FFFFFF""}}");

Console.WriteLine(response);

Request Schema

{
  colors?: {
    primary?: string;
    success?: string;
    error?: string;
    warning?: string;
    info?: string;
    border?: string;
    heading?: string;
    text?: string;
    inputText?: string;
    inputBorder?: string;
    subText?: string;
    background?: string;
  };
  style?: {
    borderRadius?: string;
    buttonBorderRadius?: string;
  };
  enabled?: boolean;
  allowedDomain?: string;
  allowedDomainsEnabled?: boolean;
  faq?: boolean;
  faqLink?: string;
  privacy?: boolean;
  privacyLink?: string;
  dobRequirement?: string;
  taxHelpText?: string;
  taxFormUpload?: boolean;
  usTax?: boolean;
  showFees?: boolean;
  showLanguage?: boolean;
  showBorders?: boolean;
  showPayoutMethods?: boolean;
  showPayments?: boolean;
  showOfflinePayments?: boolean;
  portalEnabled?: boolean;
  portalDomain?: string | null;
  enabledCountries?: string[];
  showVerificationHistory?: boolean;
}

Response (200 Ok)

{
  "ok": true,
  "iframeConfig": {
    "colors": {
      "primary": "#112233",
      "success": "#22AA66",
      "error": "#CC3344",
      "warning": "#CC9900",
      "info": "#0044CC",
      "border": "#CCCCCC",
      "heading": "#111111",
      "text": "#222222",
      "inputText": "#222222",
      "inputBorder": "#DDDDDD",
      "subText": "#666666",
      "background": "#FFFFFF",
      "brandColor": "#FF6B00"
    },
    "style": {
      "borderRadius": "4",
      "buttonBorderRadius": "4"
    },
    "enabled": true,
    "usTax": false,
    "allowedDomains": "*",
    "faq": false,
    "faqLink": "",
    "privacy": false,
    "privacyLink": "",
    "dobRequirement": "optional",
    "allowedDomainsEnabled": false,
    "taxHelpText": "",
    "taxFormUpload": false,
    "showFees": true,
    "showLanguage": false,
    "showBorders": true,
    "showPayoutMethods": true,
    "showPayments": false,
    "showOfflinePayments": false,
    "portalEnabled": true,
    "portalDomain": "acme",
    "portalURL": "https://acme.portal.trolley.com",
    "icon": "https://s3.amazonaws.com/example/logo.png",
    "businessName": "Acme Payments",
    "businessURL": "https://acme.com",
    "supportEmail": "help@acme.com",
    "supportURL": "",
    "enabledCountries": [],
    "showVerificationHistory": true
  }
}

The request body is optional; omit it to leave the current configuration unchanged.

Request

Fields Description
colors
optional
object
Widget color overrides. Hex colors, for example #112233
colors.primary
optional
string
Primary color
colors.success
optional
string
Success state color
colors.error
optional
string
Error state color
colors.warning
optional
string
Warning state color
colors.info
optional
string
Info state color
colors.border
optional
string
Border color
colors.heading
optional
string
Heading color
colors.text
optional
string
Body text color
colors.inputText
optional
string
Input text color
colors.inputBorder
optional
string
Input border color
colors.subText
optional
string
Secondary text color
colors.background
optional
string
Background color
style
optional
object
Corner radius settings
style.borderRadius
optional
string
Widget corner radius, 0 to 100
style.buttonBorderRadius
optional
string
Button corner radius, 0 to 100
enabled
optional
boolean
Enable or disable the recipient widget
allowedDomain
optional
string
Domain restriction for embedding the widget. Maximum 1024 characters. Returned as allowedDomains
allowedDomainsEnabled
optional
boolean
Enforce allowedDomain
faq
optional
boolean
Show the FAQ link
faqLink
optional
string
FAQ URL. Maximum 1024 characters
privacy
optional
boolean
Show the privacy link
privacyLink
optional
string
Privacy policy URL. Maximum 1024 characters
dobRequirement
optional
string
Date of birth. Allowed values are optional, required, and none
taxHelpText
optional
string
Help text shown on tax forms. Maximum 4096 characters
taxFormUpload
optional
boolean
Allow recipients to upload tax forms
usTax
optional
boolean
Collect US tax forms
showFees
optional
boolean
Show fees in the widget
showLanguage
optional
boolean
Show the language selector
showBorders
optional
boolean
Show borders
showPayoutMethods
optional
boolean
Show payout methods
showPayments
optional
boolean
Show payment history
showOfflinePayments
optional
boolean
Show offline payments
portalEnabled
optional
boolean
Enable the recipient portal
portalDomain
optional
string/null
Recipient portal hostname. Fully qualified domain name, minimum 4 characters. Send null to clear
enabledCountries
optional
string[]
Recipient countries as ISO 3166-1 alpha-2 codes. Maximum 1000 entries
showVerificationHistory
optional
boolean
Show verification history
HTTP Code Description
200 Widget config updated
400 Invalid request
401 Invalid API key
500 Internal error

Errors

Response (400 Bad Request)

{
  "ok": false,
  "errors": [
    {
      "code": "invalid_field",
      "field": "portalDomain",
      "message": "Please provide a fully qualified domain name."
    }
  ]
}

Response (401 Unauthorized)

{
  "ok": false,
  "errors": [
    {
      "code": "invalid_api_key",
      "message": "Invalid API key"
    }
  ]
}
Error Code Description
invalid_field Invalid field value
invalid_api_key Invalid API key
internal_server_error Internal server errors

Webhooks

Create Webhook

HTTP Request

POST https://api.trolley.com/v1/subscriptions

Adds a new webhook

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X POST 'https://api.trolley.com/v1/subscriptions' \
-d '{
  "action": "created",
  "model": "recipient",
  "target": "https://example.com/webhooks/trolley"
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->post('/v1/subscriptions', [
    "action" => "created",
    "model" => "recipient",
    "target" => "https://example.com/webhooks/trolley"
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.post("/v1/subscriptions", {"action":"created","model":"recipient","target":"https://example.com/webhooks/trolley"});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('POST', '/v1/subscriptions', {"action" => 'created', "model" => 'recipient', "target" => 'https://example.com/webhooks/trolley'})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("POST", "/v1/subscriptions", {"action":"created","model":"recipient","target":"https://example.com/webhooks/trolley"})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.post("/v1/subscriptions", "{\"action\":\"created\",\"model\":\"recipient\",\"target\":\"https://example.com/webhooks/trolley\"}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("POST", "/v1/subscriptions", @"{""action"":""created"",""model"":""recipient"",""target"":""https://example.com/webhooks/trolley""}");

Console.WriteLine(response);

Request Schema

{
  action: NotificationAction,
  model: NotificationType,
  target: string, // URL endpoint to send events to
}

Enumerated Strings

enum NotificationAction {
  CREATED = "created",
  UPDATED = "updated",
  DELETED = "deleted",

  // Batch
  PROCESSING = "processing", // fired when batch startProcessing() is called
  COMPLETED = "completed", // processed with at least one success
  // We don't expose Batch.COMPLETED in the UI because
  // It overlaps with PROCESSING too much

  // Payment
  FAILED = "failed",
  RETURNED = "returned",

  // Batch and Payments
  PROCESSED = "processed", // once a batch/payment is processed successfully
  // (for batch this means one payment is successful)

  // User
  PASSWORD_RESET = "password-reset",

  // Recipient
  COMPLIANCE_CHECK = "compliance-check",

  // Batch and Recipient uploads
  UPLOAD_RECEIVED = "upload-received",
  UPLOAD_PROCESSING = "upload-processing",
  UPLOAD_COMPLETE = "upload-completed",

  ALL = "*",
}

enum NotificationType {
  USER = "user",
  RECIPIENT = "recipient",
  UPLOAD = "upload",
  BATCH = "batch",
  PAYMENT = "payment",
  RECIPIENT_ACCOUNT = "recipientAccount",
  ALL = "*",
}

Response (200 Ok)

{
  "ok": true,
  "subscription": {
    "id": "S-1a2B3c4D5e6F7g8H9i0J1k",
    "action": "created",
    "model": "recipient",
    "target": "https://example.com/webhooks/trolley"
  }
}

Response Schema

{
  ok: boolean; 
  subscription: {
    id: string;
    action: NotificationAction;
    model: NotificationType;
    target: string;
  }
}

Request

Fields Description
action
required
string
The event that triggered this webhook
model
required
string
NotificationType - description of the model object
target
required
string
URL to send webhooks to
HTTP Code Description
200 Webhook created
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
invalid_field Invalid field value
internal_server_error Internal server errors

Webhooks List

HTTP Request

GET https://api.trolley.com/v1/subscriptions

Get the list of subscriptions and values

Example Request

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/subscriptions'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/subscriptions');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/subscriptions");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/subscriptions')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/subscriptions")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/subscriptions");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/subscriptions");

Console.WriteLine(response);

Response (200 ok)

{
  "ok": true,
  "subscriptions": [
    {
      "id": "S-1a2B3c4D5e6F7g8H9i0J1k",
      "action": "created",
      "model": "recipient",
      "target": "https://example.com/webhooks/trolley"
    }
  ]
}

Response Schema

{
  subscriptions: {
    id: string,
    action: NotificationAction,
    model: NotificationType,
    target: string,
  }[];
}
HTTP Code Description
200 Webhooks returned
401 Invalid API key
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
internal_server_error Internal server errors

Get, Update, and Delete Webhook

HTTP Request

GET https://api.trolley.com/v1/subscriptions/:id

PATCH https://api.trolley.com/v1/subscriptions/:id

DELETE https://api.trolley.com/v1/subscriptions/:id

Example Request (GET)

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X GET 'https://api.trolley.com/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->get('/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.get("/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('GET', '/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("GET", "/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k")

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.get("/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("GET", "/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "subscription": {
    "id": "S-1a2B3c4D5e6F7g8H9i0J1k",
    "action": "created",
    "model": "recipient",
    "target": "https://example.com/webhooks/trolley"
  }
}

Example Request (PATCH)

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X PATCH 'https://api.trolley.com/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k' \
-d '{
  "action": "updated",
  "model": "recipient",
  "target": "https://example.com/webhooks/trolley-updated"
}'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->patch('/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k', [
    "action" => "updated",
    "model" => "recipient",
    "target" => "https://example.com/webhooks/trolley-updated"
]);

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.patch("/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k", {"action":"updated","model":"recipient","target":"https://example.com/webhooks/trolley-updated"});

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('PATCH', '/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k', {"action" => 'updated', "model" => 'recipient', "target" => 'https://example.com/webhooks/trolley-updated'})

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

response = client.request("PATCH", "/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k", {"action":"updated","model":"recipient","target":"https://example.com/webhooks/trolley-updated"})

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.patch("/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k", "{\"action\":\"updated\",\"model\":\"recipient\",\"target\":\"https://example.com/webhooks/trolley-updated\"}");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("PATCH", "/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k", @"{""action"":""updated"",""model"":""recipient"",""target"":""https://example.com/webhooks/trolley-updated""}");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true,
  "subscription": {
    "id": "S-1a2B3c4D5e6F7g8H9i0J1k",
    "action": "updated",
    "model": "recipient",
    "target": "https://example.com/webhooks/trolley-updated"
  }
}

Example Request (DELETE)

curl \
-H "Authorization: prsign ${TROLLEY_ACCESS_KEY}:${TROLLEY_SIGNATURE:-docs-signature}" \
-H 'Content-Type: application/json' \
-H "X-PR-Timestamp: ${TROLLEY_TIMESTAMP:-$(date +%s)}" \
-X DELETE 'https://api.trolley.com/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k'
<?php
use Trolley;

Trolley\Configuration::publicKey('YOUR_ACCESS_KEY');
Trolley\Configuration::privateKey('YOUR_SECRET_KEY');

$http = new Trolley\Http(Trolley\Configuration::$global);
$response = $http->delete('/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k');

print_r($response);
?>
const trolley = require("trolleyhq");

const client = trolley.connect({
  key: "YOUR_ACCESS_KEY",
  secret: "YOUR_SECRET_KEY"
});

const response = await client.client.remove("/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k");

console.log(response);
require 'trolley'

client = Trolley.client('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY')

response = client.request('DELETE', '/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k')

puts response
from trolley.configuration import Configuration

client = Configuration.gateway(f'{ACCESS_KEY}', f'{SECRET_KEY}')

try:
    response = client.request("DELETE", "/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k")
except BaseException as error:
    response = error

print(response)
import com.trolley.Configuration;
import com.trolley.Gateway;

...

Configuration config = new Configuration("<ACCESS_KEY>", "<SECRET_KEY>");
Gateway client = new Gateway(config);

String response = client.client.delete("/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k");

System.out.println(response);
using Trolley;

Gateway gateway = new Gateway("<ACCESS_KEY>", "<SECRET_KEY>");

string response = gateway.Request("DELETE", "/v1/subscriptions/S-1a2B3c4D5e6F7g8H9i0J1k");

Console.WriteLine(response);

Response (200 Ok)

{
  "ok": true
}
Fields Description
id
required
string
Webhook subscription ID used in the path
action
optional
string
Event action filter for PATCH
model
optional
string
Event model filter for PATCH
target
optional
string
Destination URL for PATCH
HTTP Code Description
200 Webhook operation passed
401 Invalid API key
404 Webhook not found
500 Internal error

Errors

Error Code Description
invalid_api_key Invalid API key
not_found Object doesn’t exist
internal_server_error Internal server errors

Webhook callbacks for Sub-merchant profiles

The format for all webhook callbacks are the same except for the model object returned. Webhook callbacks and their format are explained here:

http://developers.trolley.com/api/#webhooks

Merchant Model Object:

interface Merchant {
  id: number;
  merchantId: string;
  name: string;
  parentMerchantId: string | null;
  status: string;
  phone: string;
  website: string;
  sandbox: boolean;
  primaryCurrency: extra.CurrencyCode; // string 3 letters
  allowedIPs: string;
  allowedDomains: string;
  country: extra.CountryCode | null; // string 3 letters
  region: string | null;
}

The “status” field will tell you if the sub-merchant has gone live. The sub-merchant status values can be one of these:

Once the sub-merchant or merchant is “approved” that merchant is live.

enum MerchantStatus {
 APPROVED = "approved",
 APPROVED_BY_PARTNER = "approved_by_partner",
 SUSPENDED = "suspended",
 DELETED = "deleted",
 PENDING = "pending",
 SIGNUP_REQUESTED = "signup_requested",
}
×