Integrations

Samsara Integration

What actually syncs, how vehicles get matched, and where to look when a reading doesn't show up.

What this integration does

The Samsara integration pulls one thing from Samsara's Fleet API: distance data for each vehicle. Specifically, it requests two stat types — obdOdometerMeters (the vehicle's OBD-reported odometer) and gpsDistanceMeters (GPS-derived cumulative distance, used as a fallback when a vehicle doesn't report OBD odometer data) — and converts whichever value is present into miles.

That's the entire scope today. Engine hours, fuel level, driver behavior/safety events, fault codes, and live GPS location are all things Samsara's API exposes, but this integration doesn't request or store any of them — the sync route only calls the /fleet/vehicles/stats?types=obdOdometerMeters,gpsDistanceMeters endpoint.

Setup, step by step

  1. In Samsara, generate a read-only API token under Settings → API Tokens. No webhook or callback URL needs to be configured on Samsara's side — this integration only ever calls out to Samsara, it never receives anything from Samsara.
  2. In Equipt, go to Settings → Integrations and paste the token into the API Key field, then click Save.
  3. For each vehicle you want synced, open its detail page and set the Samsara Vehicle ID field (under Integrations, in the New/Edit Vehicle dialog) — Samsara shows this ID in the URL when you select a vehicle under Fleet → Vehicles. This step is optional but strongly recommended; see vehicle matching below for what happens if you skip it.
  4. Click Sync Now on the Integrations tab to run a first sync immediately, rather than waiting for the next scheduled run. The button is disabled until an API key has been saved.

How vehicle matching works

Each vehicle Samsara returns is matched to an Equipt vehicle in two passes:

  1. By Samsara Vehicle ID first — if the vehicle's samsara_vehicle_id field matches the ID on the incoming record, that's the match.
  2. By exact name second — if no ID match is found, it falls back to a case-insensitive, trimmed comparison of the vehicle's name against Samsara's vehicle name.
Setting the Samsara Vehicle ID explicitly is the reliable path. Name matching is a convenience fallback — it breaks silently if a vehicle is renamed in either system, or if two vehicles share a name.

A Samsara vehicle with no match in either pass isn't treated as an error — it's simply skipped, and a line noting the unmatched vehicle name and Samsara ID is added to that sync run's detail log (visible after a manual Sync Now).

Sync mechanics

Sync is triggered two ways, both hitting the same route (/api/integrations/samsara/sync):

TriggerHow it works
ScheduledA Vercel Cron job hits the route once a day at 10:00 UTC (6:00 AM ET), authenticated with a cron secret. This run syncs every org that has Samsara enabled with a saved API key — not just one org.
ManualClicking Sync Now on Settings → Integrations sends an authenticated POST as the logged-in user. This requires an admin role — the route checks the caller's profile and rejects non-admins — and only syncs the caller's own org.

There is no webhook from Samsara and no near-real-time delivery — every reading you see came from one of these two pulls. Samsara's stats endpoint is paginated; the sync follows the pagination cursor until Samsara reports no further pages, so large fleets are fully fetched in one run.

How a reading turns into a meter update

For each matched vehicle, the sync looks for meters on that vehicle with a unit of miles or mi:

  • No miles meter exists yet — one is auto-created, named "Odometer," starting at 0. You don't need to create a meter by hand before syncing a vehicle for the first time.
  • The incoming mileage is less than the meter's current value — the reading is skipped and logged (odometers don't go backwards, so this is treated as bad or stale data rather than written).
  • The incoming mileage is greater than or equal to the current value — a new row is inserted into meter_readings with source: "samsara" and a note of whether the value came from OBD or GPS. The meter's current_value is only advanced when the new mileage is strictly greater than what's currently stored — equal readings are still logged (so you have a daily record even with no vehicle movement) but don't re-trigger the meter update.

That current-value update is conditioned on the database row's value at write time, not a value read earlier in the same run — this matters because the cron job and a manual Sync Now could in principle overlap, and this guards against one clobbering a higher value written by the other.

Sync status and errors

After every run, each org's integrations row is stamped with a timestamp and a status, both shown on Settings → Integrations:

StatusMeaning
Connected (ok)No errors this run.
PartialAt least one vehicle matched and got a reading, but at least one error also occurred (e.g. a meter failed to create, a reading failed to insert).
ErrorNo vehicles matched at all, or the call to Samsara's API failed outright — most commonly an invalid or expired API key, which surfaces here as a failed fetch rather than a distinct "bad key" message.

A manual Sync Now additionally shows the run's full detail log inline — counts of vehicles fetched from Samsara, matched, and readings written, plus a line-by-line list (unmatched vehicles, skipped backwards readings, any insert failures). The scheduled daily run doesn't surface that detail anywhere in the UI — only the summary status and last-synced timestamp update.

Odometer readings not updating as expected? Check, in order: 1) the integration shows a saved API key and status other than "Error" on Settings → Integrations. 2) the vehicle has a Samsara Vehicle ID set — if it's relying on name matching, confirm the name is an exact, case-insensitive match to what Samsara shows. 3) run Sync Now and read the detail log — an unmatched vehicle or a skipped "less than current" reading will say so explicitly. 4) the vehicle has a meter with unit miles or mi — a meter in a different unit (e.g. hours) is never touched by this sync. 5) confirm the API token in Samsara hasn't been revoked or regenerated since it was pasted in.

Meters and automations

A Samsara-written reading is not a special case for anything downstream — it updates the vehicle's meter through the exact same meter_readings + meters.current_value path a manually logged reading would use, just tagged with source: "samsara" instead of a person's name. That means any meter-threshold automation configured on that meter fires exactly the same way it would from a manual entry — see Building a meter-threshold automation and Pending Reset, explained in the Meters guide for how that firing, Pending Reset, and Service Interval logic works — none of it is re-explained here since it doesn't change based on where the reading came from.

© 2026 Landscapt. All rights reserved.