Compute the signature against the raw request body, before your framework parses it as JSON. Re-serialized JSON will not byte-match what FolioReady signed, and verification will fail. In Express, mount express.raw({ type: "application/json" }) on the webhook route.
Webhooks
Get notified the moment something happens in a folio. Instead of polling the FolioReady API on a schedule, hook your own systems up directly: post to Slack when a client opens a folio, push files into your CRM when they upload them, or kick off a custom workflow when a folio is accepted.
Why use webhooks?
- Real-time integrations. A webhook fires within seconds of the underlying event — no polling, no delay.
- Just HTTP. A small JSON POST to your server. Any language, any framework, any host that can listen on a public URL.
- Three switches, nine events. Configure up to three webhooks (folio events, file events, notification events) and receive the events you care about.
Configure webhooks under Integrations → Webhooks in the manager UI.
The three webhooks
| Webhook | Events | Reference |
|---|---|---|
| Folio Event | folio.open, folio.review, folio.update, folio.accepted, folio.expired, folio.closed | Folio Events |
| File Event | file.added, file.removed | File Events |
| Notification Event | notification.sent | Notification Events |
Common envelope
Every webhook is an HTTP POST to the URL you configured, with a JSON body. The body has an event key plus one or more context objects:
{
"event": "folio.open",
"client": { "...": "..." },
"folio": { "...": "..." }
}
File events also include a folio_section_file object. Notification events include a notification object. See the per-kind reference pages for full payloads.
HTTP request
| Property | Value |
|---|---|
| Method |
POST |
| Body | JSON, UTF-8 |
| Timeout | 15 seconds |
Headers sent on every request:
| Header | Value |
|---|---|
Accept |
application/json |
Content-Type |
application/json |
User-Agent |
folioready (support@folioready.com) |
folioready-signature |
timestamp=<unix-seconds>;signature=<hex> |
Verifying the signature
Each webhook is signed with HMAC-SHA256 over "<timestamp>:<raw-body>". The result is base16 lowercase and goes in the folioready-signature header alongside the timestamp.
Signing key:
- If your company has an API key set, that key is used.
-
If no API key is set, the literal string
FOLIOREADYis used. This is a deliberate developer-friendly default so you can wire up a receiver and start sending without configuring anything. For production, set an API key on your account so signatures can only be produced by FolioReady.
A minimal Node.js verifier:
import crypto from "node:crypto";
// Verify a FolioReady webhook request.
// secret: your company API key (or "FOLIOREADY" if no API key is set).
// header: the value of the `folioready-signature` request header.
// body: the raw request body, BEFORE any JSON parsing.
function verifyFolioreadySignature({ secret, header, body }) {
const parts = Object.fromEntries(
header.split(";").map((p) => p.split("=").map((s) => s.trim()))
);
const { timestamp, signature } = parts;
if (!timestamp || !signature) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}:${body}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex")
);
}
Retries and failures
If the request fails, FolioReady retries:
- Maximum attempts: 6
- Backoff: 1m, 1m, 5m, 10m, 15m, 30m, 1h
-
Success: HTTP
200. Any other status (or a connection error / timeout) counts as a failure and triggers a retry.
After 6 failed attempts, the event is marked failed and discarded. After 5 or more failed events in a row, the webhook is marked suspended and FolioReady stops sending until a successful delivery clears the failure count — set up your receiver, then trigger a fresh event to clear the suspension.
Delivery log
Every webhook delivery attempt is logged. View the log under Integrations → Webhooks → Webhook Events in the manager UI. Each row shows:
-
Type — the event name (e.g.
folio.open). -
Status —
pending,sending,retry,complete,failed, orsuspended. - Created at — when the event fired.
Related
- Folio Events — folio lifecycle webhook payloads
- File Events — file upload/removal webhook payloads
- Notification Events — client notification webhook payloads