DonutworkHelp Center
Api ReferenceStrumenti per sviluppatori
Documentazione Donutwork

Webhook API

Register, inspect, update, and remove webhook subscriptions for event-driven integrations.

Webhook API

Use webhooks to receive asynchronous notifications for supported platform events.


List Webhooks

GET
/2026-02-01/webhooks.json
Required permissionwebhooks:read

Path, query, and header parameters

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

Responses

Paginated list of webhook subscriptions.

{
  "entities": "Webhook",
  "count": 2,
  "per_page": 100,
  "pages": {
    "current": 1,
    "max": 1
  },
  "elements": [
    {
      "id": "wh_001",
      "name": "Billing Events",
      "description": "Receive invoice and payment updates",
      "url": "https://example.com/webhooks/billing",
      "events": [
        "Charge.succeeded"
      ],
      "token": "redacted"
    }
  ]
}

Request examples

curl --request GET 'https://api.hub.donutwork.com/2026-02-01/webhooks.json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/webhooks.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/webhooks.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 Webhook

POST
/2026-02-01/webhooks.json
Required permissionwebhooks:write

Path, query, and header parameters

No path, query, or additional header parameters.

Request Body

JSON
{
  "webhook": {
    "name": "Billing Events",
    "description": "Receive invoice and payment updates",
    "url": "https://example.com/webhooks/billing",
    "events": [
      "Charge.succeeded"
    ]
  }
}
webhook.namestringRequired
Display name of the webhook subscription.
webhook.urlstringRequired
HTTPS endpoint that receives webhook payloads.
webhook.eventsarray|stringRequired
Subscribed event names. A single event string is also accepted.
webhook.descriptionstring
Optional free-text description for internal use.

Responses

Webhook created successfully.

{
  "id": "wh_001",
  "name": "Billing Events",
  "description": "Receive invoice and payment updates",
  "url": "https://example.com/webhooks/billing",
  "events": [
    "Charge.succeeded"
  ],
  "token": "generated_webhook_token"
}

Name, url, or events missing; or TLS certificate validation failed.

{
  "error": "A valid \"url\" must be specified."
}

URL format invalid or unsupported event specified.

{
  "error": "Some events are not available"
}

Request examples

curl --request POST 'https://api.hub.donutwork.com/2026-02-01/webhooks.json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "webhook": {
    "name": "Billing Events",
    "description": "Receive invoice and payment updates",
    "url": "https://example.com/webhooks/billing",
    "events": [
      "Charge.succeeded"
    ]
  }
}'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/webhooks.json', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "webhook": {
      "name": "Billing Events",
      "description": "Receive invoice and payment updates",
      "url": "https://example.com/webhooks/billing",
      "events": [
        "Charge.succeeded"
      ]
    }
  }),
});
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/webhooks.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 => '{
  "webhook": {
    "name": "Billing Events",
    "description": "Receive invoice and payment updates",
    "url": "https://example.com/webhooks/billing",
    "events": [
      "Charge.succeeded"
    ]
  }
}',
]);
$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 Webhook

Update an existing subscription with webhooks:write. Provide only the fields to change; omitted fields and the existing delivery token are preserved. The request must contain only a webhook object, with at least one supported field, and the complete JSON body must fit within 64 KiB. company_id, token, IDs and unknown fields are rejected. The operation is scoped to the API key's company.

An updated URL must use HTTPS without credentials and resolve only to public Internet addresses. The server validates the certificate against the original hostname while connecting to a validated address; no webhook event is sent by the update. A repeated assignment of identical values succeeds.

PUT
/2026-02-01/webhooks/{webhookId}.json
Required permissionwebhooks:write

Path, query, and header parameters

webhookIdstring· pathRequired
Existing webhook ID, 24 hexadecimal characters.

Request Body

JSON
{
  "webhook": {
    "name": "Billing Notifications",
    "events": [
      "Charge.succeeded",
      "Charge.failed"
    ]
  }
}
webhook.namestring
Non-empty display name, maximum 255 bytes.
webhook.descriptionstring
Description, maximum 4096 bytes; an empty string clears it.
webhook.urlstring
HTTPS destination, maximum 2048 bytes, with a valid certificate and public IP addresses.
webhook.eventsarray|string
Non-empty list of supported company events, or one event string; replaces the subscription list.

Responses

Updated metadata; the delivery token is preserved and not returned.

{
  "id": "0123456789abcdef01234567",
  "name": "Billing Notifications",
  "description": "",
  "url": "https://example.com/webhooks/billing",
  "eventsCount": 2,
  "events": [
    "Charge.succeeded",
    "Charge.failed"
  ]
}

No webhook with this ID exists in the authenticated company.

{
  "error": "Webhook not found."
}

Invalid field, ID, URL or event subscription.

{
  "error": "Provide at least one supported webhook field"
}

The destination TLS certificate could not be validated.

{
  "error": "Webhook TLS certificate could not be validated"
}

Request examples

curl --request PUT 'https://api.hub.donutwork.com/2026-02-01/webhooks/WEBHOOKID.json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "webhook": {
    "name": "Billing Notifications",
    "events": [
      "Charge.succeeded",
      "Charge.failed"
    ]
  }
}'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/webhooks/WEBHOOKID.json', {
  method: 'PUT',
  headers: {
    Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "webhook": {
      "name": "Billing Notifications",
      "events": [
        "Charge.succeeded",
        "Charge.failed"
      ]
    }
  }),
});
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/webhooks/WEBHOOKID.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 => '{
  "webhook": {
    "name": "Billing Notifications",
    "events": [
      "Charge.succeeded",
      "Charge.failed"
    ]
  }
}',
]);
$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 Supported Webhook Types

GET
/2026-02-01/webhooks/types.json
Required permissionwebhooks:read

Path, query, and header parameters

No path, query, or additional header parameters.

Responses

List of available webhook event names for the company context.

[
  "Charge.succeeded",
  "Charge.failed",
  "Customer.subscription.past_due",
  "Customer.subscription.recovered",
  "Customer.subscription.cancelled",
  "Customer.subscription.reactivated"
]

Request examples

curl --request GET 'https://api.hub.donutwork.com/2026-02-01/webhooks/types.json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/webhooks/types.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/webhooks/types.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 Webhook

GET
/2026-02-01/webhooks/{webhookId}.json
Required permissionwebhooks:read

Path, query, and header parameters

webhookIdstring· pathRequired
Webhook unique identifier.

Responses

Webhook details.

{
  "id": "wh_001",
  "name": "Billing Events",
  "description": "Receive invoice and payment updates",
  "url": "https://example.com/webhooks/billing",
  "events": [
    "Charge.succeeded"
  ],
  "token": "redacted"
}

Webhook resource not found.

{
  "error": "Webhook not found."
}

Request examples

curl --request GET 'https://api.hub.donutwork.com/2026-02-01/webhooks/WEBHOOKID.json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/webhooks/WEBHOOKID.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/webhooks/WEBHOOKID.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.

Delete Webhook

DELETE
/2026-02-01/webhooks/{webhookId}.json
Required permissionwebhooks:write

Path, query, and header parameters

webhookIdstring· pathRequired
Webhook unique identifier.

Responses

Webhook deletion accepted.

{
  "id": "wh_001",
  "deleted": true
}

Webhook resource not found.

{
  "error": "Webhook not found. Unable to delete unknown resource"
}

Request examples

curl --request DELETE 'https://api.hub.donutwork.com/2026-02-01/webhooks/WEBHOOKID.json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/webhooks/WEBHOOKID.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/webhooks/WEBHOOKID.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.

On this page