DonutworkHelp Center
Api ReferenceRevenue & Partners
Donutwork documentation

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.

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

Path, query, and header parameters

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

Responses

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.

GET
/2026-02-01/partners/groups/{groupId}.json
Required permissionpartner_groups:read

Path, query, and header parameters

groupIdstring· pathRequired
The unique identifier of the partner group.

Responses

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

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

Path, query, and header parameters

No path, query, or additional header parameters.

Request Body

JSON
{
  "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.namestringRequired
Partner group display name.
partnerGroup.rulesarrayRequired
Rules array. Must include one recurring fallback rule.
partnerGroup.descriptionstring
Optional group description.
partnerGroup.external_idstring
Optional external identifier.
partnerGroup.tagsstring
Comma-separated tags.
partnerGroup.defaultboolean
Mark this group as default for new partners.

Responses

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

PUT
/2026-02-01/partners/groups/{partnerGroupId}.json
Required permissionpartner_groups:write

Path, query, and header parameters

partnerGroupIdstring· pathRequired
Partner group identifier.

Request Body

JSON
{
  "partnerGroup": {
    "name": "Performance Agencies v2",
    "default": true,
    "rules": [
      {
        "type": "recurring",
        "percentage": 14
      }
    ]
  }
}
partnerGroup.namestring
Updated group name.
partnerGroup.descriptionstring
Updated description.
partnerGroup.external_idstring
Updated external identifier.
partnerGroup.tagsstring
Updated comma-separated tags.
partnerGroup.defaultboolean
Update default flag.
partnerGroup.rulesarray
Updated rules array; if provided, must include recurring fallback.

Responses

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

DELETE
/2026-02-01/partners/groups/{partnerGroupId}.json
Required permissionpartner_groups:write

Path, query, and header parameters

partnerGroupIdstring· pathRequired
Partner group identifier.

Responses

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

POST
/2026-02-01/partners/groups/simulate-rules.json
Required permissionpartner_groups:write

Path, query, and header parameters

No path, query, or additional header parameters.

Request Body

JSON
{
  "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.rulesarrayRequired
Rules set to simulate. Must include recurring fallback.
partnerGroupSimulation.simulationobjectRequired
Simulation context (charge amount, lifecycle and partner KPIs).

Responses

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 TypeDescriptionKey Parameters
new_customer_time_tierApplies a specific commission for the initial months of a customer's lifecycle.max_months, percentage
recurringThe baseline commission applied to all transactions when no other specific rule applies.percentage
partner_ltv_upgradeTriggers a commission percentage increase once a partner's total managed LTV exceeds a threshold.min_ltv_total, upgrade_percentage
charge_threshold_bonusProvides 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.

On this page