Setup & Verification
Create webhook endpoints, store the signing secret, and verify the FileForms-Signature header.
Webhooks notify your systems when something happens to your orders: a filing changes status, or a document becomes available. FileForms sends two event types — filing.status_changed and document.uploaded — described in the event catalog.
Create an endpoint
Create endpoints via the API or in the dashboard under Settings → Developers → Webhook endpoints → Add Endpoint.
curl -X POST https://api.fileforms.com/v1/webhook-endpoints \
-H "x-api-key: sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/webhooks/fileforms",
"eventTypes": ["filing.status_changed", "document.uploaded"]
}'{
"id": "we_a1b2c3d4e5f6g7h8",
"url": "https://api.example.com/webhooks/fileforms",
"eventTypes": ["filing.status_changed", "document.uploaded"],
"createdAt": "2026-07-29T12:00:00.000Z",
"secret": "whsec_..."
}Endpoint URLs must be https:// and publicly resolvable — URLs that resolve to private or internal addresses are rejected.
The signing secret is returned exactly once
The whsec_ secret appears only in the create response (or the one-time
dialog in the dashboard). It cannot be retrieved later, and there is no
rotation endpoint — if you lose it or need to rotate, delete the endpoint and
create a new one. Note that deleting an endpoint also deletes its delivery
history.
Updating an endpoint (PATCH /webhook-endpoints/{id}) replaces eventTypes wholesale rather than merging, and keeps the existing secret.
Verify signatures
Every delivery is a POST with a JSON body and a FileForms-Signature header:
FileForms-Signature: t=1722268800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdt— Unix timestamp (seconds) of when the delivery was signedv1— hex-encoded HMAC-SHA256 of{t}.{raw request body}, keyed with your endpoint'swhsec_secret (the full secret string, prefix included)
Verify every delivery before acting on it:
import crypto from "node:crypto";
function verifyWebhookSignature(
rawBody: string,
header: string,
secret: string,
): boolean {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
const expectedBuf = Buffer.from(expected);
const actualBuf = Buffer.from(parts.v1 ?? "");
const isValid =
expectedBuf.length === actualBuf.length &&
crypto.timingSafeEqual(expectedBuf, actualBuf);
const isFresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return isValid && isFresh;
}Three rules that prevent almost every verification bug:
- Sign the raw body bytes. Compute the HMAC over the exact bytes you received — don't parse and re-serialize the JSON. With Express, use
express.raw({ type: 'application/json' })and passreq.body.toString(). - Compare in constant time (
crypto.timingSafeEqualabove). - Reject stale timestamps. Anything older than 5 minutes should be treated as a possible replay.
Respond with any 2xx status quickly — deliveries time out after 30 seconds, and redirects are treated as failures. Do heavy processing asynchronously after acknowledging.
Email notifications for the same events
The same activity also exists as organization-wide email notifications (Settings → Notifications): "Document uploads" and "Filing status changes", both on by default. The toggles control email only — webhook delivery is never affected.
- API integrations usually want these off once webhooks are live, so your users aren't emailed about events your systems already handle.
- Dashboard partners without an API integration usually want them on — email is how their users hear about filings and documents.