API reference
Webhook endpoints
Create, list, pause, test and delete the URLs that receive job events, rotate their secrets, and read the signing secret for per-job webhooks.
Overview
A webhook endpoint is a URL on your server. When a job finishes, the API sends an event to every enabled endpoint of the account. An account can have up to 10 endpoints. Endpoints can also be managed in the dashboard.
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/webhook_endpoints | Create an endpoint |
| GET | /v1/webhook_endpoints | List endpoints |
| PATCH | /v1/webhook_endpoints/{id} | Pause or resume an endpoint |
| POST | /v1/webhook_endpoints/{id}/rotate_secret | Replace its secret |
| POST | /v1/webhook_endpoints/{id}/test | Send it a test event |
| DELETE | /v1/webhook_endpoints/{id} | Delete an endpoint |
| GET | /v1/webhook_endpoints/signing_secret | Get the account signing secret |
- Endpoints belong to the account, not to a key or a mode. An endpoint receives the events of live jobs and of test jobs. Each event says which with
livemode. - Each endpoint has its own secret, which signs the deliveries to that endpoint. It is shown once, when the endpoint is created, and once more each time it is rotated.
- A job can also name a
webhook_urlof its own. Deliveries to that URL are signed with a different secret: the account's signing secret, described below.
See Webhooks for how to receive and verify an event.
The webhook endpoint object
{
"id": "whk_R4tYp9LmXc2VbN7qWz0K",
"object": "webhook_endpoint",
"url": "https://example.com/hooks/smol",
"enabled": true,
"created_at": "2026-10-01T09:30:12.480Z"
}| Field | Type | Description |
|---|---|---|
| id | string | The endpoint id: whk_ followed by 20 letters and digits. |
| object | string | Always "webhook_endpoint". |
| url | string | The URL that receives events. |
| enabled | boolean | Whether events are sent to this endpoint. true for an endpoint you create. false while it is paused. |
| created_at | string | When the endpoint was created. |
Create an endpoint
Send a JSON body. A field that is not listed here is refused with bad_request.
| Field | Type | Description |
|---|---|---|
| url * | string | A public https URL. It follows the same URL rules as a job's URLs. |
curl -X POST "https://api.smolmac.com/v1/webhook_endpoints" \
-H "Authorization: Bearer $SMOL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/hooks/smol"}'The response has status 201. It is the endpoint object with one more field, secret.
{
"id": "whk_R4tYp9LmXc2VbN7qWz0K",
"object": "webhook_endpoint",
"url": "https://example.com/hooks/smol",
"enabled": true,
"created_at": "2026-10-01T09:30:12.480Z",
"secret": "whsec_h2Kd8LqT5xZc1VbN9mWp3RyA7sJf0GuE"
}The secret is whsec_ followed by 32 letters and digits. The same URL can be registered more than once. Each registration is a separate endpoint with its own secret and receives its own delivery.
List endpoints
Returns every endpoint of the account, newest first. The list is not paginated, so has_more is always false. Secrets are not included.
curl "https://api.smolmac.com/v1/webhook_endpoints" \
-H "Authorization: Bearer $SMOL_API_KEY"{
"object": "list",
"data": [
{
"id": "whk_R4tYp9LmXc2VbN7qWz0K",
"object": "webhook_endpoint",
"url": "https://example.com/hooks/smol",
"enabled": true,
"created_at": "2026-10-01T09:30:12.480Z"
}
],
"has_more": false
}Pause or resume an endpoint
Send a JSON body with one field. A field that is not listed here is refused with bad_request.
| Field | Type | Description |
|---|---|---|
| enabled * | boolean | false pauses deliveries to the endpoint. true resumes them. |
curl -X PATCH "https://api.smolmac.com/v1/webhook_endpoints/whk_R4tYp9LmXc2VbN7qWz0K" \
-H "Authorization: Bearer $SMOL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":false}'{
"id": "whk_R4tYp9LmXc2VbN7qWz0K",
"object": "webhook_endpoint",
"url": "https://example.com/hooks/smol",
"enabled": false,
"created_at": "2026-10-01T09:30:12.480Z"
}- A paused endpoint keeps its id, its URL and its secret.
- Jobs that finish while the endpoint is paused send it nothing, and those events are not sent later, when it is resumed.
- Deliveries that were already queued for a job that finished before the pause, including their retries, are still attempted.
- The URL of an endpoint cannot be changed. To use another URL, create a new endpoint and delete the old one.
Rotate the secret
Gives the endpoint a new secret. The request has no body. The response is the endpoint object with the new secret, which is shown only here.
curl -X POST "https://api.smolmac.com/v1/webhook_endpoints/whk_R4tYp9LmXc2VbN7qWz0K/rotate_secret" \
-H "Authorization: Bearer $SMOL_API_KEY"{
"id": "whk_R4tYp9LmXc2VbN7qWz0K",
"object": "webhook_endpoint",
"url": "https://example.com/hooks/smol",
"enabled": true,
"created_at": "2026-10-01T09:30:12.480Z",
"secret": "whsec_Lm3Xc7VbN1qWz9RtY5pKd2HsF8gJ0aEu"
}- Events for jobs that finish after the call are signed with the new secret. The old secret is not used for them, and there is no period in which both sign.
- A delivery that was queued before the call keeps the secret it was queued with, on every retry. Retries can run for about 21 hours.
- So have your receiver accept both secrets while you switch: deploy the new one next to the old, and remove the old one a day later.
- The account signing secret, which signs deliveries to a job's
webhook_url, cannot be rotated with this request.
Send a test event
Sends one event of type ping to the endpoint, signed with its secret, and answers with what the endpoint returned. Use it to check your signature verification without running a job. The request has no body.
curl -X POST "https://api.smolmac.com/v1/webhook_endpoints/whk_R4tYp9LmXc2VbN7qWz0K/test" \
-H "Authorization: Bearer $SMOL_API_KEY"{
"object": "webhook_test",
"event_id": "evt_Tq6Wn1YdK8sLc3VbX0mR",
"delivered": true,
"status": 204,
"duration_ms": 182
}| Field | Type | Description |
|---|---|---|
| object | string | Always "webhook_test". |
| event_id | string | The id of the ping event that was sent. |
| delivered | boolean | true when the endpoint answered with a status from 200 to 299. |
| status | integer | The HTTP status the endpoint returned. 0 if it did not answer within 10 seconds or could not be reached. |
| duration_ms | integer | How long the delivery took, in milliseconds. |
- The request itself answers
200whether or not the delivery worked. Readdelivered. - The event is sent once and is not retried. Redirects are not followed.
- The event's
livemodeis true when you call with a live key and false with a test key. - A paused endpoint can be tested. The test does not resume it.
Delete an endpoint
Deletes the endpoint. Jobs that finish from now on send it nothing. Deliveries that were already queued for a job that finished earlier, including their retries, are still attempted.
curl -X DELETE "https://api.smolmac.com/v1/webhook_endpoints/whk_R4tYp9LmXc2VbN7qWz0K" \
-H "Authorization: Bearer $SMOL_API_KEY"{
"id": "whk_R4tYp9LmXc2VbN7qWz0K",
"object": "webhook_endpoint",
"deleted": true
}Returns 404 not_found if the account has no endpoint with that id.
Get the signing secret
Returns the account's signing secret. It signs deliveries to a URL given in a job's webhook_url, when that URL is not one of your registered endpoints. There is one such secret per account. It is created the first time it is needed, and this request returns the same value every time after that.
curl "https://api.smolmac.com/v1/webhook_endpoints/signing_secret" \
-H "Authorization: Bearer $SMOL_API_KEY"{
"object": "webhook_signing_secret",
"secret": "whsec_Qe7Zr2MnB5vXc8LkJ1hGf4DsA9pWy0Tu"
}| A delivery to | Is signed with |
|---|---|
| A registered endpoint | The secret of that endpoint, returned when it was created or last rotated. |
A job's webhook_url that is not a registered endpoint | The account signing secret, returned by this request. |
A job's webhook_url that is the same as the URL of an enabled registered endpoint | The secret of that endpoint. The event is delivered once, not twice. |
Errors
| Code | Status | Endpoint | When |
|---|---|---|---|
bad_request | 400 | Create | The body is not a JSON object, has an unknown field, url is missing or is not a public https URL, or the account already has 10 endpoints. |
bad_request | 400 | Pause or resume, test | Pause or resume: the body has an unknown field, or enabled is not true or false. Test: the URL of the endpoint no longer meets the URL rules. |
not_found | 404 | Pause or resume, rotate, test, delete | No endpoint with that id on this account. |
missing_key, invalid_key, revoked_key | 401 | All | The API key is missing, wrong or revoked. |
no_payment_method, payment_required | 402 | All | A live key on an account with no plan, or with an unpaid invoice. |
account_suspended, ip_not_allowed | 403 | All | The account is suspended, or the key may not be used from this IP address. |