Authentication
Secure your integration using API Key authentication.
Authentication and permissions
The Donutwork API uses API keys to authenticate requests. You can view and manage your API keys in the Donutwork Dashboard.
An API key identifies both the client and its company boundary. Keys can be restricted with resource scopes such as customers:read or customers:write; each endpoint displays the exact required scope.
Authorization Header
All API requests must include your API Key in the Authorization HTTP header as a Bearer token.
Authorization: Bearer <your_api_key>Use one key per application and environment. Store keys in a managed secret store, restrict production access, rotate on personnel or vendor changes, and revoke immediately if a value may have leaked. Never send a key in a query string.
Permission failures
When a scoped key lacks access, the API responds with 403 and identifies both the missing permission and the grants currently attached to the key:
{
"error": "insufficient_scope",
"required_permission": "customers:write",
"granted_permissions": ["customers:read"]
}Read operations use resource:read; create, update, and delete operations use the documented resource:write permission. A valid key is not sufficient when its scope is narrower than the route.
Obtaining an API Key
Donutwork is currently in a controlled rollout phase. Access is granted to partners and early adopters through the following process:
Request Access
Submit a request to the Donutwork team outlining your integration use case.
Review & Provisioning
Our team will review your requirements and provision the necessary environments.
Environment Keys
Upon approval, you will be provided with distinct keys for:
- Staging: For development, testing, and sandbox experimentation.
- Production: For live, customer-facing traffic.
Connectivity and Health Checks
Use these endpoints to validate your authentication credentials and monitor API availability.
System Ping
Verify the operational status of the Donutwork API environment.
ping:readPath, query, and header parameters
No path, query, or additional header parameters.
Responses
API is operational and responding.
{
"status": "success",
"env": "production",
"RRT": "0.08 ms"
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/ping.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/ping.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/ping.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.
Connection Metadata
Retrieve detailed information about your current connection, including client IP and protocol negotiation.
connection:readPath, query, and header parameters
No path, query, or additional header parameters.
Responses
Connection metadata retrieved successfully.
{
"client": {
"userAgent": "axios/1.6.0",
"ips": {
"ipv4": "192.168.1.1"
}
},
"endpoint": {
"apiVersion": "2026-02-01",
"env": "production",
"protocol": "HTTP3"
}
}Request examples
curl --request GET 'https://api.hub.donutwork.com/2026-02-01/connection.json' \
--header 'Authorization: Bearer YOUR_API_KEY'const response = await fetch('https://api.hub.donutwork.com/2026-02-01/connection.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/connection.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.