Webhooks
Webhooks
Receive async callbacks when document processing completes — no polling required.
Last updated: April 2026
How it works
When a job reaches a terminal state (done or failed), we POST a signed payload to your registered callback URL.
Register your webhook URL in the dashboard under Settings → Webhooks. One URL per environment is recommended.
Payload shape
JSON
{
"event": "job.completed",
"jobId": "job_abc123",
"fileStatus": "done",
"documentType": "invoice",
"timestamp": "2026-04-22T10:00:00.000Z"
}For failed events, an additional error field describes the failure reason.
Signature verification
Every webhook request includes an x-number7ai-signature header containing an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret.
Always verify the signature before trusting the payload. Reject requests with a missing or invalid signature.
TypeScript
import crypto from "crypto";
// Express example
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.headers["x-number7ai-signature"] as string;
const expected = crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET!)
.update(req.body) // raw Buffer — do NOT parse JSON first
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body.toString());
// handle event.event === "job.completed" …
res.sendStatus(200);
});Python
import hmac, hashlib
def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
# Flask example
@app.route("/webhook", methods=["POST"])
def webhook():
sig = request.headers.get("x-number7ai-signature", "")
if not verify_signature(request.data, sig, WEBHOOK_SECRET):
abort(401)
event = request.get_json(force=True)
# handle event ...
return "", 200Retry policy
- •We retry delivery up to 3 times with exponential backoff on non-2xx responses.
- •Return HTTP 200 as fast as possible — do heavy processing asynchronously.
- •Idempotency: use jobId to deduplicate; retries may deliver the same event more than once.