Webhooks
Webhooks let APULODI notify your servers when things happen — a file is
uploaded, replaced, copied, deleted, or an upload session completes. APULODI
POSTs the event to an endpoint you register; the request carries an
APULODI-Signature header so you can verify it really came from us.
Events
| Event | Fires when |
|---|---|
file.created | a file record is created (upload initiated) |
file.uploaded | upload completes and bytes are verified |
file.downloaded | a download URL is generated |
file.replaced | a replacement publishes a new version |
file.copied | a file is copied |
file.renamed | a file is renamed |
file.moved | a file is moved to another folder |
file.metadata_updated | metadata is replaced |
file.processed | an image variant finishes processing successfully |
file.processing_failed | an image variant fails to process |
file.deleted | a file is soft-deleted |
file.restored | a soft-deleted file is restored |
file.purged | the purge sweep removes an expired deleted file's object |
upload.initiated | a multipart upload session is created |
upload.completed | a multipart upload completes |
upload.aborted | a multipart upload is aborted |
Registering an endpoint with no events list subscribes it to all events.
Delivery
For every event, APULODI creates a delivery for each matching subscription
and attempts it immediately, retrying with exponential backoff
(APULODI_WEBHOOK_RETRY_DELAYS_MS, default 1s,5s,30s,2m). After the last
attempt the delivery is marked FAILED and can be redelivered manually.
Each request is:
POST <your-endpoint>
Content-Type: application/json
User-Agent: APULODI-Webhooks/1.0
APULODI-Signature: t=<unix-seconds>,v1=<hex-hmac-sha256>
{
"id": "evt_…",
"type": "file.uploaded",
"created_at": "2026-09-05T11:22:50.667Z",
"data": {
"projectId": "…",
"organizationId": "…",
"fileId": "file_…",
"size": 12345
}
}
APULODI expects a 2xx response within 10 seconds. Any other status or a
timeout counts as a failed attempt.
Verify the signature
Your endpoint should verify the APULODI-Signature header (HMAC-SHA256 of
<timestamp>.<body> using your signing secret) and reject requests older
than ~5 minutes. The SDK provides a helper that does this for you — see the
SDK webhooks guide.
Register an endpoint
POST /v1/webhooks
Authorization: Bearer $APULODI_API_KEY
Content-Type: application/json
curl -X POST https://api.apulodi.com/v1/webhooks \
-H "Authorization: Bearer $APULODI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/hooks/apulodi",
"events": ["file.uploaded", "file.deleted"]
}'
{
"data": {
"webhook": {
"id": "wh_…",
"url": "https://example.com/hooks/apulodi",
"events": ["file.uploaded", "file.deleted"],
"status": "ACTIVE",
"createdAt": "2026-09-05T11:22:50.667Z"
},
"secret": "whsec_…"
}
}
The secret is returned exactly once. It is an HMAC signing key shared
between APULODI and your endpoint — store it in your server-side secrets, and
never in client-side code.
Manage endpoints
GET /v1/webhooks // list (secrets never included)
GET /v1/webhooks/:id // a single endpoint
DELETE /v1/webhooks/:id // unregister
curl https://api.apulodi.com/v1/webhooks \
-H "Authorization: Bearer $APULODI_API_KEY"
Inspect deliveries
To debug a missed webhook, list recent deliveries for an endpoint:
GET /v1/webhooks/:id/deliveries
curl "https://api.apulodi.com/v1/webhooks/wh_…/deliveries?limit=25" \
-H "Authorization: Bearer $APULODI_API_KEY"
{
"data": [
{
"id": "dl_…",
"eventId": "evt_…",
"status": "FAILED",
"attempts": 4,
"responseStatus": 500,
"lastError": "endpoint returned HTTP 500",
"deliveredAt": null,
"createdAt": "2026-09-05T11:22:50.667Z",
"event": { "type": "file.uploaded", "createdAt": "2026-09-05T11:22:50.667Z" }
}
]
}
Re-queue a failed (or pending) delivery immediately:
POST /v1/webhooks/:id/deliveries/:deliveryId/redeliver
Errors
| Status | Code | Why |
|---|---|---|
404 | WEBHOOK_NOT_FOUND | No endpoint with that id in your project |
404 | DELIVERY_NOT_FOUND | No delivery with that id for the endpoint |
403 | WEBHOOK_LIMIT_REACHED | A project is capped at 20 endpoints |
400 | VALIDATION_ERROR | Invalid URL or unknown event type |