Docs / AI Box And Video Analytics Software / API Reference
Traffic Monitor API
The Traffic Monitor API: vehicle counts, classes, speeds and plates on a road's cameras. It gives the volumes and their trend, corridor analytics (peak hour, speeds, class share, approaches), incidents (speeding, wrong way, stopped vehicles, congestion, watchlist hits), the vehicle log with each passage's crop, the plate watchlist, and the site's DATEX II v3 feed for traffic management centres.
Speeds are advisory estimates for traffic management; they are no basis for enforcement.
Personal data. Plates are personal data. A token sees plates and crops only with the personal_data scope; without it the log, its export and the incidents are answered without plates and crops, and plate search and the watchlist are refused. Plate searches are recorded in the audit log.
Calling the API
The same as the platform API: an API account's token as Authorization: Bearer <API_KEY>. Site and camera ids come from the platform API (GET /api/v1/sites, GET /api/v1/cameras). Each operation says who can call it and which scopes it needs. Errors, times, paging and the rate limit follow the platform API.
Feeds With Their Own Credentials
Two operations are for other systems and use a credential of their own, made by an admin of the site on the dashboard: the DATEX II pull feed (a feed token) and the signed watchlist push (the site's watchlist key, Standard Webhooks signatures).
1.0.0
Traffic
Volumes, trend, corridor analytics and incidents.
Vehicles, Speeds, Plates Read, Incidents, Congested Lanes Now
GET/sites/{id}/overview
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer | |
fromrequired query | string | RFC 3339 |
torequired query | string | RFC 3339 |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/overview?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/overview?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/overview?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"vehicles": 18452,
"by_class": {
"car": 7311,
"motorcycle": 9870,
"bus": 212,
"truck": 1003,
"bicycle": 56
},
"flow_per_min": 12.8,
"speeds_measured": 15207,
"mean_speed_kmh": 38.6,
"p85_speed_kmh": 51,
"plates_read_pct": 0,
"incidents": {
"speeding": 41,
"wrong_way": 3,
"stopped": 5,
"congestion": 2,
"watchlist": 0
},
"congested_now": [
{
"camera_id": 21,
"lane": "Lane 2",
"occupancy": 82,
"since": "2026-11-02T09:41:00Z"
}
]
}Vehicles And Mean Speed Per Hour Or Day
GET/sites/{id}/trend
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer | |
fromrequired query | string | RFC 3339 |
torequired query | string | RFC 3339 |
bucket query | string | hour or day |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/trend?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/trend?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/trend?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"bucket": "hour",
"buckets": [
{
"start": "<start>",
"vehicles": 12,
"mean_speed_kmh": 12
}
]
}Corridor Analytics: Peak Hour And Factor, Heatmap, Speed Histogram, Class Share, Approaches
GET/sites/{id}/analytics
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer | |
fromrequired query | string | RFC 3339 |
torequired query | string | RFC 3339 |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/analytics?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/analytics?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/analytics?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"summary": {
"vehicles": 12,
"daily_average": 12,
"mean_speed_kmh": 12,
"p85_speed_kmh": 12,
"peak_hour": {
"start": "<start>",
"vehicles": 12,
"phf": 12
}
},
"heatmap": [
{
"day": "<day>",
"hours": [
7
]
}
],
"speed_histogram": {
"bucket_kmh": 12,
"counts": [
12
]
},
"class_share": [
{
"day": "<day>",
"counts": {
"car": 12,
"motorcycle": 12,
"bus": 12,
"truck": 12,
"bicycle": 12
}
}
],
"approaches": [
{
"camera_id": 1,
"line": "<line>",
"direction": "in",
"vehicles": 12,
"mean_speed_kmh": 12
}
]
}Speeding, Wrong Way, Stopped Vehicles, Congestion And Watchlist Hits, Newest First
GET/sites/{id}/incidents
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read. Without personal_data the answer is the anonymous form: no names, plates, pictures or footage.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer | |
fromrequired query | string | RFC 3339 |
torequired query | string | RFC 3339 |
kind query | string | |
limit query | integer |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/incidents?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&limit=50" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/incidents?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&limit=50",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/incidents?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&limit=50", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"items": [
{
"id": "<id>",
"time": "2026-11-02T08:00:00Z",
"camera_id": 1,
"kind": "speeding",
"lane": "Lane 1",
"zone": "Loading bay",
"class": "car",
"plate": "B 1234 XYZ",
"speed_kmh": 12,
"limit_kmh": 12,
"state": "ok",
"occupancy": 12,
"stopped_s": 60
}
]
}Vehicle Log
Every passage with its class, speed, lane and plate; the export and crops.
The Vehicle Log, Newest First, Paged; A Plate Search Is Audited
GET/sites/{id}/logs
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read. Without personal_data the answer is the anonymous form: no names, plates, pictures or footage.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer | |
fromrequired query | string | RFC 3339 |
torequired query | string | RFC 3339 |
plate query | string | a plate search; API tokens need personal_data for it |
class query | string | |
camera query | integer | |
limit query | integer | |
before query | string | next_before of the previous page |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/logs?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&camera=1&limit=50&before=2026-11-02T08%3A00%3A00Z" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/logs?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&camera=1&limit=50&before=2026-11-02T08%3A00%3A00Z",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/logs?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00&camera=1&limit=50&before=2026-11-02T08%3A00%3A00Z", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"items": [
{
"id": "21:90311",
"time": "2026-11-02T09:44:12Z",
"camera_id": 21,
"line": "count",
"direction": "in",
"lane": "Lane 1",
"class": "car",
"colour": "white",
"speed_kmh": 42.5,
"plate": null,
"media": null,
"photo": null
},
{
"id": "21:90310",
"time": "2026-11-02T09:44:09Z",
"camera_id": 21,
"line": "count",
"direction": "in",
"lane": "Lane 2",
"class": "motorcycle",
"colour": null,
"speed_kmh": null,
"plate": null,
"media": null,
"photo": null
}
],
"next_before": "2026-11-02T09:44:09Z"
}The Vehicle Log As CSV Or XLSX (EN/ID, Local Times, At Most 20 000 Rows; Audited)
GET/sites/{id}/logs/export
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read, export. Without personal_data the answer is the anonymous form: no names, plates, pictures or footage.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer | |
fromrequired query | string | RFC 3339 |
torequired query | string | RFC 3339 |
format query | string | csv or xlsx |
language query | string | en or id |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/logs/export?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/logs/export?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/logs/export?from=2026-11-01T00%3A00%3A00%2B07%3A00&to=2026-11-02T00%3A00%3A00%2B07%3A00", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());A Passage's Crop (The Vehicle And Its Plate); Only A Reference That A Passage Of This Site Holds
GET/sites/{id}/media/{ref}
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read, personal_data.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer | |
refrequired path | string |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/media/{ref}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/media/{ref}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/media/{ref}", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Watchlist
Plates to watch for, their hits, the CSV import and the signed feed from other systems.
Import A CSV (plate,reason[,notes]): Adds Or Updates Entries; Audited
POST/sites/{id}/watchlist/upload
Who can call it: An account that is an admin of the site. Token scopes: write, personal_data.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X POST "https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist/upload" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist/upload",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist/upload", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"added": 12,
"updated": 12,
"removed": 12
}Signed Watchlist Update From Another System
POST/feeds/watchlist/{site}
Adds or updates plates (upsert) and removes plates (remove) on the site's watchlist. The call is signed with the site's watchlist key the Standard Webhooks way: webhook-id, webhook-timestamp (within 5 minutes) and webhook-signature: v1,<base64 HMAC-SHA256 of id.timestamp.body>. A repeated message id, a bad signature and a site without a key all get the same 401. Every import is recorded in the audit log.
Authentication: The site's watchlist key (made by an admin of the site on the dashboard); no API token.
Parameters
| Field | Type | Description |
|---|---|---|
siterequired path | integer | |
webhook-idrequired header | string | A unique id for this message, at most 128 characters. A repeated id is refused. |
webhook-timestamprequired header | string | The time of signing in Unix seconds; it must be within 5 minutes of the box's clock. |
webhook-signaturerequired header | string | v1, and the base64 HMAC-SHA256 of <webhook-id>.<webhook-timestamp>.<body> with the site's key (the base64 part after whsec_). Several signatures may be given, separated by spaces. |
Body
| Field | Type | Description |
|---|---|---|
upsert | object[] | |
upsert[].platerequired | string | an Indonesian plate, stored as B 1234 XYZ |
upsert[].reasonrequired | enum | stolen, wanted, tax_arrears, other |
upsert[].notes | string | |
remove | string[] |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X POST "https://<BOX_HOST>/api/traffic/v1/feeds/watchlist/1" \
-H "webhook-id: <WEBHOOK_ID>" \
-H "webhook-timestamp: <WEBHOOK_TIMESTAMP>" \
-H "webhook-signature: <WEBHOOK_SIGNATURE>" \
-H "Content-Type: application/json" \
-d '{"upsert":[{"plate":"B 1234 XYZ","reason":"stolen","notes":"Blue pickup"}],"remove":["D 5678 AB"]}'import requests
r = requests.post(
"https://<BOX_HOST>/api/traffic/v1/feeds/watchlist/1",
headers={"webhook-id": "<WEBHOOK_ID>", "webhook-timestamp": "<WEBHOOK_TIMESTAMP>", "webhook-signature": "<WEBHOOK_SIGNATURE>"},
json={
"upsert": [
{
"plate": "B 1234 XYZ",
"reason": "stolen",
"notes": "Blue pickup"
}
],
"remove": [
"D 5678 AB"
]
},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/feeds/watchlist/1", {
method: "POST",
headers: { "webhook-id": "<WEBHOOK_ID>", "webhook-timestamp": "<WEBHOOK_TIMESTAMP>", "webhook-signature": "<WEBHOOK_SIGNATURE>", "Content-Type": "application/json" },
body: JSON.stringify({
"upsert": [
{
"plate": "B 1234 XYZ",
"reason": "stolen",
"notes": "Blue pickup"
}
],
"remove": [
"D 5678 AB"
]
}),
});
console.log(r.status, await r.text());Response 200
{
"added": 12,
"updated": 12,
"removed": 12
}The Site's Watchlist
GET/sites/{id}/watchlist
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read, personal_data.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer | |
q query | string | part of a plate |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"items": [
{
"id": 1,
"entry_id": "<entry_id>",
"plate": "B 1234 XYZ",
"reason": "stolen",
"notes": "Shift A",
"active": true,
"added_by": "<added_by>",
"source": "manual",
"created": "<created>",
"updated": "<updated>"
}
],
"key": true
}Add A Plate To The Watchlist (Pushed To The Site's Cameras; Audited)
POST/sites/{id}/watchlist
Who can call it: An account that is an admin of the site. Token scopes: write, personal_data.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer |
Body
| Field | Type | Description |
|---|---|---|
platerequired | string | an Indonesian plate, stored as B 1234 XYZ |
reasonrequired | enum | stolen, wanted, tax_arrears, other |
notes | string |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X POST "https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"plate":"B 1234 XYZ","reason":"stolen","notes":"Shift A"}'import requests
r = requests.post(
"https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist",
headers={"Authorization": "Bearer <API_KEY>"},
json={
"plate": "B 1234 XYZ",
"reason": "stolen",
"notes": "Shift A"
},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
body: JSON.stringify({
"plate": "B 1234 XYZ",
"reason": "stolen",
"notes": "Shift A"
}),
});
console.log(r.status, await r.text());Response 200
{
"id": 1,
"entry_id": "<entry_id>",
"plate": "B 1234 XYZ",
"reason": "stolen",
"notes": "Shift A",
"active": true,
"added_by": "<added_by>",
"source": "manual",
"created": "<created>",
"updated": "<updated>"
}Change, Pause Or Resume An Entry (Audited)
PATCH/watchlist/{entry}
Who can call it: An account that is an admin of the site. Token scopes: write, personal_data.
Parameters
| Field | Type | Description |
|---|---|---|
entryrequired path | integer |
Body
| Field | Type | Description |
|---|---|---|
reason | enum | stolen, wanted, tax_arrears, other |
notes | string | |
active | boolean |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X PATCH "https://<BOX_HOST>/api/traffic/v1/watchlist/1" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"reason":"stolen","notes":"Shift A","active":true}'import requests
r = requests.patch(
"https://<BOX_HOST>/api/traffic/v1/watchlist/1",
headers={"Authorization": "Bearer <API_KEY>"},
json={
"reason": "stolen",
"notes": "Shift A",
"active": True
},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/watchlist/1", {
method: "PATCH",
headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
body: JSON.stringify({
"reason": "stolen",
"notes": "Shift A",
"active": true
}),
});
console.log(r.status, await r.text());Response 200
{
"id": 1,
"entry_id": "<entry_id>",
"plate": "B 1234 XYZ",
"reason": "stolen",
"notes": "Shift A",
"active": true,
"added_by": "<added_by>",
"source": "manual",
"created": "<created>",
"updated": "<updated>"
}Delete An Entry (Audited)
DELETE/watchlist/{entry}
Who can call it: An account that is an admin of the site. Token scopes: write, personal_data.
Parameters
| Field | Type | Description |
|---|---|---|
entryrequired path | integer |
Responses
| Status | Meaning |
|---|---|
204 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X DELETE "https://<BOX_HOST>/api/traffic/v1/watchlist/1" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.delete(
"https://<BOX_HOST>/api/traffic/v1/watchlist/1",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/watchlist/1", {
method: "DELETE",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());The Latest Watchlist Hits
GET/sites/{id}/watchlist/hits
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read, personal_data.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer | |
limit query | integer |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist/hits?limit=50" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist/hits?limit=50",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/watchlist/hits?limit=50", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"items": [
{
"id": "<id>",
"time": "2026-11-02T08:00:00Z",
"camera_id": 1,
"plate": "B 1234 XYZ",
"reason": "Unpaid toll",
"entry_id": "<entry_id>"
}
]
}DATEX II
The site's DATEX II v3 feed: camera locations, the pull token, the push target.
The Camera's Location For The DATEX II Feed
GET/cameras/{id}/location
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/cameras/1/location" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/cameras/1/location",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/cameras/1/location", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"camera": 12,
"latitude": -6.2,
"longitude": 106.8,
"road": "<road>",
"direction": "in",
"version": 3
}Set The Camera's Location (Latitude, Longitude, Road, DATEX II Direction); Audited
PUT/cameras/{id}/location
Who can call it: An account that is an admin of the site. Token scopes: write.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer |
Body
| Field | Type | Description |
|---|---|---|
latituderequired | number | |
longituderequired | number | |
road | string | |
direction | enum | northBound, northEastBound, eastBound, southEastBound, southBound, southWestBound, westBound, northWestBound, bothWays, inboundTowardsTown, outboundFromTown, unknown |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X PUT "https://<BOX_HOST>/api/traffic/v1/cameras/1/location" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"latitude":-6.2,"longitude":106.8,"road":"<road>","direction":"northBound"}'import requests
r = requests.put(
"https://<BOX_HOST>/api/traffic/v1/cameras/1/location",
headers={"Authorization": "Bearer <API_KEY>"},
json={
"latitude": -6.2,
"longitude": 106.8,
"road": "<road>",
"direction": "northBound"
},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/cameras/1/location", {
method: "PUT",
headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
body: JSON.stringify({
"latitude": -6.2,
"longitude": 106.8,
"road": "<road>",
"direction": "northBound"
}),
});
console.log(r.status, await r.text());Response 200
{
"camera": 12,
"latitude": -6.2,
"longitude": 106.8,
"road": "<road>",
"direction": "in",
"version": 3
}The Site's DATEX II Feed: Pull Token Set, Push Target, Interval, Last Push
GET/sites/{id}/datex
Who can call it: An account that is an admin of the site. Token scopes: read.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/datex" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/datex",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/datex", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"site": 12,
"pull_token": true,
"pull_url": "https://<RECEIVER_HOST>/hooks/arsa",
"push_url": "https://<RECEIVER_HOST>/hooks/arsa",
"interval_s": 60,
"last_push": "<last_push>",
"last_status": "<last_status>",
"push_secret": "<push_secret>",
"shown_once": true
}Set The Push Target And Interval; A New Target Gets A New Signing Secret, Shown Once; Audited
PUT/sites/{id}/datex
Who can call it: An account that is an admin of the site. Token scopes: write.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer |
Body
| Field | Type | Description |
|---|---|---|
push_url | string | |
interval_srequired | integer |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X PUT "https://<BOX_HOST>/api/traffic/v1/sites/1/datex" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"push_url":"https://<RECEIVER_HOST>/hooks/arsa","interval_s":60}'import requests
r = requests.put(
"https://<BOX_HOST>/api/traffic/v1/sites/1/datex",
headers={"Authorization": "Bearer <API_KEY>"},
json={
"push_url": "https://<RECEIVER_HOST>/hooks/arsa",
"interval_s": 60
},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/datex", {
method: "PUT",
headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
body: JSON.stringify({
"push_url": "https://<RECEIVER_HOST>/hooks/arsa",
"interval_s": 60
}),
});
console.log(r.status, await r.text());Response 200
{
"site": 12,
"pull_token": true,
"pull_url": "https://<RECEIVER_HOST>/hooks/arsa",
"push_url": "https://<RECEIVER_HOST>/hooks/arsa",
"interval_s": 60,
"last_push": "<last_push>",
"last_status": "<last_status>",
"push_secret": "<push_secret>",
"shown_once": true
}The Site's DATEX II v3 Feed
GET/feeds/datex/{site}/{doc}
DATEX II v3 XML (EN 16157, valid against the v3.7 schemas): measurement-sites, measured-data (the last complete interval) or situations (now). Send the site's feed token as Authorization: Bearer <FEED_TOKEN>.
Authentication: The site's DATEX II feed token (made by an admin of the site on the dashboard); no API token.
Parameters
| Field | Type | Description |
|---|---|---|
siterequired path | integer | |
docrequired path | enum | measurement-sites, measured-data, situations |
Responses
| Status | Meaning |
|---|---|
200 | a DATEX II d2:payload |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/feeds/datex/1/measurement-sites" \
-H "Authorization: Bearer <FEED_TOKEN>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/feeds/datex/1/measurement-sites",
headers={"Authorization": "Bearer <FEED_TOKEN>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/feeds/datex/1/measurement-sites", {
method: "GET",
headers: { "Authorization": "Bearer <FEED_TOKEN>" },
});
console.log(r.status, await r.text());Settings
Speed limit, slow and stopped vehicles, congestion, retention of statistics and plates.
The Site's Traffic Monitor Settings
GET/sites/{id}/settings
Who can call it: An account with a role (viewer or admin) on the site. Token scopes: read.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X GET "https://<BOX_HOST>/api/traffic/v1/sites/1/settings" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"https://<BOX_HOST>/api/traffic/v1/sites/1/settings",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/settings", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Response 200
{
"passage_hold_s": 60,
"stopped_s": 60,
"slow_kmh": 12,
"speed_limit_kmh": 12,
"congestion_window_s": 60,
"stats_days": 30,
"plate_days": 12,
"version": 3
}Change The Site's Traffic Monitor Settings (Applied To Every Camera Of The Site)
PUT/sites/{id}/settings
Who can call it: An account that is an admin of the site. Token scopes: write.
Parameters
| Field | Type | Description |
|---|---|---|
idrequired path | integer |
Body
| Field | Type | Description |
|---|---|---|
passage_hold_srequired | number | |
stopped_srequired | number | |
slow_kmhrequired | number | |
speed_limit_kmh | number | |
congestion_window_srequired | number | |
stats_daysrequired | integer | |
plate_daysrequired | integer | plates are blanked after this |
version | integer |
Responses
| Status | Meaning |
|---|---|
200 | OK |
401 | RFC 9457 problem details |
403 | RFC 9457 problem details |
404 | RFC 9457 problem details |
409 | RFC 9457 problem details |
422 | RFC 9457 problem details |
502 | RFC 9457 problem details |
Request
curl -s -X PUT "https://<BOX_HOST>/api/traffic/v1/sites/1/settings" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"passage_hold_s":60,"stopped_s":60,"slow_kmh":12,"speed_limit_kmh":12,"congestion_window_s":60,"stats_days":30,"plate_days":12,"version":3}'import requests
r = requests.put(
"https://<BOX_HOST>/api/traffic/v1/sites/1/settings",
headers={"Authorization": "Bearer <API_KEY>"},
json={
"passage_hold_s": 60,
"stopped_s": 60,
"slow_kmh": 12,
"speed_limit_kmh": 12,
"congestion_window_s": 60,
"stats_days": 30,
"plate_days": 12,
"version": 3
},
)
print(r.status_code, r.text)const r = await fetch("https://<BOX_HOST>/api/traffic/v1/sites/1/settings", {
method: "PUT",
headers: { "Authorization": "Bearer <API_KEY>", "Content-Type": "application/json" },
body: JSON.stringify({
"passage_hold_s": 60,
"stopped_s": 60,
"slow_kmh": 12,
"speed_limit_kmh": 12,
"congestion_window_s": 60,
"stats_days": 30,
"plate_days": 12,
"version": 3
}),
});
console.log(r.status, await r.text());Response 200
{
"passage_hold_s": 60,
"stopped_s": 60,
"slow_kmh": 12,
"speed_limit_kmh": 12,
"congestion_window_s": 60,
"stats_days": 30,
"plate_days": 12,
"version": 3
}