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.