Skip to content

Webhooks ​

Webhooks send an HTTP POST to your URL when an event happens in your workspace.

Events ​

EventTriggered when
contact.created / contact.updated / contact.deletedA contact changes
campaign.started / campaign.paused / campaign.completedA campaign's status changes
email.sent / email.deliveredAn email is sent or delivered
email.opened / email.clickedA recipient opens an email or clicks a link in it
email.bounced / email.complained / email.unsubscribedA delivery fails, a recipient complains, or a recipient unsubscribes

Payload ​

http
POST /your-endpoint HTTP/1.1
Content-Type: application/json
X-Beeving-Event: email.opened
X-Beeving-Signature: 5d41402abc4b2a76b9719d911017c592...
json
{
  "event": "email.opened",
  "workspace_id": 4,
  "data": { /* event-specific */ }
}

Verify the signature ​

X-Beeving-Signature is the hex-encoded HMAC-SHA256 of the raw request body, keyed with your webhook's secret (whsec_…). Always verify it before you trust a payload.

js
import crypto from 'node:crypto'

function isValid(rawBody, signature, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}
php
$expected = hash_hmac('sha256', $request->getContent(), $secret);
$valid = hash_equals($expected, $request->header('X-Beeving-Signature'));

Delivery ​

  • Reply with a 2xx status within 10 seconds.
  • After 10 consecutive failures, the webhook is automatically disabled (is_active: false).
  • Delivery logs are kept for 30 days.

The webhook object ​

json
{
  "id": 1,
  "url": "https://example.com/hooks/beeving",
  "events": ["email.opened", "email.clicked"],
  "is_active": true,
  "last_triggered_at": "...",
  "failure_count": 0,
  "created_at": "...",
  "updated_at": "..."
}

The secret is returned only when you create the webhook and when you regenerate its secret.

Endpoints ​

MethodPathPermissionDescription
GET/webhookswebhooks:readList webhooks (paginated)
POST/webhookswebhooks:writeCreate a webhook
GET/webhooks/{id}webhooks:readGet a webhook
PUT/webhooks/{id}webhooks:writeUpdate a webhook (partial)
DELETE/webhooks/{id}webhooks:writeDelete a webhook. Returns 204.
POST/webhooks/{id}/testwebhooks:writeSend a test event
GET/webhooks/{id}/logswebhooks:readDelivery logs (paginated)
POST/webhooks/{id}/regenerate-secretwebhooks:writeRotate the signing secret

List webhooks ​

GET /webhooks

Returns webhooks from the token workspace. Results are ordered by created_at desc, then id desc, unless another sort is requested. The signing secret is never included.

QueryTypeDescription
pageintegerPage number, minimum 1, default 1.
per_pageintegerWebhooks per page, from 1 to 100, default 15.
searchstringPartial URL search. Every term must match; at most six terms are used.
is_activeboolean1 keeps active webhooks; 0 keeps inactive webhooks.
eventenumKeep webhooks subscribed to one supported event.
sortenumcreated_at, updated_at, url, is_active, last_triggered_at, or failure_count.
directionenumasc or desc. Default desc.

The response contains data, navigation URLs in links, and meta with current_page, last_page, per_page, total, and count for the current page.

Create a webhook ​

json
{
  "url": "https://example.com/hooks/beeving",
  "events": ["email.opened", "email.clicked"],
  "is_active": true
}
FieldRules
urlRequired. A public HTTPS URL, max 255 characters
eventsRequired. At least one event from the list above
is_activeboolean, default true

201: { "data": Webhook }. The response includes secret: store it now.

Test ​

POST /webhooks/{id}/test sends this payload:

json
{
  "event": "webhook.test",
  "workspace_id": 4,
  "data": { "message": "This is a test webhook from Beeving", "timestamp": "2026-04-01T08:00:00+00:00" }
}

The response reports { "success", "message", "status_code" }.

Logs ​

GET /webhooks/{id}/logs

Only logs belonging to the requested webhook in the token workspace are returned. Search matches event names only and never inspects stored payloads or responses.

QueryTypeDescription
pageintegerPage number, minimum 1, default 1.
per_pageintegerLogs per page, from 1 to 100, default 15.
searchstringPartial event-name search. Every term must match; at most six terms are used.
successboolean1 keeps successful deliveries; 0 keeps failed deliveries.
status_codeintegerExact HTTP status, from 0 to 599. Status 0 means the endpoint was not reached.
created_fromdateInclude logs created on or after this YYYY-MM-DD date.
created_todateInclude logs created on or before this YYYY-MM-DD date.
sortenumcreated_at, event, status_code, or success.
directionenumasc or desc. Default desc.
json
{
  "data": [
    { "id": 55, "event": "email.opened", "payload": {}, "status_code": 200, "response": "ok", "success": true, "created_at": "..." }
  ],
  "links": { "first": "...", "last": "...", "prev": null, "next": null },
  "meta": { "current_page": 1, "last_page": 1, "per_page": 15, "total": 1, "count": 1 }
}

Log resources expose only the recorded event, payload, delivery status, truncated response and timestamp. They never include the webhook signing secret.

Regenerate the secret ​

200: { "data": { "id": 1, "secret": "whsec_…" }, "message": "Secret regenerated successfully" }

The old secret stops working immediately.

API v1.0