Developers Webhooks
Webhooks
Get events pushed to your server, and check they really came from LonexOne.
Webhooks push events to your server as they happen, so you don't have to keep asking. They're the same events your Discord log channels can get, from new calls and warrants to promotions and staff actions, sent as JSON.
Setting one up
- In the LonexOne panel, open Developer API and choose Add a webhook.
- Enter an
https://address on your server, tick the events you want, and save. - Copy the signing secret: it's shown once. You'll use it to check requests are really from LonexOne.
- Press Send a test. LonexOne sends a
webhook.testevent and tells you what your server answered.
A community can have up to five webhooks. Webhooks work whether or not you also post the events to Discord.
What you receive
Each event is a POST with a JSON body. data carries the same title, description and labelled fields as the Discord message, with Discord mentions written as @123….
{
"id": "0c2b6a1e-6f0a-4b8e-9c55-6a6c3f5d2e10",
"event": "cad_call",
"event_name": "New calls",
"created_at": "2026-11-08T20:01:00+00:00",
"community": {
"id": 12,
"name": "Sunnyside RP"
},
"data": {
"department": null,
"title": "Call #58: Shots fired",
"description": null,
"url": null,
"fields": {
"Priority": "1 Emergency",
"Location": "Grove St",
"Postal": "8012"
}
}
}
| Header | What it is |
|---|---|
X-LonexOne-Event | The event, like cad_call. Same as event in the body. |
X-LonexOne-Delivery | A unique id for this delivery. Retries keep the same id, so you can ignore repeats. |
X-LonexOne-Timestamp | When it was sent, in Unix seconds. |
X-LonexOne-Signature | sha256= and the HMAC-SHA256 of the timestamp, a dot, and the raw body, keyed with your signing secret. |
Checking the signature
Work out the signature yourself from the raw body (before parsing it) and compare. Also refuse old timestamps, so a captured request can't be replayed later.
$body = file_get_contents('php://input'); $timestamp = $_SERVER['HTTP_X_LONEXONE_TIMESTAMP'] ?? ''; $signature = $_SERVER['HTTP_X_LONEXONE_SIGNATURE'] ?? ''; $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, getenv('LONEXONE_WEBHOOK_SECRET')); if (! hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) { http_response_code(401); exit; } $event = json_decode($body, true); // $event['event'], $event['data']['title'], $event['data']['fields'] ... http_response_code(204);
import crypto from 'node:crypto'; import express from 'express'; const app = express(); app.post('/lonexone-webhook', express.raw({ type: 'application/json' }), (req, res) => { const timestamp = req.get('X-LonexOne-Timestamp') ?? ''; const signature = req.get('X-LonexOne-Signature') ?? ''; const expected = 'sha256=' + crypto .createHmac('sha256', process.env.LONEXONE_WEBHOOK_SECRET) .update(`${timestamp}.${req.body}`) .digest('hex'); const valid = signature.length === expected.length && crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); if (!valid || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) { return res.sendStatus(401); } const event = JSON.parse(req.body); // event.event, event.data.title, event.data.fields ... res.sendStatus(204); });
Responding and retries
- Answer with any
2xxwithin 5 seconds. Do slow work after you've answered. - Anything else counts as a failure. LonexOne tries again after 10 seconds, 1 minute and 5 minutes.
- After 25 failures in a row the webhook turns itself off, and the panel says why. Saving it again turns it back on.
- Redirects aren't followed, and webhooks can't point at private or local addresses.
Events
The events you can pick depend on your community's modules.
Community
joined | Joined a department |
left | Left or removed |
transfer | Transfers |
promotion | Promotions |
demotion | Demotions |
discipline | Discipline |
certification | Certifications |
leave | Leave |
interest | Letters of interest |
application | Applications |
shift | Shifts |
courts | Court cases |
CAD
cad_call | New calls |
cad_panic | Panic buttons |
cad_signal | Signal 100 |
cad_arrest | Arrests filed |
cad_warrant | Warrants issued or requested |
cad_warrant_approved | Warrants approved |
cad_bolo | BOLOs |
cad_report_submitted | Reports submitted |
cad_report_returned | Reports returned |
cad_forced_off | Units signed off by a supervisor |
cad_verdict | Court verdicts |
cad_inspection_failed | Failed fire inspections |
Staff Panel
staff_ban | Bans |
staff_ban_pending | Bans waiting for approval |
staff_kick | Kicks |
staff_service | Community service |
staff_warn | Warnings |
staff_commend | Commendations |
staff_revoked | Bans lifted and actions revoked |
staff_report | New reports and staff requests |
staff_alert | Flagged players and possible ban evasion joining |
staff_appeal | New appeals |
Training
training_session | Training sessions |
training_result | Training results |
training_request | Requests to join training |