Preserve // Developer Platform
Webhooks
Real-time HTTP callbacks when events happen in your organization. Hand this page to your developers. Everything needed to integrate is here.
01
How it works
-
Register
A platform admin saves your endpoint URL in Preserve.
-
Event
Something happens, like a document being created.
-
Deliver
Preserve POSTs a JSON payload to every registered webhook.
-
Acknowledge
Your endpoint returns a 2xx. Done.
02
Setup
Webhooks are managed per organization by a platform admin. Each registration has three fields:
| Field | Description |
|---|---|
name |
Label included in every delivery so you can tell which registration fired. |
description |
Optional notes for your team. |
url |
Publicly reachable HTTPS endpoint Preserve will POST to. Plain HTTP is not accepted. |
03
Delivery
Transport
POST with Content-Type: application/json, sent asynchronously, typically within seconds.
Fan-out
Every registered webhook receives every event. Filter by event name on your side.
Respond fast
Return a 2xx within about 10 seconds; do heavy processing after acknowledging. Slower responses count as failed deliveries. Redirects are not followed.
Retries
Failed deliveries (non-2xx, timeout, or connection error) are retried with increasing backoff for about 4 hours, then dropped.
X-Preserve-Delivery header, resolve ordering from payload content rather than arrival order, and treat webhooks as notifications rather than a system of record.
04
Request format
Every delivery uses the same envelope.
hook.name identifies your registration;
event names which event fired, so you can branch on type;
payload carries the event-specific fields
documented under Events.
The X-Preserve-Delivery header carries a unique ID
for the delivery. Retries of the same event reuse the same ID, so deduplicate on it before processing.
{
"hook": { "name": "<your webhook name>" },
"event": "<event name>",
"payload": { … }
}
05
Events
Each event type has its own page with payload fields and example deliveries.
Fires when a document (file) is created. Folder creation does not fire.
Payload docs →Fires when a document's name or description changes. Folders and internal bookkeeping changes do not fire.
Payload docs →Fires when a certificate is issued to a user, e.g. on completing a qualification.
Payload docs →Fires when a user requalifies and their certificate is renewed with a new expiration date.
Payload docs →Fires when a certificate passes its expiration date and is marked expired.
Payload docs →More event types are on our roadmap.
06
Security
-
→
Use an unguessable URL: a long random token in the path, e.g.
/hooks/preserve/3f9a8c…, and reject mismatches. - → Endpoints must be HTTPS (enforced at registration). Payloads are unsigned, so transport encryption is your only in-transit protection.
-
→
Treat the payload as a notification. For anything sensitive, look the record up via its
id/url.
07
Example receiver
A minimal Node.js / Express endpoint: verify the token, acknowledge immediately, process asynchronously.
Tip: during development, ngrok or a webhook
inspection service lets you receive deliveries on a local machine.
const express = require("express");
const app = express();
app.use(express.json());
app.post("/hooks/preserve/:token", (req, res) => {
if (req.params.token !== process.env.PRESERVE_HOOK_TOKEN) {
return res.sendStatus(404);
}
const { hook, payload } = req.body;
const deliveryId = req.get("X-Preserve-Delivery");
res.sendStatus(200); // ack first
// skip if deliveryId was already processed, then
// ... enqueue your own processing here ...
});
app.listen(3000);