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