Why both
Different best-of-breed surfaces.
Samsara's telematics is the de-facto industry surface for on-road fleet visibility — location, hours, fuel, driver behavior. DirtFleet is built around the shop floor: maintenance scheduling, work orders, hour-based PM, tool tracking, repair-log P&L. Most heavy-equipment shops we talk to want both, with Samsara feeding telemetry into DirtFleet where the maintenance workflows live.
For pure off-road fleets (excavators, dozers, gensets) Samsara is overkill. For pure on-road compliance (HOS, IFTA) DirtFleet is the wrong tool — we're not an ELD. The interesting middle is the mixed fleet: trucks + trailers + yellow iron + tools, where Samsara on the trucks translates cleanly into DirtFleet for everything else.
Pick a pattern
From metadata sync to DTC routing.
Pattern 1 — Vehicle metadata sync (works today via Samsara API)
Pull your Samsara vehicle list and create matching DirtFleet assets. Keep names, VINs, license plates in sync so the same identifier works across both systems. One-way: Samsara is source-of-truth for the roster.
When to pick this
When you've standardized on Samsara for vehicle management but want DirtFleet to handle maintenance + PM + work orders + tool tracking. Most common pattern.
Show the setup
# Cron-driven sync (run nightly): read Samsara → upsert DirtFleet import requests SAMSARA_TOKEN = os.environ["SAMSARA_API_TOKEN"] DF_KEY = os.environ["DIRTFLEET_API_KEY"] # 1. Pull all vehicles from Samsara r = requests.get( "https://api.samsara.com/fleet/vehicles", headers={"Authorization": f"Bearer {SAMSARA_TOKEN}"}, timeout=30, ) r.raise_for_status() vehicles = r.json()["data"] # 2. Build a DirtFleet bulk-create batch batch = [] for v in vehicles: batch.append({ "nickname": v.get("name") or v.get("externalIds", {}).get("samsara.serial", "Unknown"), "assetClass": "on-road", # Samsara vehicles default to road "vin": v.get("vin"), "customerAssetNumber": v.get("externalIds", {}).get("customer.id"), }) # Chunk to /api/v1/assets/batch's cap of 50 for i in range(0, len(batch), 50): r = requests.post( "https://dirtfleet.app/api/v1/assets/batch", headers={"Authorization": f"Bearer {DF_KEY}"}, json=batch[i:i+50], timeout=30, ) print(r.json()["summary"])Pattern 2 — Engine-hours auto-log (works today, ~50 LOC)
Poll Samsara's engine-hours feed every 15 minutes; for each delta, POST a DirtFleet hours log. Now PM tracking + auto-flag-on-threshold work without drivers having to log manually.
When to pick this
Fleets where mechanics are happy with DirtFleet but drivers won't reliably log meter readings. Samsara already has the hours from the telematics module; DirtFleet treats them as canonical.
Show the setup
# Sketch: 15-min cron that reads engine-hours and posts deltas const SAMSARA = process.env.SAMSARA_API_TOKEN; const DF = process.env.DIRTFLEET_API_KEY; const VEHICLE_MAP = JSON.parse(process.env.SAMSARA_TO_DF_ASSETS); // { samsaraId: dfAssetId } // 1. Pull current odometer + engine-hours snapshot from Samsara const r = await fetch( "https://api.samsara.com/fleet/vehicles/stats?types=engineHours", { headers: { Authorization: `Bearer ${SAMSARA}` } }, ); const snapshot = await r.json(); // 2. Cross-reference with last-known reading per vehicle (you maintain // this in a tiny key-value store — Redis, a JSON file on disk, etc). const lastSeen = await loadLastSeen(); // 3. For each vehicle whose engine-hours advanced, POST a DirtFleet hours log const newLogs = []; for (const v of snapshot.data) { const reading = v.engineHours?.value; if (!reading) continue; if (reading <= (lastSeen[v.id] ?? 0)) continue; newLogs.push({ assetId: VEHICLE_MAP[v.id], hoursReading: reading, note: "auto-synced from Samsara", clientMutationId: `samsara-${v.id}-${reading}`, // idempotency }); lastSeen[v.id] = reading; } // 4. Bulk-import (partial success — bad rows surface per-entry) await fetch("https://dirtfleet.app/api/v1/hours/batch", { method: "POST", headers: { Authorization: `Bearer ${DF}`, "Content-Type": "application/json" }, body: JSON.stringify(newLogs), }); await saveLastSeen(lastSeen);Pattern 3 — DTC routing (scaffolded; activates with first customer)
Samsara surfaces DTCs (diagnostic trouble codes) from the OBD/J1939 port; DirtFleet has a DTC narrative AI that can interpret them. The lib/integrations/samsara helpers route incoming DTC events into a flag-create with the AI-generated explanation.
When to pick this
Operations with mechanics who want fewer Slack pings and more pre-triaged information. The combo: Samsara catches the DTC, DirtFleet creates a RED flag with a human-readable cause + suggested next step.
Show the setup
# Status: SCAFFOLDED — repo has the OAuth + DTC normalization helpers, # the live wiring activates with the first paying customer's # Samsara API-token-bring-your-own flow. # # Pre-shipped: # lib/integrations/samsara.ts — token storage + DTC parsing # lib/ai-dtc-narrative.ts — DTC code → plain English (Gemini) # /api/integrations/samsara/connect (token-paste form) # # When live, the flow is: # 1. Samsara webhook → DirtFleet /api/integrations/samsara/dtc # 2. We resolve the Samsara vehicle id → DirtFleet asset id # 3. AI generates "P0420 → catalyst efficiency low; common cause is..." # 4. We post a YELLOW flag with the narrative as the note # 5. Mechanic sees it in their queue with context already attached # # Email hello@dirtfleet.app to be the first-customer activator.
Field mapping reference
Samsara → DirtFleet.
| Samsara field | DirtFleet field | Notes |
|---|---|---|
| externalIds.samsara.serial | Asset.serial | Samsara's serial maps cleanly to DirtFleet's "serial" field. Use this as the primary cross-reference key for off-road equipment. |
| Vehicle.vin | Asset.vin | On-road only. NHTSA VIN format. DirtFleet's VIN decoder works against either source. |
| Vehicle.name | Asset.nickname | Both have a human-readable "friendly name". Same field, different name. |
| Vehicle.externalIds.customer.id | Asset.customerAssetNumber | Customer-side asset numbers (your asset tag system). Map this both ways if you maintain it in both systems. |
| Vehicle.engineHours.value | HoursLog.hoursReading | The headline data point for Pattern 2. Samsara reports cumulative; DirtFleet stores cumulative; the delta is computed at the DirtFleet end. |
Honest scope
What we don't do.
- HOS / IFTA / ELD compliance. Stays in Samsara. DirtFleet is not an FMCSA-registered ELD; the integration explicitly skips the regulatory data path.
- Driver-facing Samsara dashboards. Drivers keep using the Samsara Driver app for HOS + workflows; DirtFleet's mobile is for the maintenance side. Two apps, two purposes.
- Real-time GPS streaming. The integration uses Samsara's polling API on a 15-min cadence — fine for hours, plates, and DTCs. If you need second-by-second tracking that's Samsara's product, not ours.
Want to activate Pattern 3 (DTC routing) early? Email hello@dirtfleet.app — first-customer activation is the unlock path. Full integrations directory.