Get started
Webhooks
Skip polling. Give a callback_url and we POST the finished search to you, signed so you can prove it came from LegiScore.
Turn it on
Add callback_url to a search, or once at the top of a batch. It must be https and carry no username or password. Without one, the webhook subscriptions configured on your workspace receive the same events.
Events
| Event | Sent when |
|---|---|
search.completed | A search reaches succeeded. |
search.failed | A search reaches failed or cancelled. |
{
"delivery_id": "dlv_7f3a...",
"event": "search.completed",
"api_version": "2026-04-26",
"occurred_at": "2026-10-05T10:03:12Z",
"data": {
"search": { "id": "SRCH-2026-A1B2C3D4", "status": "succeeded", "found": true, "...": "..." },
"links": {
"self": "/api/v1/search/SRCH-2026-A1B2C3D4",
"documents": ["/api/v1/search/SRCH-2026-A1B2C3D4/documents/result.pdf"]
}
}
}Links are paths, not signed URLs. Call them with your API key to download documents.
Verify the signature
| Header | Value |
|---|---|
X-LegiScore-Event | The event name. |
X-LegiScore-Delivery | Unique id for this delivery. Use it to de-duplicate. |
X-LegiScore-Timestamp | Unix time the delivery was signed. |
X-LegiScore-Signature | sha256= plus the hex HMAC-SHA256 of "<timestamp>.<raw body>", keyed with your webhook secret. |
import crypto from "node:crypto";
export function verify(rawBody, headers, secret) {
const timestamp = headers["x-legiscore-timestamp"];
const received = headers["x-legiscore-signature"]; // "sha256=<hex>"
const expected =
"sha256=" +
crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const a = Buffer.from(received);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}import hashlib
import hmac
def verify(raw_body: bytes, headers: dict, secret: str) -> bool:
timestamp = headers["X-LegiScore-Timestamp"]
received = headers["X-LegiScore-Signature"] # "sha256=<hex>"
signed = timestamp.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(received, expected)Verify against the raw body
Compute the HMAC over the exact bytes received, before any JSON parsing. Reject deliveries whose timestamp is more than a few minutes old.Delivery and retries
- Answer with any 2xx within 30 seconds to acknowledge.
- Anything else, or a timeout, is retried: after 60, 120, 240, 480 and 960 seconds (6 attempts in all).
- The same delivery can arrive twice. De-duplicate on
X-LegiScore-Delivery. - Webhooks are a convenience. Polling
GET /api/v1/search/{id}always gives the truth.