Webhooks
Get notified, and verify the signature.
BasiX POSTs a signed JSON body to your HTTPS endpoint when something happens. Create endpoints in Dashboard → Webhooks and pick the events. Each endpoint has a secret starting with whsec_.
Events:
file.uploaded: A file landed — whatever sent it: dashboard, API key or presigned link.file.deleted: A file went to the trash. It is still recoverable at this point.import.finished: A zip import finished, successfully or not.quota.warning: Storage crossed 80%, then 90%, then 95%. Once per threshold.log.alert: A log alert rule fired. Sent only to the endpoints that rule targets.ping: Sent only when you press Test.
Every delivery uses the same envelope:
json
{
"id": "clx8f2...",
"event": "file.uploaded",
"createdAt": "2026-10-25T14:03:11.204Z",
"account": "a7k2m9x4",
"data": {
"id": "k7x2m9q4pz1a",
"path": "avatars/john.png",
"url": "https://cdn.basix.media/a7k2m9x4/avatars/john.png",
"mime": "image/png",
"sizeBytes": 15320,
"sha256": "...",
"originalName": "john.png",
"replaced": false
}
}data per event:
file.uploaded:id,path,url,mime,sizeBytes,sha256,originalName,replaced.file.deleted:id,path,url,mime,sizeBytes.import.finished:id,fileName,prefix,status,total,extracted,skipped,refused,bytes,error.quota.warning:threshold(80, 90 or 95),percent,usedBytes,quotaBytes.log.alert:rule(id,name),count,windowMinutes,events(up to 10).
Headers:
text
X-BasiX-Event: file.uploaded
X-BasiX-Delivery: clx8f2...
X-BasiX-Attempt: 1
X-BasiX-Signature: t=1790000000,v1=<hex>
Verify the signature. v1 is the hex HMAC-SHA256 of <t>.<raw body> with your secret. Use the raw body, before any JSON parsing. Reject old timestamps.
import crypto from 'node:crypto'
import express from 'express'
const app = express()
app.post('/basix', express.raw({ type: 'application/json' }), (req, res) => {
const parts = Object.fromEntries(req.get('X-BasiX-Signature').split(',').map((p) => p.split('=')))
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return res.sendStatus(400)
const expected = crypto
.createHmac('sha256', process.env.BASIX_WEBHOOK_SECRET)
.update(`${parts.t}.${req.body}`)
.digest()
const given = Buffer.from(parts.v1, 'hex')
if (given.length !== expected.length || !crypto.timingSafeEqual(given, expected)) return res.sendStatus(400)
res.sendStatus(200)
handle(JSON.parse(req.body))
})
Delivery rules.
- Answer
2xxwithin 5 seconds. Do the work after answering. - Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 6 h. A
4xxother than429is not retried. - After 20 deliveries that ran out of attempts, the endpoint is disabled. The dashboard shows it.
- The same delivery can arrive twice. Use
X-BasiX-Delivery(stable across retries) to ignore duplicates. - The URL must be HTTPS with a valid certificate, on port 443 or 8443, on a public address. Redirects are not followed.
Try it on your own server
Create a free account and your API key is ready in about two minutes. 3 GB included, bandwidth never billed, no card needed.