WorkloomDevelopers
v1Get an API key
Webhooks/Webhook endpoints

Webhook endpoints

Everything Settings › API and webhooks › Webhooks does, over the API with the webhooks:manage scope. A key acting for one person also needs that person’s permission to manage webhooks.

Scopes: webhooks:manage to change.

The webhook endpoint object

idstring

The endpoint’s id.

namestring

Its name.

urlstring

Where deliveries go. HTTPS, on the public internet.

eventsarray of string

The events it gets, or ["*"] for every event except the Activity log.

statusstring

Disabled endpoints get nothing. Workloom disables one after 20 failed attempts in a row. One of active, disabled.

disabled_reasonstring, nullable

Why it was turned off.

consecutive_failuresinteger, nullable

Failed attempts since the last success.

last_success_attimestamp, nullable

The last delivery your server accepted.

last_failure_attimestamp, nullable

The last failed attempt.

secretstring, nullable

The signing secret. Only on create, rotate and GET …/secret; null everywhere else.

workloom_versionstring

The dated version its deliveries are shaped in, set when it was added. Change it with PATCH to move it to a later one.

created_attimestamp

When it was added.

The webhook endpoint object
{
  "id": "66ff91a7e4b0a1b2c3d4ee01",
  "name": "Revenue alerts",
  "url": "https://example.com/hooks/workloom",
  "events": [
    "deal.won",
    "deal.lost"
  ],
  "status": "active",
  "disabled_reason": null,
  "consecutive_failures": 0,
  "last_success_at": "2026-10-07T09:12:44.000Z",
  "last_failure_at": null,
  "secret": null,
  "workloom_version": "2026-10-07",
  "created_at": "2026-10-01T09:00:00.000Z"
}

The delivery object

idstring

The delivery id, as sent in X-Workloom-Delivery. Every attempt at one delivery shares it.

event_typestring

The event.

event_idstring, nullable

The event, when it can be sent again.

attemptinteger, nullable

Which attempt this was, from 1.

okboolean

Whether your server answered with a 2xx.

status_codeinteger, nullable

What your server answered. Null when it did not answer.

errorstring, nullable

What went wrong, when something did.

duration_msinteger, nullable

How long it took.

created_attimestamp

When the attempt was made.

The delivery object
{
  "id": "whd_3f1c0d9a2b7e44c1a8d6b5f0",
  "event_type": "deal.won",
  "event_id": "evt_66ff5d63e4b0a1b2c3d4aa01",
  "attempt": 1,
  "ok": true,
  "status_code": 200,
  "error": null,
  "duration_ms": 142,
  "created_at": "2026-10-07T09:12:44.000Z"
}

Endpoints

POST/sandbox/events

Make an event happen (sandbox only)

In a developer sandbox (sandbox-api.useworkloom.com, a wl_sbx_ key) only: records an event nobody can cause through the API (a reply, an open, a click, a bounce, a meeting, a booking, a form, a call, a Tell, an unsubscribe) about one of your sandbox contacts, and every endpoint subscribed to it gets the delivery, signed and retried as always. The live API answers sandbox_only.

Scope: webhooks:manage
Body parameters
typestringRequired

Which event. The ones the API can cause itself (contact.created, deal.won…) are made the real way.

contact_idstringRequired

The contact it is about.

categorystring, nullable

reply.received: what kind of reply (interested, meeting, not_interested…). Default interested.

urlstring, nullable

email.clicked, booking.made, form.submitted: the page.

answersobject

form.submitted: what they entered, by field name.

outcomestring, nullable

call.logged: the outcome. Default connected.

titlestring, nullable

meeting.*: the meeting’s title; tell.fired: what happened.

start_attimestamp

meeting.*: when it starts. Default two days from now.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

409

duplicate, conflict or idempotency_key_in_use.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Make an event happen (sandbox only)
curl -X POST "https://api.useworkloom.com/api/public/v1/sandbox/events" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/sandbox/events", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.post(
    "https://api.useworkloom.com/api/public/v1/sandbox/events",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{}
201 Createdsample
GET/webhooks

List webhook endpoints

Scope: webhooks:manage
Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

List webhook endpoints
curl "https://api.useworkloom.com/api/public/v1/webhooks" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/webhooks",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": [
    {
      "id": "66ff91a7e4b0a1b2c3d4ee01",
      "name": "Revenue alerts",
      "url": "https://example.com/hooks/workloom",
      "events": [
        "deal.won",
        "deal.lost"
      ],
      "status": "active",
      "disabled_reason": null,
      "consecutive_failures": 0,
      "last_success_at": "2026-10-07T09:12:44.000Z",
      "last_failure_at": null,
      "secret": null,
      "workloom_version": "2026-10-07",
      "created_at": "2026-10-01T09:00:00.000Z"
    }
  ]
}
200 OKsample
POST/webhooks

