Webhooks
React the moment a refresh lands.
Register an endpoint and Quorel will POST to it the moment a dataset refresh completes. No polling. Available on Pro and Scale.
How it works
One webhook per dataset. When a refresh completes and a new version is created, Quorel sends a single POST to your registered URL with a JSON payload describing the event. The result is recorded in last_fired_at and last_status.
Expose a public endpoint
Your server needs a publicly accessible URL that accepts POST requests with a JSON body. It must return a 2xx status code within a reasonable timeout.
Register the webhook
Call POST /webhook/register with your dataset_id, endpoint URL, and an optional secret. One webhook per dataset — registering again replaces the existing one.
Verify the signature (recommended)
If you provided a secret, Quorel signs every payload with it. Verify the signature on your server before processing the event.
Respond with 2xx
Return any 2xx status code quickly. Quorel records the status in last_status. Non-2xx responses are noted but the webhook is not automatically disabled.
Endpoints
All three endpoints require session authentication — they are called from your dashboard or your own backend, not from your webhook receiver.
Register or update a webhook for a dataset. If a webhook already exists for this dataset, it is replaced. The secret is optional but recommended.
Auth: Session cookie
Request body
{
"dataset_id": 42,
"url": "https://your-server.com/webhook",
"secret": "your_signing_secret"
}Response
{ "ok": true }Remove the webhook registered for a dataset. The endpoint will no longer receive POST requests after a refresh.
Auth: Session cookie
Request body
{ "dataset_id": 42 }Response
{ "ok": true }Check whether a webhook exists for a dataset and inspect its status.
Auth: Session cookie
Response
{
"has_webhook": true,
"url": "https://your-server.com/webhook",
"has_secret": true,
"is_active": true,
"last_fired_at": "2026-06-01T03:12:44Z",
"last_status": 200,
"created_at": "2026-05-15T10:00:00Z"
}Registration fields
The dataset to attach the webhook to. Must be owned by the authenticated user.
The endpoint that will receive POST requests. Must be a valid http or https URL.
An optional string used to sign the payload. See the signature verification section below.
Payload
Quorel sends a single POST to your endpoint with Content-Type: application/json. The body looks like this:
{
"dataset_id": 42,
"dataset_name": "remote-jobs",
"version": 15,
"entity_count": 847,
"refreshed_at": "2026-06-01T03:12:44Z"
}The numeric ID of the dataset that refreshed.
The display name of the dataset.
The version number that was just created by the refresh.
The number of entities in the new version.
ISO 8601 timestamp of when the refresh completed.
Signature verification
If you registered a secret, Quorel includes an X-Quorel-Signature header on every request. It is a hex-encoded HMAC-SHA256 of the raw request body, keyed with your secret. Verify it before processing the payload.
Node.js
import crypto from "crypto";
function verifySignature(rawBody, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}Python
import hmac, hashlib
def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)Always use a constant-time comparison function to prevent timing attacks. Never compare strings with === or ==.
View response fields
Whether a webhook is registered for this dataset.
The registered endpoint URL.
Whether a signing secret is configured. The secret itself is never returned.
Whether the webhook is active. Webhooks are active by default on registration.
ISO 8601 timestamp of the last time the webhook was fired. Null if never fired.
The HTTP status code returned by your endpoint on the last attempt. Null if never fired.
ISO 8601 timestamp of when the webhook was registered.
If no webhook is registered, the view endpoint returns { "has_webhook": false }.
Plan availability
Free
Webhooks are not available. Register via the API will return 403.
Pro
Webhooks are available. One webhook per dataset.
Scale
Webhooks are available. One webhook per dataset.
See the full pricing page for a complete feature comparison.
Notes
One webhook per dataset
Registering a second webhook for the same dataset replaces the first. There is no queue of multiple endpoints.
No automatic retries
Quorel fires the webhook once and records the result. If your endpoint is down, you will not receive the event retroactively. Use the API to fetch the new version manually if needed.
Non-2xx responses are recorded, not fatal
If your endpoint returns 4xx or 5xx, the webhook is not disabled. The status is recorded in last_status and the next refresh will fire again.
Secret is write-only
The secret you provide is never returned by the view endpoint. has_secret tells you whether one is configured.
Registering replaces, not stacks
Calling POST /webhook/register when a webhook already exists updates the URL and secret in place. is_active is reset to true and last_fired_at is cleared.