Webhooks

Webhooks let you subscribe to events on your PaperDB database. When an event fires, PaperDB sends an HTTP POST to your endpoint with a signed payload.

Creating a Webhook

curl -X POST https://api.paperdb.app/webhooks \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook",
    "events": ["document.created", "document.updated"],
    "collections": ["users", "orders"],
    "description": "Order notifications"
  }'

Supported Events

  • document.created — a new document was inserted.
  • document.updated — an existing document was patched.
  • document.deleted — a document was deleted.
  • * — wildcard, triggers on all events.

Verifying Signatures

Every delivery includes an X-PaperDB-Signature header — an HMAC SHA-256 hash of the raw request body using your webhook secret. Verify it before processing the payload.

The secret is shown once when you create the webhook. Use POST /webhooks/:id/rotate-secret to generate a new one. The secret is never returned in GET responses.

import { createHmac } from "crypto";

function verifyWebhook(
  rawBody: string,
  signature: string,
  secret: string
): boolean {
  const expected = "sha256=" + createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return signature === expected;
}

// In your Express/Hono handler:
app.post("/webhook", (req, res) => {
  const sig = req.headers["x-paperdb-signature"] as string;
  if (!verifyWebhook(req.rawBody, sig, process.env.WEBHOOK_SECRET!)) {
    return res.status(401).send("Invalid signature");
  }
  // process payload...
  res.sendStatus(200);
});

Or use the SDK helper which does this for you:

import { createWebhookHandler } from "paperdb";

const handler = createWebhookHandler(process.env.WEBHOOK_SECRET!, (payload, event) => {
  console.log("Event:", event, payload);
});

Delivery & Retries

Deliveries are queuedand processed asynchronously. If your server returns a non-2xx status or times out (30 s), PaperDB retries with exponential backoff — up to 5 attempts. You can also retry manually from the dashboard or via the API.

Managing Webhooks via SDK

// Create
const hook = await db.webhooks.create({
  url: "https://example.com/hook",
  events: ["document.created", "document.deleted"],
  collections: ["orders"]
});

// List / get
const hooks = await db.webhooks.list();
const hook = await db.webhooks.get(id);

// Update
await db.webhooks.update(id, { enabled: false });

// Send a test delivery
const delivery = await db.webhooks.test(id);

// View delivery history
const deliveries = await db.webhooks.getDeliveries(id);

// Retry a failed delivery
await db.webhooks.retryDelivery(id, deliveryId);

// Rotate secret
const { secret } = await db.webhooks.rotateSecret(id);

// Delete
await db.webhooks.delete(id);