Add a webhook endpoint

Adds an endpoint. The answer carries its signing secret, the only time it is sent unasked. The address must be HTTPS on the public internet.

Scope: webhooks:manageTakes Idempotency-Key
Header parameters
Idempotency-Keystring

Any unique string, up to 255 characters. Send the same key when you retry and the request runs once; the first answer comes back with Idempotent-Replayed: true. Kept 24 hours.

Body parameters
namestringRequired

A name you will recognise.

urlstringRequired

An HTTPS address on the public internet that accepts POST.

eventsarray of string

Event types from GET /events, or ["*"] for every event except the Activity log. Defaults to ["*"].

workloom_versionstring

The dated version to shape its deliveries in. Defaults to the latest.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

409

duplicate, conflict or idempotency_key_in_use.

422

idempotency_key_reused.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Add a webhook endpoint
curl -X POST "https://api.useworkloom.com/api/public/v1/webhooks" \
  -H "Authorization: Bearer $WORKLOOM_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "name": "Revenue alerts",
  "url": "https://example.com/hooks/workloom",
  "events": [
    "deal.won",
    "deal.lost"
  ]
}'
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID()
  },
  body: JSON.stringify({
    "name": "Revenue alerts",
    "url": "https://example.com/hooks/workloom",
    "events": [
      "deal.won",
      "deal.lost"
    ]
  })
});
const data = await res.json();
import os, uuid, requests

res = requests.post(
    "https://api.useworkloom.com/api/public/v1/webhooks",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
    json={
      "name": "Revenue alerts",
      "url": "https://example.com/hooks/workloom",
      "events": [
        "deal.won",
        "deal.lost"
      ]
    },
)
data = res.json()
Response
{
  "data": {
    "id": "66ff91a7e4b0a1b2c3d4ee01",
    "name": "Revenue alerts",
    "url": "https://example.com/hooks/workloom",
    "events": [
      "deal.won",
      "deal.lost"
    ],
    "status": "active",
    "disabled_reason": null,
    "consecutive_failures": 0,
    "last_success_at": "2026-10-07T09:12:44.000Z",
    "last_failure_at": null,
    "secret": null,
    "workloom_version": "2026-10-07",
    "created_at": "2026-10-01T09:00:00.000Z"
  },
  "livemode": true
}
201 Createdsample
GET/webhooks/{id}

Get a webhook endpoint

Scope: webhooks:manage
Path parameters
idstringRequired

The endpoint’s id.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Get a webhook endpoint
curl "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": {
    "id": "66ff91a7e4b0a1b2c3d4ee01",
    "name": "Revenue alerts",
    "url": "https://example.com/hooks/workloom",
    "events": [
      "deal.won",
      "deal.lost"
    ],
    "status": "active",
    "disabled_reason": null,
    "consecutive_failures": 0,
    "last_success_at": "2026-10-07T09:12:44.000Z",
    "last_failure_at": null,
    "secret": null,
    "workloom_version": "2026-10-07",
    "created_at": "2026-10-01T09:00:00.000Z"
  }
}
200 OKsample
PATCH/webhooks/{id}

Change a webhook endpoint

Scope: webhooks:manage
Path parameters
idstringRequired

The endpoint’s id.

Body parameters
namestring

A new name.

urlstring

A new address.

eventsarray of string

Replaces the events.

statusstring

Turn it off, or back on. Turning it on clears the failure count.

workloom_versionstring

Move its deliveries to another dated version.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

409

duplicate, conflict or idempotency_key_in_use.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Change a webhook endpoint
curl -X PATCH "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01" \
  -H "Authorization: Bearer $WORKLOOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "events": [
    "deal.won",
    "deal.lost",
    "contact.deleted"
  ]
}'
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "events": [
      "deal.won",
      "deal.lost",
      "contact.deleted"
    ]
  })
});
const data = await res.json();
import os, requests

