Overview
Webhooks let your server receive HTTP POST callbacks when events occur in SmartlyQ — like a job completing or a social post being published.
Setting up webhooks
- Go to the Developer Dashboard
- Navigate to the Webhooks tab
- Add an endpoint URL (must be HTTPS)
- Select the events you want to receive
All webhook payloads are JSON:
{
"event": "job.completed",
"timestamp": "2026-03-02T14:30:00Z",
"data": {
"job_id": "job_abc123",
"type": "article_generation",
"status": "completed",
"result": { ... }
}
}
Verifying signatures
Every delivery is signed. Two headers accompany each request:
X-SmartlyQ-Signature — t=<timestamp>,v1=<hmac>, where <hmac> is the HMAC-SHA256 of <timestamp>.<raw-body> keyed with your webhook secret.
X-SmartlyQ-Event — the event name (e.g. job.completed).
Compute the HMAC over the timestamp and the raw request body joined with a ., then compare it against v1:
const crypto = require("crypto");
// rawBody = the exact bytes of the request body (do not re-serialize the JSON)
function verifySignature(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((kv) => kv.split("="))
);
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(parts.v1),
Buffer.from(expected)
);
}
Always verify the signature before processing a webhook. Unverified payloads may be spoofed. The signing secret is shown once, when you add the endpoint in the dashboard.
Retry policy
If your endpoint returns a non-2xx status code, SmartlyQ retries delivery up to 5 times with exponential backoff — the wait doubles each attempt (≈2, 4, 8, 16, 32 minutes). Deliveries still failing after the final attempt are dropped.
Available events
| Event | Trigger |
|---|
job.completed | An async job finished successfully |
job.failed | An async job failed |
social.posted | A social media post was published |
social.failed | A social media post failed to publish |
chatbot.message | A chatbot received a visitor message |
wallet.low | Wallet balance dropped below threshold |