Push API (webhooks)
The Noccela Push API sends HTTPS requests to your endpoint when tags enter or leave areas or change state. Set-up, validation, retries, event types, payloads.
The Push API, also called the event API, turns the direction around: instead of asking Noccela for data, you give Noccela an HTTPS endpoint and it calls you when something happens. It suits events that are infrequent but important, such as a forklift entering a zone or a tag going offline, where polling would add delay and a permanent WebSocket would be more than you need.
How it works
- Create an endpoint: tell Noccela the URL that will receive events.
- Register the endpoint: Noccela sends a challenge to the URL, and your server echoes it back.
- Subscribe the endpoint to the event types you want, with filters.
- Receive events as they happen, and acknowledge each request.
- Fetch missed events after downtime.
1. Create an endpoint
POST /eventapi/endpoint HTTP/1.1
Host: api.noccela.io
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"name": "Warehouse events",
"url": "https://integration.example.com/noccela/events",
"secret": "YOUR_ENDPOINT_SECRET",
"retryPolicy": null
}
The response contains the new endpoint’s id. name and an absolute url are required.
Secret. If you set a secret, every event request carries the header Authorization: Basic followed by the Base64 encoding of noccela:YOUR_ENDPOINT_SECRET. Check it on every request. To change the secret later, use POST /eventapi/modifyendpoint.
Retry policy. Leave retryPolicy out or null to use the defaults:
| Field | Default | Meaning |
|---|---|---|
hoursToDiscard |
6 | Hours after the event when Noccela stops trying to deliver it |
maxFailedAttempts |
3 | Failed attempts before the event is given up |
cooldownBetweenAttempts |
10 | Minutes to wait between attempts |
maxFailedTriesInSingleAttempt |
100 | Tries within one attempt |
maxMinutesToWaitBetweenTriesInSingleAttempt |
1 | Longest wait between tries; the wait starts at 1 second and doubles up to this limit |
A changed retry policy takes effect immediately. When modifying, send the full policy; to return to defaults, send the default values.
2. Register (validate) the endpoint
An endpoint must be validated before it can receive events. Call:
POST /eventapi/registerendpoint HTTP/1.1
Host: api.noccela.io
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{ "id": 123 }
Noccela then sends a challenge to your URL:
{ "type": "challenge", "challenge": "AAABBB" }
Your endpoint must answer 200 OK with the challenge string as the whole body, with no JSON around it (preferably as text/plain). When the challenge succeeds, the registration call returns 204 No Content and the endpoint is ready.
3. Subscribe to events
A subscription (also called a registration) links one endpoint to one event type, with optional parameters. It gets its own ID.
POST /eventapi/event HTTP/1.1
Host: api.noccela.io
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"endpointId": 123,
"type": "AreaChange",
"parameters": {
"accountId": 1234,
"siteId": 5,
"deviceIds": null,
"areaIds": [17, 18]
}
}
Every event type accepts the common parameters accountId, siteId and deviceIds (null for every tag on the site). You can register the same type several times with different parameters, for example one subscription per group of tags; each must be removed separately. The OpenAPI description lists the event types as numbers (2 to 6); the examples here use their names.
4. Receive events
Each request to your endpoint has this shape:
| Field | Meaning |
|---|---|
type |
Event type, for example AreaChange |
ts |
Timestamp of the message (Unix time) |
regId |
ID of the subscription that produced it |
ev |
Array of one or more events of that type |
ev[].id |
Event ID, increasing; use it to fetch missed events |
ev[].ts |
When the event happened (Unix time) |
ev[].p |
Event-specific payload |
Example (illustrative values) of an AreaChange delivery:
{
"type": "AreaChange",
"ts": 1792656003120,
"regId": 456,
"ev": [
{
"id": 98231,
"ts": 1792656001874,
"p": {
"deviceId": 1234567,
"areaId": 17,
"status": "inside",
"secondsSinceChange": 340,
"entrancePointId": 3
}
}
]
}
Acknowledge with 200 OK or 204 No Content. Anything else, including a timeout, counts as a failure: Noccela retries according to the retry policy and, if your system stays unreachable, a circuit breaker pauses deliveries for a while. Answer quickly and do the processing afterwards.
A minimal receiver in Node.js that handles the challenge, checks the secret and logs events:
// receiver.mjs: run behind HTTPS, for example a reverse proxy (Node.js 22 or later)
import { createServer } from 'node:http';
import { timingSafeEqual } from 'node:crypto';
const SECRET = process.env.NCC_PUSH_SECRET; // YOUR_ENDPOINT_SECRET
const expected = Buffer.from('Basic ' + Buffer.from(`noccela:${SECRET}`).toString('base64'));
function authorised(header = '') {
if (!SECRET) return true;
const given = Buffer.from(header);
return given.length === expected.length && timingSafeEqual(given, expected);
}
createServer(async (req, res) => {
if (req.method !== 'POST') return res.writeHead(405).end();
let body = '';
for await (const chunk of req) body += chunk;
const message = JSON.parse(body);
if (message.type === 'challenge') {
return res.writeHead(200, { 'Content-Type': 'text/plain' }).end(message.challenge);
}
if (!authorised(req.headers.authorization)) return res.writeHead(401).end();
res.writeHead(204).end(); // acknowledge first, then process
for (const event of message.ev) {
console.log(message.type, `subscription ${message.regId}`, `event ${event.id}`, event.p);
}
}).listen(8080);
5. Fetch missed events
If your endpoint was down, fetch the stored events of a subscription. Pass the ID of the last event you processed to get only newer ones; leave it out to get all.
curl -s "https://api.noccela.io/eventapi/events?RegistrationId=456&EventId=98231" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
The response is { "events": [ … ] }, with each event in the same id, ts, p form as above.
Event types
AreaChange
A tag entered or left an area. One subscription can watch several areas.
| Parameter | Meaning |
|---|---|
areaIds |
Areas to watch; leave out, null or empty for all areas of the site |
| Payload field | Meaning |
|---|---|
deviceId |
The tag |
areaId |
The area |
status |
inside (entered) or outside (left) |
secondsSinceChange |
Seconds the tag spent inside or outside the area before this change |
entrancePointId |
Closest entrance point of the area, if entrance points are configured in the layout |
RoomChange
A tag entered or left one area, with tunable sensitivity. A tag counts as inside once it has stayed within the area’s boundary, plus a 0.5 m margin, long enough to meet both thresholds.
| Parameter | Default | Meaning |
|---|---|---|
areaId |
(required) | The area to watch; area IDs come from the site layout |
cooldownSeconds |
60 | After a tag leaves, how long before another entry can be reported; 0 disables |
timeInAreaTreshold |
5 | Seconds the tag must spend inside or outside before the change counts; 0 disables |
locationCountTreshold |
5 | Positions inside the area needed before the change counts; 0 disables |
The parameter names are spelled Treshold in the API. The payload has deviceId, status (inside or outside) and, when the tag leaves, timeSpent in seconds; the message root also carries areaId. The event time is the moment the tag actually crossed, not the moment the thresholds were met.
TagStateChange
A tag’s state changed. The payload has deviceId and a state object with only the changed properties, such as name, fwVersion, floorId, signalLost, powerSave, areas, inCharger, groupId, secondaryTagGroupIds, batteryPercentage and cycleStride.
| Parameter | Meaning |
|---|---|
properties |
Property names to report; leave out, null or empty for all |
BeaconStateChange
A base station’s state changed. The payload has deviceId and a state object with the changed values: online, charging and voltage (mobile base stations), and radioDisabled.
| Parameter | Meaning |
|---|---|
getDiagnostics |
Also report diagnostics: connectTimestamp, statusCode (error bitmask, 0 means all OK), pingTime in ms, clockMasterWanted and clockMasterActive |
StrokeCount
A tag’s stroke counter increased. Stroke counting is an industrial feature that must be enabled for the tag. The payload has deviceId and strokeCount.
| Parameter | Default | Meaning |
|---|---|---|
updateFrequency |
1 | Send an event every time the count is a multiple of this number |
Last updated 22 October 2026