Partners
Manage partner channels, commission settlement cycles, and partner-customer associations.
Partners API
The Partners API enables organizations to build and scale distribution networks through agencies, resellers, and affiliates. It provides a framework for managing partner identities, commission calculations (fees), and financial settlements (payouts), while maintaining clear attribution for every customer acquired through the channel.
Partner records and all related financial resources are isolated to the company associated with the API token. Use partners:read|write for partner profiles, partner_groups:read|write for the referral policy, and partner_payouts:read|write for payouts. Monetary snapshots created by the current payout flow use integer minor units together with an ISO 4217 currency code; decimal fields remain available for backward compatibility.
Channel Management
List Partners
Retrieve a paginated directory of all registered partners. This list includes high-level identification data and current status for each collaborative entity.
partners:readPath, query, and header parameters
sizepageResponses
Partner directory successfully retrieved.
{
"entities": "Partner",
"count": 24,
"per_page": 100,
"pages": {
"current": 1,
"max": 1
},
"elements": [
{
"id": "675d...",
"name": "Global Solutions Agency",
"external_id": "GSA-9921",
"partner_code": "GOLD-PARTNER-X"
}
]
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Provision New Partner
Onboard a new partner into the ecosystem. This initializes their profile and associates them with a governance group (Partner Group) to define their commission rules.
partners:writePath, query, and header parameters
No path, query, or additional header parameters.
Request Body
{
"partner": {
"name": "Apex Marketing Group",
"email": "channel@apex-marketing.com",
"external_id": "APEX_001",
"code": "APEXPROMO",
"partner_group_id": "66bf22b0...",
"referrer_partner_id": "66bf22b01234567890abcdef",
"address": {
"name": "Apex Marketing Group Ltd",
"tax_number": "GB123456789",
"country": "GB",
"city": "London",
"address": "1 Example Street",
"email": "billing@apex-marketing.com",
"legal_email": "legal@apex-marketing.com"
}
}
}partner.nameRequiredpartner.emailRequiredpartner.partner_group_idpartner.referrer_partner_idpartner.address.namepartner.address.vat_numberpartner.address.tax_numberpartner.address.countrypartner.address.statepartner.address.citypartner.address.postal_codepartner.address.addresspartner.address.emailpartner.address.legal_emailResponses
Partner successfully provisioned.
{
"partner": {
"id": "6768..."
}
}Request examples
curl --request POST 'https://api.hub.donutwork.com/2026-02-01/partners.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"partner": {
"name": "Apex Marketing Group",
"email": "channel@apex-marketing.com",
"external_id": "APEX_001",
"code": "APEXPROMO",
"partner_group_id": "66bf22b0...",
"referrer_partner_id": "66bf22b01234567890abcdef",
"address": {
"name": "Apex Marketing Group Ltd",
"tax_number": "GB123456789",
"country": "GB",
"city": "London",
"address": "1 Example Street",
"email": "billing@apex-marketing.com",
"legal_email": "legal@apex-marketing.com"
}
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners.json', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"partner": {
"name": "Apex Marketing Group",
"email": "channel@apex-marketing.com",
"external_id": "APEX_001",
"code": "APEXPROMO",
"partner_group_id": "66bf22b0...",
"referrer_partner_id": "66bf22b01234567890abcdef",
"address": {
"name": "Apex Marketing Group Ltd",
"tax_number": "GB123456789",
"country": "GB",
"city": "London",
"address": "1 Example Street",
"email": "billing@apex-marketing.com",
"legal_email": "legal@apex-marketing.com"
}
}
}),
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{
"partner": {
"name": "Apex Marketing Group",
"email": "channel@apex-marketing.com",
"external_id": "APEX_001",
"code": "APEXPROMO",
"partner_group_id": "66bf22b0...",
"referrer_partner_id": "66bf22b01234567890abcdef",
"address": {
"name": "Apex Marketing Group Ltd",
"tax_number": "GB123456789",
"country": "GB",
"city": "London",
"address": "1 Example Street",
"email": "billing@apex-marketing.com",
"legal_email": "legal@apex-marketing.com"
}
}
}',
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Creating a partner through this endpoint does not grant Partner Portal access. Portal access requires a separate onboarding flow.
portal_access_mode is returned by partner read/update responses with one of disabled, active, or read_only. It is read-only in this API and can be changed only by a company administrator.
Partner Referral Policy
The company-wide referral policy is separate from Partner Groups. It applies a percentage to the preceding commission at each upstream level, beginning with the direct partner fee. The policy is selected when the direct fee is assigned. It is disabled by default; no partner subscription is required. A partner can introduce another partner and still earn direct commissions from their own customers.
rate_bps is basis points (200 = 2%), max_depth counts upstream partners (1–10), and cap_bps limits total direct plus referral commissions to at most 50% of the original commissionable customer amount. If the direct fee alone exceeds the cap, no referral fee is issued; the direct fee is not reduced. Each level is rounded to minor currency units; processing stops at a zero fee, a missing upstream partner, the depth limit, or an exhausted cap.
Read Referral Policy
partner_groups:readPath, query, and header parameters
No path, query, or additional header parameters.
Responses
Current company referral policy. A null ID indicates defaults without a saved version.
{
"id": null,
"enabled": false,
"rate_bps": 200,
"max_depth": 3,
"cap_bps": 5000,
"effective_at": null
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/referral-policy.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/referral-policy.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/referral-policy.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Update Referral Policy
Submit the complete policy. Each update creates a new effective version; existing direct fees retain the policy effective when they were assigned.
partner_groups:writePath, query, and header parameters
No path, query, or additional header parameters.
Request Body
{
"enabled": true,
"rate_bps": 200,
"max_depth": 3,
"cap_bps": 5000
}enabledRequiredrate_bpsRequiredmax_depthRequiredcap_bpsRequiredResponses
New company policy version saved.
{
"id": "66bf22b01234567890abcdef",
"enabled": true,
"rate_bps": 200,
"max_depth": 3,
"cap_bps": 5000,
"effective_at": "2026-09-24T12:00:00+00:00"
}Invalid rate, depth, or cap.
{
"error": "Invalid referral rate."
}Request examples
curl --request PUT 'https://api.hub.donutwork.com/2026-02-01/partners/referral-policy.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"enabled": true,
"rate_bps": 200,
"max_depth": 3,
"cap_bps": 5000
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/referral-policy.json', {
method: 'PUT',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"enabled": true,
"rate_bps": 200,
"max_depth": 3,
"cap_bps": 5000
}),
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/referral-policy.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{
"enabled": true,
"rate_bps": 200,
"max_depth": 3,
"cap_bps": 5000
}',
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Retrieve Partner Profile
Fetch the comprehensive profile of a specific partner, including their fee structure, whitelist configuration, and association metadata.
partners:readPath, query, and header parameters
partnerIdRequiredResponses
Partner profile retrieved.
{
"id": "66b136b9...",
"name": "Apex Marketing Group",
"status": "active",
"tags": [
"tier-1",
"mfa-enabled"
],
"email": "channel@apex-marketing.com",
"legal_address": {
"city": "London",
"country": "GB",
"tax_number": "GB123..."
},
"portal_access_mode": "active",
"referrer_partner_id": "66bf22b01234567890abcdef"
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Attribution & Performance
Audit Attributed Customers
Retrieve a paginated list of all customers currently attributed to a specific partner. This is used to verify channel performance and revenue attribution.
partners:readPath, query, and header parameters
partnerIdRequiredResponses
Attributed customer list retrieved.
{
"entities": "PartnerCustomers",
"count": 142,
"elements": [
{
"id": "68d2...",
"company_name": "Acme Corp",
"email": "procurement@acme.com",
"status": "active"
}
]
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/customers.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/customers.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/customers.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Performance Analytics
Retrieve high-level performance metrics for a partner, including total attributed revenue and cumulative commissions earned.
partners:readPath, query, and header parameters
partnerIdRequiredResponses
Aggregated performance statistics retrieved.
{
"total_revenue": "45200.00",
"commission_earned": "4520.00",
"currency": "EUR"
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/stats.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/stats.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/stats.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Financial Settlement (Payouts)
Audit Commission Ledger (Fees)
Retrieve individual commission entries (fees) generated by attributed customer transactions. Direct fees have commission_kind: "direct"; upstream fees have commission_kind: "referral" and a one-based referral_level. Referral fees are calculated asynchronously after the direct fee is assigned, so they may not be visible immediately. Both types use the existing payout flow.
partners:readPath, query, and header parameters
partnerIdRequiredResponses
Commission ledger retrieved.
{
"entities": "PartnerFees",
"elements": [
{
"id": "fee_991",
"partner_id": "675d...",
"transaction_amount": 2500,
"fee_value": 15.5,
"commission_kind": "referral",
"referral_level": 1,
"status": "pending"
}
]
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/fees.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/fees.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/fees.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Create an Offline Payout
Consolidate available commission fees into a payout record. The partner supplies the actual invoice details, while invoice delivery and payment take place offline. This endpoint does not generate a self-billing or fiscal document.
partner_payouts:writePath, query, and header parameters
Idempotency-KeyRequest Body
{
"payout": {
"partner_id": "675d...",
"transactions": [
"fee_991",
"fee_992"
],
"invoice": {
"number": "INV-2026-001",
"date": "2026-03-01"
},
"vat_settings": {
"tax_id": "tax_vat_22"
}
}
}payout.partner_idRequiredpayout.transactionsRequiredpayout.invoice.numberRequiredpayout.invoice.dateRequiredpayout.vat_settings.tax_idRequiredResponses
Payout created and all selected fees reserved atomically.
{
"id": "pay_5521",
"total": 1250,
"subtotal_minor": 102459,
"tax_minor": 22541,
"total_minor": 125000,
"currency": "EUR",
"status": "pending"
}The idempotency key was reused with a different payload, the selected fees changed concurrently, or their currencies do not match the persisted company currency.
{
"error": "The idempotency key was used with a different payload."
}A selected fee has already been reserved or processed by another payout.
{
"error": "A selected fee is already assigned to a payout."
}Invoice data is invalid, a fee amount is invalid, or the configured minimum payout was not reached.
{
"error": "The selected fees do not reach the minimum payout amount."
}Request examples
curl --request POST 'https://api.hub.donutwork.com/2026-02-01/partners/payouts.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"payout": {
"partner_id": "675d...",
"transactions": [
"fee_991",
"fee_992"
],
"invoice": {
"number": "INV-2026-001",
"date": "2026-03-01"
},
"vat_settings": {
"tax_id": "tax_vat_22"
}
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/payouts.json', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"payout": {
"partner_id": "675d...",
"transactions": [
"fee_991",
"fee_992"
],
"invoice": {
"number": "INV-2026-001",
"date": "2026-03-01"
},
"vat_settings": {
"tax_id": "tax_vat_22"
}
}
}),
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/payouts.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{
"payout": {
"partner_id": "675d...",
"transactions": [
"fee_991",
"fee_992"
],
"invoice": {
"number": "INV-2026-001",
"date": "2026-03-01"
},
"vat_settings": {
"tax_id": "tax_vat_22"
}
}
}',
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
The server derives the currency and its configuration version from the company configuration. Mixed-currency requests are rejected. Replaying the same request with the same idempotency key returns the existing payout and never claims the same fee twice. After a payout is refused, use a new idempotency key when intentionally creating a new payout from the released fees.
Retrieve Payout Documentation (PDF)
Retrieve a base64-encoded operational payout report. It is an account statement for the payout and is not the partner invoice or a platform-generated fiscal document.
partner_payouts:readPath, query, and header parameters
partnerIdRequiredpayoutIdRequiredResponses
PDF documentation retrieved.
{
"stream": {
"type": "application/pdf",
"base64_content": "JVBER..."
}
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts/PAYOUTID/report.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts/PAYOUTID/report.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts/PAYOUTID/report.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Additional Partner Operations
Search Partner by External ID or Email
partners:readPath, query, and header parameters
externalIdemailResponses
Partner record retrieved.
{
"id": "675d...",
"name": "Apex Marketing Group",
"email": "channel@apex-marketing.com"
}Neither `externalId` nor `email` was provided.
{
"error": "A valid \"externalId\" or \"email\" must be specified in the request"
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/partner.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/partner.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/partner.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Update Partner Profile
partners:writePath, query, and header parameters
partnerIdRequiredRequest Body
{
"partner": {
"name": "Apex Marketing Group - Enterprise",
"email": "enterprise@apex-marketing.com",
"description": "Updated profile",
"external_id": "APEX_001",
"code": "APEXPROMO",
"partner_group_id": "66bf22b0...",
"referrer_partner_id": "66bf22b01234567890abcdef",
"tags": [
"tier-1",
"priority"
],
"address": {
"name": "Apex Marketing Group Ltd",
"tax_number": "GB123456789",
"country": "GB",
"city": "London",
"state": "",
"postal_code": "SW1A 1AA",
"address": "1 Example Street",
"email": "billing@apex-marketing.com",
"legal_email": "legal@apex-marketing.com"
}
}
}partner.namepartner.emailpartner.descriptionpartner.external_idpartner.codepartner.partner_group_idpartner.referrer_partner_idpartner.tagspartner.addressResponses
Partner updated.
{
"id": "675d...",
"name": "Apex Marketing Group - Enterprise",
"portal_access_mode": "active",
"referrer_partner_id": "66bf22b01234567890abcdef"
}Request examples
curl --request PUT 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"partner": {
"name": "Apex Marketing Group - Enterprise",
"email": "enterprise@apex-marketing.com",
"description": "Updated profile",
"external_id": "APEX_001",
"code": "APEXPROMO",
"partner_group_id": "66bf22b0...",
"referrer_partner_id": "66bf22b01234567890abcdef",
"tags": [
"tier-1",
"priority"
],
"address": {
"name": "Apex Marketing Group Ltd",
"tax_number": "GB123456789",
"country": "GB",
"city": "London",
"state": "",
"postal_code": "SW1A 1AA",
"address": "1 Example Street",
"email": "billing@apex-marketing.com",
"legal_email": "legal@apex-marketing.com"
}
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID.json', {
method: 'PUT',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"partner": {
"name": "Apex Marketing Group - Enterprise",
"email": "enterprise@apex-marketing.com",
"description": "Updated profile",
"external_id": "APEX_001",
"code": "APEXPROMO",
"partner_group_id": "66bf22b0...",
"referrer_partner_id": "66bf22b01234567890abcdef",
"tags": [
"tier-1",
"priority"
],
"address": {
"name": "Apex Marketing Group Ltd",
"tax_number": "GB123456789",
"country": "GB",
"city": "London",
"state": "",
"postal_code": "SW1A 1AA",
"address": "1 Example Street",
"email": "billing@apex-marketing.com",
"legal_email": "legal@apex-marketing.com"
}
}
}),
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{
"partner": {
"name": "Apex Marketing Group - Enterprise",
"email": "enterprise@apex-marketing.com",
"description": "Updated profile",
"external_id": "APEX_001",
"code": "APEXPROMO",
"partner_group_id": "66bf22b0...",
"referrer_partner_id": "66bf22b01234567890abcdef",
"tags": [
"tier-1",
"priority"
],
"address": {
"name": "Apex Marketing Group Ltd",
"tax_number": "GB123456789",
"country": "GB",
"city": "London",
"state": "",
"postal_code": "SW1A 1AA",
"address": "1 Example Street",
"email": "billing@apex-marketing.com",
"legal_email": "legal@apex-marketing.com"
}
}
}',
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
state and postal_code are optional. A billing profile needs at least one of vat_number or tax_number before the company can approve it. Updating Address data changes its canonical review hash, so any previous billing approval no longer applies to the modified data. The API never updates encrypted payment coordinates.
An invalid or cross-company referring partner, or an attempt to replace an assigned one, returns 422. A concurrent partner update may return 409; reload the profile before retrying.
portal_access_mode in the response reflects the company-controlled Portal policy. Supplying it in this update payload has no effect; a company administrator controls disabled, active, and read_only transitions.
Delete Partner
partners:writePath, query, and header parameters
partnerIdRequiredResponses
Partner deleted.
{
"id": "675d...",
"deleted": true
}Partner has still attached customers.
{
"error": "Unable to remove this partner. Customer attached to partner: ..."
}Request examples
curl --request DELETE 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID.json', {
method: 'DELETE',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Get Partner Fee by ID
partners:readPath, query, and header parameters
partnerIdRequiredfeeIdRequiredResponses
Partner fee entry retrieved.
{
"id": "fee_991",
"partner_id": "675d...",
"fee_value": 15.5,
"calculated_fee_minor": 1550,
"currency": "EUR",
"commission_kind": "referral",
"referral_level": 1,
"source_direct_fee_id": "fee_990",
"status": "pending"
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/fees/FEEID.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/fees/FEEID.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/fees/FEEID.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
List Global Payouts
partner_payouts:readPath, query, and header parameters
sizepageResponses
Paginated payout list across all partners.
{
"entities": "PartnerPayout",
"count": 3,
"elements": [
{
"id": "pay_5521",
"total": 1250,
"total_minor": 125000,
"currency": "EUR",
"status": "pending",
"partner_id": "675d..."
}
]
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/payouts.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/payouts.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/payouts.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
List Payouts for One Partner
partner_payouts:readPath, query, and header parameters
partnerIdRequiredsizepageResponses
Paginated payout list for the specified partner.
{
"entities": "PartnerPayout",
"count": 2,
"elements": [
{
"id": "pay_5521",
"total": 1250,
"total_minor": 125000,
"currency": "EUR",
"status": "pending",
"partner_id": "675d..."
}
]
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Get Payout by Partner and ID
partner_payouts:readPath, query, and header parameters
partnerIdRequiredpayoutIdRequiredResponses
Payout details retrieved.
{
"id": "pay_5521",
"partner_id": "675d...",
"status": "pending",
"total": 1250,
"total_minor": 125000,
"currency": "EUR",
"currency_config_version": 3
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts/PAYOUTID.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts/PAYOUTID.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts/PAYOUTID.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Get Payout by ID
partner_payouts:readPath, query, and header parameters
payoutIdRequiredResponses
Payout details retrieved.
{
"id": "pay_5521",
"partner_id": "675d...",
"status": "pending",
"total": 1250,
"total_minor": 125000,
"currency": "EUR",
"currency_config_version": 3
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/payouts/PAYOUTID.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/payouts/PAYOUTID.json', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/payouts/PAYOUTID.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Update Payout Status
partner_payouts:writePath, query, and header parameters
payoutIdRequiredRequest Body
{
"payout": {
"status": "paid",
"metadata": {
"reference": "BANK-TRX-2026-8891"
}
}
}payout.statusRequiredpayout.metadataResponses
Payout status updated and related fee entries synchronized.
{
"payout": {
"id": "pay_5521",
"status": "paid"
}
}Invalid status transition.
{
"error": "Paid payouts cannot be updated"
}Request examples
curl --request PUT 'https://api.hub.donutwork.com/2026-02-01/partners/payouts/PAYOUTID/status.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"payout": {
"status": "paid",
"metadata": {
"reference": "BANK-TRX-2026-8891"
}
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/payouts/PAYOUTID/status.json', {
method: 'PUT',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"payout": {
"status": "paid",
"metadata": {
"reference": "BANK-TRX-2026-8891"
}
}
}),
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.text();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/payouts/PAYOUTID/status.json');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{
"payout": {
"status": "paid",
"metadata": {
"reference": "BANK-TRX-2026-8891"
}
}
}',
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.
Setting a payout to refused releases all associated fees idempotently, making them available for a later payout. Setting it to paid marks the fees as paid and is terminal: a paid payout cannot return to pending or refused.
Stream Payout PDF
partner_payouts:readPath, query, and header parameters
partnerIdRequiredpayoutIdRequiredResponses
Inline PDF stream response.
{
"note": "Response is streamed as application/pdf."
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts/PAYOUTID/report.pdf' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts/PAYOUTID/report.pdf', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
},
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
const error = await response.text();
throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
: contentType.includes('application/json') ? await response.json()
: await response.arrayBuffer();$request = curl_init('https://api.hub.donutwork.com/2026-02-01/partners/PARTNERID/payouts/PAYOUTID/report.pdf');
curl_setopt_array($request, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
],
]);
$response = curl_exec($request);
if ($response === false) {
$error = curl_error($request);
curl_close($request);
throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
: (str_contains($contentType, 'application/json')
? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.