Filodos

Webhook events

What each delivery carries and how to trust it. For managing subscriptions, see webhook management. Part of the documentation.

Event catalog

EventFires whenUse it for
trip.endedA segmented trip closes (only trips ending inside the sync window, so a first sync never replays history).Mileage filing.
geofence.enterA linked vehicle enters one of your zones.Delivery timestamps.
geofence.exitA linked vehicle leaves one of your zones.Visit durations.
alarm.criticalSOS, collision, or rollover fires.Opening an incident.
report.dailyThe nightly report e-mail run finishes for the organization.Confirming the day closed.
webhook.testOnly from the test button — never from real activity.Proving the chain.

Every delivery is a POST of JSON with two headers: X-Filodos-Signature (HMAC-SHA256 hex of the raw body, keyed with the subscription secret) and X-Filodos-Delivery (a unique id per delivery — store it and ignore repeats).

Envelopes

Event pushes (geofence.enter, geofence.exit, alarm.critical) share one shape; attributes carries type detail such as geofenceId:

{"event": "geofence.enter", "delivery_id": "947c…",
 "organization": "acme", "device_id": 43,
 "unique_id": "359876543210987",
 "occurred_at": "2026-10-05T08:00:00+00:00",
 "event_type": "geofenceEnter", "traccar_event_id": 101,
 "attributes": {"geofenceId": 5}}

trip.ended carries the closed trip instead:

{"event": "trip.ended", "delivery_id": "a41d…",
 "organization": "acme", "device_id": 43,
 "unique_id": "359876543210987",
 "occurred_at": "2026-10-05T06:47:03+00:00",
 "trip": {"id": 812, "started_at": "2026-10-05T06:02:11+00:00",
          "ended_at": "2026-10-05T06:47:03+00:00", "distance_km": 24.6}}

report.daily carries the send outcome:

{"event": "report.daily", "delivery_id": "77e0…",
 "organization": "acme", "period_start": "2026-10-04",
 "sent": 3, "failed": 0}

Verify the signature

Reject any delivery whose signature does not match — only then parse the body. Python example:

import hashlib, hmac

def trusted(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Compare in constant time (hmac.compare_digest, never ==), over the raw body bytes before JSON parsing. Deliveries are best-effort with a short timeout and no automatic retries yet — keep the read API as the source of truth and webhooks as the trigger.