Partner Groups
Define classification tiers for partners and manage automated rules for commissions and performance-based incentives.
Partner Groups API
Partner Groups provide an automated logic layer for categorizing partners and enforcing organization-wide distribution policies. These groups define rules that govern revenue sharing percentages, time-based commission tiers, and performance-driven upgrades based on total Lifetime Value (LTV) or charge volume.
Group Inventory
List Partner Groups
Retrieve a paginated directory of all defined Partner Groups. This directory contains the high-level metadata and administrative identifiers for each group.
partner_groups:readPath, query, and header parameters
sizepageResponses
Partner group directory successfully retrieved.
{
"entities": "PartnerGroup",
"count": 4,
"per_page": 100,
"pages": {
"current": 1,
"max": 1
},
"elements": [
{
"id": "68f76721...",
"name": "Standard Managed Agency",
"external_id": "P-GROUP-ADVANCED",
"default": true
}
]
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/groups.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/groups.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/groups.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.
Retrieve Group Configuration & Rules
Fetch the comprehensive configuration for a specific governance group, including the active rules engine parameters that define commission logic.
partner_groups:readPath, query, and header parameters
groupIdRequiredResponses
Group configuration and rules engine definition retrieved.
{
"id": "68f76721...",
"name": "Standard Managed Agency",
"external_id": "P-GROUP-ADVANCED",
"rules": [
{
"type": "new_customer_time_tier",
"max_months": 12,
"percentage": 50,
"description": "Introductory 12-month high-incentive tier."
},
{
"type": "recurring",
"percentage": 15,
"description": "Standard recurring commission (post-time-tier)."
},
{
"type": "partner_ltv_upgrade",
"min_ltv_total": 10000,
"upgrade_percentage": 5,
"description": "LTV Performance Bonus (Threshold: 10,000€)."
}
],
"tags": "managed,high-volume",
"description": "Default group for high-performance managed agencies."
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/partners/groups/GROUPID.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/groups/GROUPID.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/groups/GROUPID.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.
Group Lifecycle Management
Create Partner Group
partner_groups:writePath, query, and header parameters
No path, query, or additional header parameters.
Request Body
{
"partnerGroup": {
"name": "Performance Agencies",
"description": "Tier for high-volume agencies",
"external_id": "P-GROUP-PERF",
"tags": "managed,performance",
"default": false,
"rules": [
{
"type": "new_customer_time_tier",
"max_months": 6,
"percentage": 30
},
{
"type": "recurring",
"percentage": 12
}
]
}
}partnerGroup.nameRequiredpartnerGroup.rulesRequiredpartnerGroup.descriptionpartnerGroup.external_idpartnerGroup.tagspartnerGroup.defaultResponses
Partner group created.
{
"group": {
"id": "68f76721..."
}
}Request examples
curl --request POST 'https://api.hub.donutwork.com/2026-02-01/partners/groups.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"partnerGroup": {
"name": "Performance Agencies",
"description": "Tier for high-volume agencies",
"external_id": "P-GROUP-PERF",
"tags": "managed,performance",
"default": false,
"rules": [
{
"type": "new_customer_time_tier",
"max_months": 6,
"percentage": 30
},
{
"type": "recurring",
"percentage": 12
}
]
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/groups.json', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"partnerGroup": {
"name": "Performance Agencies",
"description": "Tier for high-volume agencies",
"external_id": "P-GROUP-PERF",
"tags": "managed,performance",
"default": false,
"rules": [
{
"type": "new_customer_time_tier",
"max_months": 6,
"percentage": 30
},
{
"type": "recurring",
"percentage": 12
}
]
}
}),
});
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/groups.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 => '{
"partnerGroup": {
"name": "Performance Agencies",
"description": "Tier for high-volume agencies",
"external_id": "P-GROUP-PERF",
"tags": "managed,performance",
"default": false,
"rules": [
{
"type": "new_customer_time_tier",
"max_months": 6,
"percentage": 30
},
{
"type": "recurring",
"percentage": 12
}
]
}
}',
]);
$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 Group
partner_groups:writePath, query, and header parameters
partnerGroupIdRequiredRequest Body
{
"partnerGroup": {
"name": "Performance Agencies v2",
"default": true,
"rules": [
{
"type": "recurring",
"percentage": 14
}
]
}
}partnerGroup.namepartnerGroup.descriptionpartnerGroup.external_idpartnerGroup.tagspartnerGroup.defaultpartnerGroup.rulesResponses
Partner group updated.
{
"group": {
"id": "68f76721..."
}
}Request examples
curl --request PUT 'https://api.hub.donutwork.com/2026-02-01/partners/groups/PARTNERGROUPID.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"partnerGroup": {
"name": "Performance Agencies v2",
"default": true,
"rules": [
{
"type": "recurring",
"percentage": 14
}
]
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/groups/PARTNERGROUPID.json', {
method: 'PUT',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"partnerGroup": {
"name": "Performance Agencies v2",
"default": true,
"rules": [
{
"type": "recurring",
"percentage": 14
}
]
}
}),
});
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/groups/PARTNERGROUPID.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 => '{
"partnerGroup": {
"name": "Performance Agencies v2",
"default": true,
"rules": [
{
"type": "recurring",
"percentage": 14
}
]
}
}',
]);
$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.
Delete Partner Group
partner_groups:writePath, query, and header parameters
partnerGroupIdRequiredResponses
Partner group deleted.
{
"group": {
"id": "68f76721...",
"deleted": true
}
}Request examples
curl --request DELETE 'https://api.hub.donutwork.com/2026-02-01/partners/groups/PARTNERGROUPID.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/groups/PARTNERGROUPID.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/groups/PARTNERGROUPID.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.
Simulate Rules
partner_groups:writePath, query, and header parameters
No path, query, or additional header parameters.
Request Body
{
"partnerGroupSimulation": {
"rules": [
{
"type": "new_customer_time_tier",
"max_months": 3,
"percentage": 25
},
{
"type": "recurring",
"percentage": 10
}
],
"simulation": {
"charge_amount": 299.9,
"customer_months": 2,
"partner_ltv": 4500,
"qualified_customers": 12,
"first_paid_customer_bootstrap": false
}
}
}partnerGroupSimulation.rulesRequiredpartnerGroupSimulation.simulationRequiredResponses
Rules simulation completed.
{
"status": "success",
"result": {
"final_fee": 74.98
}
}Request examples
curl --request POST 'https://api.hub.donutwork.com/2026-02-01/partners/groups/simulate-rules.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"partnerGroupSimulation": {
"rules": [
{
"type": "new_customer_time_tier",
"max_months": 3,
"percentage": 25
},
{
"type": "recurring",
"percentage": 10
}
],
"simulation": {
"charge_amount": 299.9,
"customer_months": 2,
"partner_ltv": 4500,
"qualified_customers": 12,
"first_paid_customer_bootstrap": false
}
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/partners/groups/simulate-rules.json', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"partnerGroupSimulation": {
"rules": [
{
"type": "new_customer_time_tier",
"max_months": 3,
"percentage": 25
},
{
"type": "recurring",
"percentage": 10
}
],
"simulation": {
"charge_amount": 299.9,
"customer_months": 2,
"partner_ltv": 4500,
"qualified_customers": 12,
"first_paid_customer_bootstrap": false
}
}
}),
});
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/groups/simulate-rules.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 => '{
"partnerGroupSimulation": {
"rules": [
{
"type": "new_customer_time_tier",
"max_months": 3,
"percentage": 25
},
{
"type": "recurring",
"percentage": 10
}
],
"simulation": {
"charge_amount": 299.9,
"customer_months": 2,
"partner_ltv": 4500,
"qualified_customers": 12,
"first_paid_customer_bootstrap": false
}
}
}',
]);
$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 Rules Engine
Partner groups are powered by a deterministic rules engine that evaluates transactions in real-time. The following rule types are supported:
Rule Type Definitions
| Rule Type | Description | Key Parameters |
|---|---|---|
new_customer_time_tier | Applies a specific commission for the initial months of a customer's lifecycle. | max_months, percentage |
recurring | The baseline commission applied to all transactions when no other specific rule applies. | percentage |
partner_ltv_upgrade | Triggers a commission percentage increase once a partner's total managed LTV exceeds a threshold. | min_ltv_total, upgrade_percentage |
charge_threshold_bonus | Provides a one-time bonus for transactions exceeding a specific amount. | min_charge_total, bonus |
Evaluation Hierarchy: Rules are evaluated based on the customer's acquisition date and the partner's historical performance. A recurring rule is required for all groups as a fallback mechanism.