Taxes
Manage regional tax profiles, automate VAT calculations, and ensure billing compliance across global markets.
Taxes API
The Taxes API provides a centralized layer for managing tax profiles and regional tax logic. By defining tax profiles (such as VAT, GST, or Sales Tax), organizations can automate tax calculation during invoicing and subscription renewals, ensuring consistent compliance with local regulations.
Tax Profiles
List Tax Profiles
Retrieve a comprehensive directory of all tax profiles configured for your organization. These profiles act as the authoritative source for tax percentage calculations and must be referenced when provisiong new customers or products.
taxes:readPath, query, and header parameters
sizepageResponses
Tax profile directory successfully retrieved.
{
"entities": "Taxes",
"count": 5,
"per_page": 100,
"pages": {
"current": 1,
"max": 1
},
"elements": [
{
"id": "66a151b7...",
"name": "Standard EU VAT (22%)",
"percentage": 22,
"description": "Standard Value Added Tax for European Union member states."
}
]
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/taxes.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/taxes.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/taxes.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.
Technical Implementation
Integration Note: When orchestrating customer provisioning (via the CRM/Customer API), ensure the tax_id from these profiles is correctly mapped. This association is critical for accurate automated invoicing and financial reporting.
Tax Profile Lifecycle
Create Tax Profile
taxes:writePath, query, and header parameters
No path, query, or additional header parameters.
Request Body
{
"tax": {
"name": "Standard EU VAT (22%)",
"description": "EU default VAT profile",
"percentage": 22,
"default": false
}
}tax.nameRequiredtax.percentageRequiredtax.descriptiontax.defaultResponses
Tax profile created.
{
"status": "success",
"tax": {
"id": "tax_001",
"name": "Standard EU VAT (22%)",
"percentage": 22
}
}Request examples
curl --request POST 'https://api.hub.donutwork.com/2026-02-01/taxes.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"tax": {
"name": "Standard EU VAT (22%)",
"description": "EU default VAT profile",
"percentage": 22,
"default": false
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/taxes.json', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"tax": {
"name": "Standard EU VAT (22%)",
"description": "EU default VAT profile",
"percentage": 22,
"default": 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/taxes.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 => '{
"tax": {
"name": "Standard EU VAT (22%)",
"description": "EU default VAT profile",
"percentage": 22,
"default": 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.
Update Tax Profile
taxes:writePath, query, and header parameters
taxIdRequiredRequest Body
{
"tax": {
"name": "EU VAT 2026",
"description": "Updated profile metadata",
"percentage": 23,
"default": true
}
}tax.nametax.descriptiontax.percentagetax.defaultResponses
Tax profile updated.
{
"status": "success",
"tax": {
"id": "tax_001",
"name": "EU VAT 2026",
"percentage": 23
}
}Request examples
curl --request PUT 'https://api.hub.donutwork.com/2026-02-01/taxes/TAXID.json' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"tax": {
"name": "EU VAT 2026",
"description": "Updated profile metadata",
"percentage": 23,
"default": true
}
}'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/taxes/TAXID.json', {
method: 'PUT',
headers: {
Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"tax": {
"name": "EU VAT 2026",
"description": "Updated profile metadata",
"percentage": 23,
"default": true
}
}),
});
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/taxes/TAXID.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 => '{
"tax": {
"name": "EU VAT 2026",
"description": "Updated profile metadata",
"percentage": 23,
"default": true
}
}',
]);
$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 Tax Profile
taxes:writePath, query, and header parameters
taxIdRequiredResponses
Tax profile deleted.
{
"status": "success",
"tax": {
"id": "tax_001",
"deleted": true
}
}Tax profile not found.
{
"error": "This tax profile does not exists"
}Request examples
curl --request DELETE 'https://api.hub.donutwork.com/2026-02-01/taxes/TAXID.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/taxes/TAXID.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/taxes/TAXID.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.