Skip to content

Webhooks

Register an HTTPS URL on a project. After a successful Data API write, Tefily POSTs a JSON payload to every matching webhook. Failed deliveries are retried with exponential backoff (up to five attempts) and never fail the original API call.

Configure webhooks in the console (Database → Automation → Webhooks) or with the admin API / MCP tools.

Empty events means all of these. Unknown names are rejected.

Event When
record.created Single document create
record.created_many Bulk POST array (one delivery, not one per row)
record.updated Single document update
record.deleted Single document delete
record.updated_many Bulk PATCH (one delivery, not one per row)
record.deleted_many Bulk collection DELETE
file.uploaded Upload session completed
file.deleted File deleted from a record
auth.registered End-user register
auth.logged_in End-user login
auth.password_reset.requested Reset email sent for an existing user
auth.password_reset.completed Password actually changed
webhook.ping Manual ping from the console or API

Reads (list / get / download) and schema-admin changes do not fire webhooks.

Internal collections _usage_events, _files, and _auth_tokens never emit record.* events. Auth methods emit auth.* instead of duplicating record.* for _users. Direct CRUD on _users still emits record.*.

collections is an allow-list of table names. Empty means every collection. Auth events and webhook.ping ignore this filter.

{
"id": "<deliveryId>",
"event": "record.updated",
"projectId": "...",
"collection": "posts",
"timestamp": "2026-09-15T18:00:00.000Z",
"actor": { "type": "project", "userId": "..." },
"data": { }
}

actor.type is project, platform, service, or anonymous.

data by event:

  • create / update: record (after). Update also includes previous.
  • delete: record (the deleted document).
  • bulk create: count and records.
  • bulk update / delete: filter, count, and patch on update-many.
  • file: fileId, fieldName, filename, contentType, size, recordId, plus record when available.
  • auth: user (password fields stripped). Login does not include the JWT.

If the UTF-8 JSON of data is larger than 100 × 1024 bytes, the whole data object is replaced by { "_truncated": true }, plus _id when data.record._id or data._id exists. Bulk events often have no single _id, so the replacement may be only { "_truncated": true }.

Header Value
Content-Type application/json
X-Kallax-Event Event name
X-Kallax-Delivery Delivery id
X-Kallax-Signature sha256=<hex> when you set a signing secret

X-Kallax-Signature is omitted when the secret is empty. The algorithm is HMAC-SHA256 over the raw body, hex-encoded, with the header value sha256=<hex>. rawBody must be the exact bytes received, not JSON.stringify(JSON.parse(body)).

import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyKallaxSignature(secret, rawBody, header) {
if (!header?.startsWith('sha256=')) return false;
const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
const got = header.slice('sha256='.length);
const a = Buffer.from(expected);
const b = Buffer.from(got);
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}

Success is HTTP 200–299. Any other status, or a thrown error, is a failure. A webhook failure never fails the original API call.

maxAttempts defaults to 5. Attempts 1–4 stay pending. Attempt 5 marks the delivery failed and clears nextRetryAt.

Backoff after a failure is min(3600, 2^attempt) seconds, with the attempt counted after it is incremented. The first failure waits 2 seconds, then 4, 8, and 16. The wait is capped at 3600 seconds.

The truncation rule above replaces the whole data payload. Stored response bodies in delivery history are cut to 2000 characters. That cut does not change the outbound payload.

A project can have at most 20 webhooks, on all plans. Create beyond the cap fails with A project may have at most 20 webhooks. See Usage and limits.

Requires a platform JWT or PAT with schema read (list) or schema update (create / update / delete / ping).

Method Path
GET / POST /api/projects/:projectId/webhooks
GET / PATCH / DELETE /api/projects/:projectId/webhooks/:id
GET /api/projects/:projectId/webhooks/:id/deliveries
POST /api/projects/:projectId/webhooks/:id/ping

The signing secret is returned once on create. Later list/get responses include hasSecret and secretLastFour only.

URLs are validated against SSRF (no loopback, private, or metadata IPs). Production requires HTTPS.

To send an email instead of an HTTP call, see Email triggers.

See Endpoints, Files, and End-user auth.