Samsara, Geotab and Motive Platform Adapters
Three telematics vendors dominate North American fleets, and no two of them speak the same protocol. Samsara authenticates with a bearer token and pushes webhooks; Geotab authenticates through a session-based MyGeotab API and expects you to poll; Motive uses OAuth and its own vehicle-id scheme. If your compliance pipeline hard-codes any one of these, every new customer on a different platform forces a rewrite of the delivery layer — and every vendor rate-limit change becomes a production incident. The fix is an adapter layer that hides each vendor’s auth, rate limits, id scheme, and payload shape behind one internal interface, so the code that emits a DvirComplianceEvent never learns which platform receives it. This guide specifies that layer.
It sits inside the Telematics, ELD & Maintenance Platform Integration section, which defines the canonical DvirComplianceEvent this layer delivers, and it is the vendor-abstraction counterpart to Webhook Event Delivery for DVIR Pipelines, which owns the delivery mechanics. The same adapters serve the ELD Identity and HOS Record Linkage work when it queries each vendor’s driver directory. The concrete base-class and registry implementation is walked through in Building a Vendor-Neutral Telematics Adapter Layer.
Prerequisites and Environment Setup
Anchor link to "Prerequisites and Environment Setup"The adapter layer is a library the delivery service depends on, not a service itself. Target Python 3.10+ (the code uses Protocol, match/case, and X | Y unions) with:
- Pydantic 2.x — validate per-vendor config and the internal event before it is translated.
- httpx — one async client per adapter, each with its own timeout, retry budget, and rate-limit policy.
typing.Protocolandabc— the structural contract every concrete adapter satisfies.- A secrets source — vendor credentials injected as config, never hard-coded; apply the handling from Encrypting DVIR Data at Rest and in Transit.
- The canonical event — the
DvirComplianceEventdefined by the Telematics, ELD & Maintenance Platform Integration section is the single input every adapter accepts.
The one rule that makes the layer worth building: nothing above the adapter may reference a vendor by name. The dispatcher selects an adapter by enum; it never branches on if vendor == "samsara".
Vendor API Models Compared
Anchor link to "Vendor API Models Compared"The three vendors differ on exactly the axes an adapter must absorb. Knowing where they diverge tells you what the internal protocol has to hide.
| Concern | Samsara | Geotab (MyGeotab) | Motive |
|---|---|---|---|
| Auth style | Bearer API token | Session credential (database + user) | OAuth 2.0 access token |
| Vehicle id scheme | Numeric asset id | Id GUID-like string |
Numeric vehicle id |
| Delivery model | Outbound webhooks | Poll Get/GetFeed |
Webhooks + REST |
| Rate limits | Per-token request budget | Per-database call budget | Per-app request budget |
| Payload shape | Flat JSON | Entity objects with typed refs | Nested JSON envelopes |
Two consequences fall out of this table. First, resolve_vehicle_id must exist as a first-class adapter method, because a vehicle_vin on the internal event maps to a different native id on every platform. Second, delivery cannot assume push: a Geotab adapter’s push_event may enqueue for the next poll cycle rather than fire immediately, so the acknowledgement semantics must be part of the contract rather than assumed synchronous.
The auth axis is the one most likely to leak upward if the boundary is drawn carelessly. A Samsara bearer token is effectively static and needs only presence validation; a Geotab session must be acquired and can expire mid-batch, forcing a re-authentication path; a Motive OAuth token must be refreshed before expiry and carries a refresh token that must be stored as carefully as the access token itself. If the delivery layer had to know which of these it was dealing with, every credential-rotation policy change would ripple through the dispatcher. The adapter absorbs all three behind one authenticate method whose only contract is “after this returns, the adapter can talk to its vendor” — idempotent, so calling it before every batch is safe, and internally responsible for whatever refresh the vendor demands. That single guarantee is what lets the dispatcher call authenticate unconditionally without knowing whether it triggered a no-op, a session fetch, or a token refresh.
Data Schema and Normalization
Anchor link to "Data Schema and Normalization"The adapter layer standardizes two things: the config each adapter is built from, and the acknowledgement each returns. The event itself is the section’s canonical shape and is never redefined here.
| Field | Type | Enumeration / range | Compliance tag |
|---|---|---|---|
vendor |
str (enum) |
samsara, geotab, motive |
Registry routing key |
api_base_url |
str |
vendor endpoint | Deployment config |
credential |
SecretStr |
token / session / OAuth | Never logged |
rate_limit_per_min |
int |
vendor-specific budget | Backpressure control |
ack_mode |
str (enum) |
sync, deferred |
Delivery semantics |
Model the config so an adapter cannot be constructed without the fields it needs, and so a credential never lands in a log line:
from enum import Enum
from pydantic import BaseModel, SecretStr
class Vendor(str, Enum):
SAMSARA = "samsara"
GEOTAB = "geotab"
MOTIVE = "motive"
class AdapterConfig(BaseModel):
vendor: Vendor
api_base_url: str
credential: SecretStr # excluded from repr and logs by SecretStr
rate_limit_per_min: int
ack_mode: str = "sync" # "sync" or "deferred" (poll-based vendors)
class DeliveryAck(BaseModel):
vendor: Vendor
accepted: bool
vendor_ref: str | None = None # vendor-side id for the delivered event
deferred: bool = False # True when queued for a later poll cycle
Core Algorithm or Workflow
Anchor link to "Core Algorithm or Workflow"The layer has one abstract protocol, one registry, and one concrete class per vendor. The dispatcher resolves and delegates; it never knows the vendor.
- Define the protocol. Declare
TelematicsAdapterwithauthenticate,resolve_vehicle_id,push_event, and an acknowledgement contract. Every concrete adapter must satisfy it structurally. - Implement per-vendor concretes. Each of
SamsaraAdapter,GeotabAdapter, andMotiveAdaptertranslates the internal event into the vendor’s auth, id scheme, and payload shape. - Register by enum. A registry maps each
Vendorvalue to its adapter factory, built from injected config. - Resolve and delegate. The dispatcher looks up the adapter for the fleet’s vendor, resolves the native vehicle id, and calls
push_event, returning a normalizedDeliveryAck. - Fail an unknown vendor loudly. Resolving a vendor with no registered adapter raises, rather than silently dropping the event.
The protocol is the contract that lets the dispatcher stay vendor-blind:
from typing import Protocol, runtime_checkable
@runtime_checkable
class TelematicsAdapter(Protocol):
vendor: Vendor
async def authenticate(self) -> None:
"""Acquire or refresh vendor credentials. Idempotent."""
...
async def resolve_vehicle_id(self, vehicle_vin: str) -> str:
"""Map the internal VIN to the vendor's native vehicle id."""
...
async def push_event(self, event: dict) -> DeliveryAck:
"""Translate and deliver a DvirComplianceEvent; return a normalized ack."""
...
Compliance Thresholding and Routing
Anchor link to "Compliance Thresholding and Routing"The adapter layer is deliberately thin on compliance logic — the severity band and compliance_action are already decided by the time an event reaches it — but it carries two obligations that are compliance-relevant, not merely operational:
- Preserve the event verbatim. An adapter translates the envelope, not the payload’s meaning. The
severity_score,compliance_action, andaudit_hashmust survive translation unchanged, so a70-and-above OOS event still reads as OOS on the vendor side. Verify theaudit_hashbefore and reject a translation that would alter a compliance-bearing field. - Never drop an OOS event to a rate limit. A per-vendor rate limit is real backpressure, but an out-of-service notification cannot simply be discarded when the budget is exhausted. Queue it with priority and deliver on the next available token; treat a silently dropped OOS event as a compliance incident. The delivery guarantees for this live in Idempotent Webhook Retry and Signature Verification.
Because the same DvirComplianceEvent reaches every vendor unchanged, a downstream consumer such as Syncing DVIR Severity Scores to Samsara and Geotab via Webhook sees a consistent compliance signal no matter which platform delivered it.
A subtle but important discipline: the adapter must not enrich or interpret the event on the way out. It is tempting to have a vendor adapter compute a display string, re-derive a severity band, or translate compliance_action into a vendor-specific status label — but every such transformation is a place where the compliance meaning can drift between platforms, and a drifted meaning is exactly what an auditor will catch. Keep the adapter to envelope translation: map the vehicle id, format the payload the vendor’s API demands, attach auth, and carry the compliance-bearing fields through verbatim. If a vendor genuinely needs a derived field, derive it once upstream where the audit_hash still covers it, not inside the adapter where the derivation is invisible to the hash.
Production Integration and Platform Synchronization
Anchor link to "Production Integration and Platform Synchronization"- Dependency-inject config; never hard-code a vendor. Build each adapter from an
AdapterConfigso credentials and endpoints come from the environment. Adding a fourth vendor is a new class plus a registry entry — no change to the dispatcher. - Handle deferred acknowledgement honestly. A poll-based vendor cannot confirm delivery synchronously. Return
deferred=Trueand reconcile on the next poll rather than reporting a success the vendor has not confirmed. - Isolate rate limits per adapter. Each vendor’s budget is independent, so one platform’s throttling must not stall delivery to the others. Give each adapter its own client and limiter.
- Survive partial outages. When one vendor’s API is down, the registry keeps delivering to the others; the failing adapter’s events go to a dead-letter queue for controlled replay once it recovers.
- Guard id-mapping drift. A vendor can re-key a vehicle when an asset is re-provisioned, breaking a cached VIN-to-native-id mapping. Re-resolve on a delivery failure that looks like an unknown-vehicle error rather than trusting a stale cache.
Engineering Standards Checklist
Anchor link to "Engineering Standards Checklist"- One protocol, many concretes — every adapter satisfies
TelematicsAdapter; the dispatcher never branches on vendor name. - Registry by enum — vendors resolve through a registry keyed on the
Vendorenum; an unknown vendor raises, never silently drops. - Config injected — adapters are built from
AdapterConfig; no hard-coded credentials or endpoints. - Compliance fields preserved —
severity_score,compliance_action, andaudit_hashsurvive translation unchanged, verified against the hash. - Rate limits isolated — each adapter owns its client and limiter so one vendor’s throttle never stalls another.
- Deferred acks are honest — poll-based vendors return
deferred=Trueand reconcile later rather than faking synchronous success. - Outages contained — a failing vendor dead-letters its events; the others keep delivering.
Frequently Asked Questions
Anchor link to "Frequently Asked Questions"Why put an adapter layer in front of three telematics APIs at all?
Because the three vendors differ on auth, id scheme, rate limits, and payload shape, and hard-coding any one of them couples your entire delivery path to that vendor. An adapter layer hides those differences behind one internal protocol, so the code that emits a DvirComplianceEvent never changes when you add a customer on a different platform or when a vendor alters its API.
How do you add a fourth vendor without touching the core?
Write one new concrete class that satisfies the TelematicsAdapter protocol and register it under a new Vendor enum value. The dispatcher resolves adapters through the registry, so it picks up the new vendor without modification. Nothing above the adapter references a vendor by name, which is what keeps the addition local to one class and one registry entry.
What happens when one vendor’s API goes down?
The registry keeps delivering to the healthy vendors while the failing adapter’s events route to a dead-letter queue for controlled replay once the vendor recovers. Because each adapter has its own client and rate limiter, one platform’s outage or throttling does not stall delivery to the others, and no out-of-service event is dropped in the process.
Does the adapter change the compliance meaning of an event?
No. An adapter translates the delivery envelope — auth, id mapping, payload shape — but the severity_score, compliance_action, and audit_hash pass through unchanged and are verified against the hash. A translation that would alter a compliance-bearing field is rejected, so a 70-and-above out-of-service event reads as out-of-service on every platform.
Related
Anchor link to "Related"- Building a Vendor-Neutral Telematics Adapter Layer — the concrete base class, registry, and per-vendor implementation.
- Webhook Event Delivery for DVIR Pipelines — the delivery mechanics these adapters plug into.
- Idempotent Webhook Retry and Signature Verification — the retry and rate-limit guarantees for delivery.
- ELD Identity and HOS Record Linkage — reuses the same adapters to query vendor driver directories.
- Syncing DVIR Severity Scores to Samsara and Geotab via Webhook — a downstream consumer of the normalized event.