res = requests.patch(
    "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
    json={
      "events": [
        "deal.won",
        "deal.lost",
        "contact.deleted"
      ]
    },
)
data = res.json()
Response
{
  "data": {
    "id": "66ff91a7e4b0a1b2c3d4ee01",
    "name": "Revenue alerts",
    "url": "https://example.com/hooks/workloom",
    "events": [
      "deal.won",
      "deal.lost"
    ],
    "status": "active",
    "disabled_reason": null,
    "consecutive_failures": 0,
    "last_success_at": "2026-10-07T09:12:44.000Z",
    "last_failure_at": null,
    "secret": null,
    "workloom_version": "2026-10-07",
    "created_at": "2026-10-01T09:00:00.000Z"
  },
  "livemode": true
}
200 OKsample
DELETE/webhooks/{id}

Delete a webhook endpoint

Scope: webhooks:manageDeletes
Path parameters
idstringRequired

The endpoint’s id.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

409

duplicate, conflict or idempotency_key_in_use.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Delete a webhook endpoint
curl -X DELETE "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.delete(
    "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{}
200 OKsample
POST/webhooks/{id}/rotate-secret

Rotate an endpoint’s secret

Makes a new signing secret and returns it. The old one stops at once, so update your server first or accept both for a moment.

Scope: webhooks:manage
Path parameters
idstringRequired

The endpoint’s id.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

409

duplicate, conflict or idempotency_key_in_use.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Rotate an endpoint’s secret
curl -X POST "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/rotate-secret" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/rotate-secret", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.post(
    "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/rotate-secret",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": {
    "id": "66ff91a7e4b0a1b2c3d4ee01",
    "name": "Revenue alerts",
    "url": "https://example.com/hooks/workloom",
    "events": [
      "deal.won",
      "deal.lost"
    ],
    "status": "active",
    "disabled_reason": null,
    "consecutive_failures": 0,
    "last_success_at": "2026-10-07T09:12:44.000Z",
    "last_failure_at": null,
    "secret": null,
    "workloom_version": "2026-10-07",
    "created_at": "2026-10-01T09:00:00.000Z"
  },
  "livemode": true
}
200 OKsample
GET/webhooks/{id}/secret

Show an endpoint’s secret

Scope: webhooks:manage
Path parameters
idstringRequired

The endpoint’s id.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Show an endpoint’s secret
curl "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/secret" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/secret", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/secret",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": {
    "secret": "whsec_Lr8Tg2Vb6Nq4Kk9Wd1Zp7a3c"
  }
}
200 OKsample
POST/webhooks/{id}/test

Send a test event

Sends webhook.test now, signed like any event, and answers with what your server answered.

Scope: webhooks:manage
Path parameters
idstringRequired

The endpoint’s id.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

409

duplicate, conflict or idempotency_key_in_use.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Send a test event
curl -X POST "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/test" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/test", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.post(
    "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/test",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": {
    "sent": true,
    "ok": true,
    "status_code": 200,
    "error": null
  },
  "livemode": true
}
200 OKsample
GET/webhooks/{id}/deliveries

List recent deliveries

The latest attempts to this endpoint, newest first, from the last 30 days.

Scope: webhooks:manage
Path parameters
idstringRequired

The endpoint’s id.

Query parameters
limitinteger

How many, 1 to 200. Default 50.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

List recent deliveries
curl "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/deliveries?limit=50" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/deliveries?limit=50", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/deliveries?limit=50",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": [
    {
      "id": "whd_3f1c0d9a2b7e44c1a8d6b5f0",
      "event_type": "deal.won",
      "event_id": "evt_66ff5d63e4b0a1b2c3d4aa01",
      "attempt": 1,
      "ok": true,
      "status_code": 200,
      "error": null,
      "duration_ms": 142,
      "created_at": "2026-10-07T09:12:44.000Z"
    }
  ]
}
200 OKsample
POST/webhooks/{id}/deliveries/{delivery_id}/resend

Send a delivery again

Sends one delivery again now, with the same id, reading the event as it is now. A test or an Activity log entry is sent once and cannot be resent.

Scope: webhooks:manage
Path parameters
idstringRequired

The endpoint’s id.

delivery_idstringRequired

The delivery’s id (whd_…).

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

409

duplicate, conflict or idempotency_key_in_use.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Send a delivery again
curl -X POST "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/deliveries/whd_3f1c0d9a2b7e44c1a8d6b5f0/resend" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/deliveries/whd_3f1c0d9a2b7e44c1a8d6b5f0/resend", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.post(
    "https://api.useworkloom.com/api/public/v1/webhooks/66ff91a7e4b0a1b2c3d4ee01/deliveries/whd_3f1c0d9a2b7e44c1a8d6b5f0/resend",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": {
    "sent": true,
    "ok": true,
    "status_code": 200,
    "error": null
  },
  "livemode": true
}
200 OKsample
Was this page useful?