Appwrite
Receive Appwrite storage file webhooks at files.events.webhook(), verified with the webhook's signature key.
Appwrite project webhooks report storage file events. The appwrite adapter reads the appwrite format.
Create a webhook in the console (Project → Settings → Webhooks) for the buckets.*.files.*.create and buckets.*.files.*.delete events, and copy its signature key.
import { createRouteHandler } from "files-sdk/next";
import { files } from "@/lib/files";
export const { POST } = createRouteHandler(
files.events.webhook({
verify: {
secret: process.env.APPWRITE_WEBHOOK_KEY!,
url: "https://app.example.com/api/storage-events",
},
})
);
Appwrite signs the webhook URL followed by the body (base64 HMAC-SHA1), so url must be exactly the URL configured in Appwrite, not one derived from the request. The signature has no timestamp; dedupe on event.id, which is Appwrite’s delivery id.
What happened to the file is in the X-Appwrite-Webhook-Events header, not the body, so Appwrite deliveries need the whole request: use webhook() or parse(request), not parse(body).
What maps to what
…files.<id>.create becomes created and …files.<id>.delete becomes deleted; updates (renames, permissions) are skipped. The key is the file’s $id, which is the key files-sdk/appwrite writes under. A project webhook can cover several buckets, so events() keeps only the adapter’s own bucket by default; pass events({ bucket: "<bucket id>" }) to pick another, or bucket: false for all of them.