Skip to content
BasiX Media

Discord alerts & webhooks

Alerts on Discord, and webhooks for a web backend.

On this page

Alerts

A rule fires when threshold matching events arrive within windowMinutes. It then stays quiet for cooldownMinutes. It posts to a Discord webhook, to your webhook endpoints (event log.alert), or both. Rules are checked after each ingest. Full key required.

MethodPath
GET, POST/logs/alerts
GET, PATCH, DELETE/logs/alerts/<id>
FieldDefaultNotes
namerequired1-80 chars.
enabledtrue
conditionsmatch allcategories, levels, contains (text), metadata (up to 10 { key, equals }).
threshold11-10,000 events.
windowMinutes51-1,440.
cooldownMinutes50-10,080.
discordWebhookUrlnullhttps://discord.com/api/webhooks/<id>/<token>.
webhookIds[]Ids of your webhook endpoints. At least one target is required.
curl
curl -X POST https://upload.basix.media/api/v1/logs/alerts \
  -H "Authorization: Bearer $BASIX_KEY" -H "Content-Type: application/json" \
  -d '{
    "name": "Money exploit",
    "conditions": { "categories": ["economy"], "levels": ["warn", "error"], "contains": "money",
                    "metadata": [ { "key": "source", "equals": "admin" } ] },
    "threshold": 3, "windowMinutes": 5, "cooldownMinutes": 15,
    "discordWebhookUrl": "https://discord.com/api/webhooks/123456789/your-webhook-token"
  }'

The number of rules depends on your plan. Over the limit, creating one answers 403 PLAN_REQUIRED.

Webhooks

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.

JavaScript
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 2xx within 5 seconds. Do the work after answering.
  • Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 6 h. A 4xx other than 429 is 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.

Get alerted on Discord

Create an alert rule with discordWebhookUrl (see Alerts). Example: 3 warn or error events in the economy dataset within 5 minutes.

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.

Create a free account
Discord alerts & webhooks — FiveM docs — BasiX