Webhooks (SDK)
The SDK mirrors the /v1/webhooks API with typed
methods and ships a signature verifier for your receiving endpoint.
Register an endpoint
ts
import { Apulodi } from "@apulodi/sdk";
const apulodi = new Apulodi({
apiKey: process.env.APULODI_API_KEY!,
});
const { webhook, secret } = await apulodi.webhooks.create({
url: "https://example.com/hooks/apulodi",
events: ["file.uploaded", "file.deleted"], // omit for ALL events
});
python
import os
from apulodi import Apulodi
apulodi = Apulodi(api_key=os.environ["APULODI_API_KEY"])
result = apulodi.webhooks.create(
"https://example.com/hooks/apulodi",
events=["file.uploaded", "file.deleted"], # omit for ALL events
)
secret = result["secret"]
secret is shown exactly once — save it somewhere secure (e.g. your
infrastructure's secrets manager). It is the HMAC key used to verify
deliveries; it never appears again in API responses.
Manage endpoints
ts
const webhooks = await apulodi.webhooks.list();
const one = await apulodi.webhooks.get("wh_…");
await apulodi.webhooks.delete("wh_…");
python
webhooks = apulodi.webhooks.list()
one = apulodi.webhooks.get("wh_…")
apulodi.webhooks.delete("wh_…")
Verify deliveries in your endpoint
Every delivery arrives with an APULODI-Signature header of the form
t=<unix-seconds>,v1=<hex-hmac-sha256>. Verify it before trusting the
payload (constant-time, with a 5-minute replay window):
ts
import { Apulodi } from "@apulodi/sdk";
// Inside your route handler (Next.js server route shown):
export async function POST(request: Request) {
const rawBody = await request.text();
const signature = request.headers.get("APULODI-Signature") ?? "";
const secret = process.env.APULODI_WEBHOOK_SECRET!; // the whsec_… you saved
const valid = Apulodi.webhooks.verify(secret, rawBody, signature);
if (!valid) {
return new Response("invalid signature", { status: 401 });
}
const event = JSON.parse(rawBody);
// event.type === "file.uploaded", event.data.fileId, …
return new Response("ok", { status: 200 });
}
python
import json
import os
from apulodi import verify_webhook_signature
# Inside your route handler (Flask shown):
@app.post("/webhooks/apulodi")
def apulodi_webhook():
raw = request.get_data() # RAW body — the signature covers exact bytes
signature = request.headers.get("APULODI-Signature", "")
secret = os.environ.get("APULODI_WEBHOOK_SECRET", "") # the whsec_… you saved
if not verify_webhook_signature(secret, raw.decode("utf-8"), signature):
return {"error": "invalid signature"}, 401
event = json.loads(raw)
# event["type"] == "file.uploaded", event["data"]["file"]["id"], …
return {"ok": True}
Respond 2xx quickly (APULODI times out at 10s and retries on failure).
Inspect & redeliver
ts
const deliveries = await apulodi.webhooks.deliveries("wh_…", { limit: 25 });
// deliveries[0].status === "FAILED", .attempts, .lastError, …
await apulodi.webhooks.redeliver("wh_…", "dl_…");
python
deliveries = apulodi.webhooks.deliveries("wh_…", limit=25)
# deliveries[0]["status"] == "FAILED", ["attempts"], ["lastError"], …
apulodi.webhooks.redeliver("wh_…", "dl_…")
Security
- Verify every signature before processing. Reject expired timestamps.
- Keep the signing secret server-side — never ship it to browsers.
- APULODI never sends your API key, the signing secret, or any other secret in webhook payloads.