DonutworkHelp Center
Api ReferenceRicavi e partner
Documentazione Donutwork

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.

GET
/2026-02-01/partners.json
Required permissionpartners:read

Path, query, and header parameters

sizeinteger· query
Maximum records per page (max 100).
pageinteger· query
Target page index.

Responses

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.

POST
/2026-02-01/partners.json
Required permissionpartners:write

Path, query, and header parameters

No path, query, or additional header parameters.

Request Body

JSON
{
  "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.namestringRequired
Legal entity name of the partner.
partner.emailstringRequired
Primary contact for financial and operational notifications.
partner.partner_group_idstring
The Partner Group ID for direct commission rules. If empty, the default group is used.
partner.referrer_partner_idstring
ID of an existing partner in the same company who introduced this partner. This relationship can be assigned only once.
partner.address.namestring
Legal or billing name. Defaults to the partner name when omitted.
partner.address.vat_numberstring
VAT or indirect-tax identifier, when applicable.
partner.address.tax_numberstring
National or business tax identifier, when applicable.
partner.address.countrystring
ISO 3166-1 alpha-2 country code. Partner profiles are not limited to Italy.
partner.address.statestring
State, province, or region. Optional for countries that do not use it.
partner.address.citystring
City or locality.
partner.address.postal_codestring
Postal code. Optional where it is not applicable.
partner.address.addressstring
Street address.
partner.address.emailstring
Administrative or billing email. Defaults to the partner email.
partner.address.legal_emailstring
Optional legal or certified-delivery email.

Responses

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

GET
/2026-02-01/partners/referral-policy.json
Required permissionpartner_groups:read

Path, 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.

PUT
/2026-02-01/partners/referral-policy.json
Required permissionpartner_groups:write

Path, query, and header parameters

No path, query, or additional header parameters.

Request Body

JSON
{
  "enabled": true,
  "rate_bps": 200,
  "max_depth": 3,
  "cap_bps": 5000
}
enabledbooleanRequired
Enable or disable new upstream commission calculations.
rate_bpsintegerRequired
Percentage of the preceding fee at each level, in basis points (0–10000).
max_depthintegerRequired
Maximum upstream partner levels (1–10).
cap_bpsintegerRequired
Maximum aggregate commission budget, in basis points of the original commissionable customer amount (1–5000).

Responses

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.

GET
/2026-02-01/partners/{partnerId}.json
Required permissionpartners:read

Path, query, and header parameters

partnerIdstring· pathRequired
The unique identifier of the partner.

Responses

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.

GET
/2026-02-01/partners/{partnerId}/customers.json
Required permissionpartners:read

Path, query, and header parameters

partnerIdstring· pathRequired
The partner identifier.

Responses

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.

GET
/2026-02-01/partners/{partnerId}/stats.json
Required permissionpartners:read

Path, query, and header parameters

partnerIdstring· pathRequired
The partner identifier.

Responses

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.

GET
/2026-02-01/partners/{partnerId}/fees.json
Required permissionpartners:read

Path, query, and header parameters

partnerIdstring· pathRequired
The partner identifier.

Responses

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.

POST
/2026-02-01/partners/payouts.json
Required permissionpartner_payouts:write

Path, query, and header parameters

Idempotency-Keystring· header
Recommended unique key (16-200 characters) for this exact payout payload. Reuse it only when retrying the same request. Requests without this header are also accepted.

Request Body

JSON
{
  "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_idstringRequired
The partner identifier.
payout.transactionsarrayRequired
Between 1 and 500 fee entry IDs. Every fee must belong to this company and partner, be available, and match the configured company currency.
payout.invoice.numberstringRequired
Number of the actual invoice supplied by the partner.
payout.invoice.datestringRequired
Invoice date in YYYY-MM-DD format.
payout.vat_settings.tax_idstringRequired
Company tax-profile ID used to calculate the payout tax snapshot.

Responses

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

The company currency has not been persisted in Partner Portal settings.

{
  "error": "The company currency must be persisted before financial operations are enabled."
}

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.

GET
/2026-02-01/partners/{partnerId}/payouts/{payoutId}/report.json
Required permissionpartner_payouts:read

Path, query, and header parameters

partnerIdstring· pathRequired
The partner identifier.
payoutIdstring· pathRequired
The payout identifier.

Responses

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

GET
/2026-02-01/partners/partner.json
Required permissionpartners:read

Path, query, and header parameters

externalIdstring· query
Partner external identifier.
emailstring· query
Partner email address.

Responses

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

PUT
/2026-02-01/partners/{partnerId}.json
Required permissionpartners:write

Path, query, and header parameters

partnerIdstring· pathRequired
Partner identifier.

Request Body

JSON
{
  "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.namestring
Updated partner legal name.
partner.emailstring
Updated partner contact email.
partner.descriptionstring
Updated profile description.
partner.external_idstring
Updated external identifier.
partner.codestring
Updated partner code.
partner.partner_group_idstring
Partner group assignment (empty string removes current group).
partner.referrer_partner_idstring
Assign an existing partner in the same company as introducer. Once assigned, it cannot be changed or cleared.
partner.tagsarray|string
Updated tags list.
partner.addressobject
Partial worldwide Address update. Supported fields: name, vat_number, tax_number, country, state, city, postal_code, address, email, and legal_email.

Responses

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

DELETE
/2026-02-01/partners/{partnerId}.json
Required permissionpartners:write

Path, query, and header parameters

partnerIdstring· pathRequired
Partner identifier.

Responses

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

GET
/2026-02-01/partners/{partnerId}/fees/{feeId}.json
Required permissionpartners:read

Path, query, and header parameters

partnerIdstring· pathRequired
Partner identifier.
feeIdstring· pathRequired
Fee entry identifier.

Responses

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

GET
/2026-02-01/partners/payouts.json
Required permissionpartner_payouts:read

Path, query, and header parameters

sizeinteger· query
Maximum records per page (1-100).
pageinteger· query
Page index (1-based).

Responses

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

GET
/2026-02-01/partners/{partnerId}/payouts.json
Required permissionpartner_payouts:read

Path, query, and header parameters

partnerIdstring· pathRequired
Partner identifier.
sizeinteger· query
Maximum records per page (1-100).
pageinteger· query
Page index (1-based).

Responses

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

GET
/2026-02-01/partners/{partnerId}/payouts/{payoutId}.json
Required permissionpartner_payouts:read

Path, query, and header parameters

partnerIdstring· pathRequired
Partner identifier.
payoutIdstring· pathRequired
Payout identifier.

Responses

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

GET
/2026-02-01/partners/payouts/{payoutId}.json
Required permissionpartner_payouts:read

Path, query, and header parameters

payoutIdstring· pathRequired
Payout identifier.

Responses

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

PUT
/2026-02-01/partners/payouts/{payoutId}/status.json
Required permissionpartner_payouts:write

Path, query, and header parameters

payoutIdstring· pathRequired
Payout identifier.

Request Body

JSON
{
  "payout": {
    "status": "paid",
    "metadata": {
      "reference": "BANK-TRX-2026-8891"
    }
  }
}
payout.statusstringRequired
Next payout status. Allowed values: pending, paid, refused. paid is terminal.
payout.metadataobject
Optional metadata merged into payout metadata.

Responses

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

GET
/2026-02-01/partners/{partnerId}/payouts/{payoutId}/report.pdf
Required permissionpartner_payouts:read

Path, query, and header parameters

partnerIdstring· pathRequired
Partner identifier.
payoutIdstring· pathRequired
Payout identifier.

Responses

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.

On this page