TMS · Developer documentation

ELD Integration API

Build an integration once, and any TMS customer can connect your ELD to their fleet. You host a small REST API that conforms to this spec; TMS polls it on a schedule and maps your data onto the customer's trucks and drivers.

Overview

This is a pull integration. There are no webhooks to send and no credentials of ours to manage — you expose read-only endpoints, and TMS calls them. Each TMS customer enters your base URL and an API key you issue them, then TMS polls your API for four resources:

  • Vehicle events — GPS location, odometer, engine hours.
  • Driver info — the driver roster (matched to TMS drivers by CDL number).
  • Driver state — HOS duty-status changes.
  • Documents — certified RODS logs, DVIRs, inspection reports.

You expose one base URL, not one per resource. All paths hang off it ({baseUrl}/v1/vehicles/events, etc.). A single /v1/capabilities endpoint can tell TMS which of the four you support. If it is unavailable, TMS defaults to vehicle events and driver information; HOS intervals and documents remain disabled until explicitly reported.

Authentication

Every request carries a bearer token — the API key the customer pasted into TMS when connecting you. Reject any request without a valid key with 401 and the error envelope below.

GET /v1/capabilities HTTP/1.1
Host: eld.your-company.com
Authorization: Bearer <customer-api-key>
Accept: application/json

Versioning

All endpoints live under a /v1 path prefix. The customer enters only the root (e.g. https://eld.your-company.com); TMS appends /v1/<resource>. Ship breaking changes under a new prefix (/v2) rather than mutating /v1.

GET /v1/capabilities · recommended

This endpoint declares which optional resources you support. TMS calls it when a customer connects and caches the result. A missing or unreachable endpoint no longer blocks connection; TMS uses the safe defaults described above. Authentication failures still fail the connection.

FieldTypeRequiredDescription
providerstringrequiredHuman-readable vendor/company name.
apiVersionstringrequiredAPI version this vendor implements, e.g. 'v1'.
capabilitiesobjectrequiredWhich optional resources this vendor implements.
{
  "provider": "Acme ELD",
  "apiVersion": "v1",
  "capabilities": {
    "vehicle_events": true,
    "driver_info": true,
    "driver_state": false,
    "documents": false
  }
}

GET /v1/vehicles/events

A flat list of telemetry events across all vehicles. Include unitNumber when you can — TMS matches vehicles by unit number and creates a local truck when no match exists. Absolute odometerMiles is preferred over inferring distance from GPS.

FieldTypeRequiredDescription
vehicleExternalIdstringrequiredVendor's own ID for this vehicle.
vinstring | nulloptionalVehicle VIN, if known.
unitNumberstring | nulloptionalFleet unit number — used to auto-match against TMS trucks.
latnumberrequiredLatitude, WGS84.
lngnumberrequiredLongitude, WGS84.
headingnumber | nulloptionalCompass heading in degrees, 0-359.
speednumber | nulloptionalSpeed in mph.
odometerMilesnumber | nulloptionalAbsolute cumulative odometer reading, in miles.
engineHoursnumber | nulloptionalAbsolute cumulative engine hours.
recordedAtstringrequiredISO 8601 timestamp this event was recorded at the vehicle.

GET /v1/drivers

The driver roster. licenseNumber (CDL) is the primary matching key, followed by normalized full name. TMS creates a local driver automatically when no match exists.

FieldTypeRequiredDescription
driverExternalIdstringrequiredVendor's own ID for this driver.
firstNamestringrequiredDriver first name.
lastNamestringrequiredDriver last name.
licenseNumberstring | nulloptionalCDL number — used to auto-match against TMS drivers.
licenseStatestring | nulloptionalCDL issuing state.
phonestring | nulloptionalDriver phone number.
emailstring | nulloptionalDriver email address.
status'active' | 'inactive'requiredWhether this driver is currently active on the vendor side.
updatedAtstringrequiredISO 8601 timestamp this record was last updated.

GET /v1/duty-status

HOS duty-status changes for all drivers in one flat list (not nested per driver, to avoid N+1 polling). Return items in ascending startedAt order — TMS applies intervals sequentially to reconstruct FMCSA clocks. The four status values map exactly to the FMCSA duty states.

FieldTypeRequiredDescription
driverExternalIdstringrequiredVendor's own ID for the driver this interval belongs to.
status'off_duty' | 'sleeper' | 'driving' | 'on_duty'requiredFMCSA duty status at the start of this interval.
startedAtstringrequiredISO 8601 timestamp this duty status began. Items MUST be returned in ascending startedAt order.
latnumber | nulloptionalLatitude at the start of this interval, if known.
lngnumber | nulloptionalLongitude at the start of this interval, if known.
truckExternalIdstring | nulloptionalVendor's own vehicle ID the driver was operating, if known.

GET /v1/documents

ELD-originated documents. Provide the bytes inline as contentBase64 for small files, or a bearer-authenticated contentUrl TMS will fetch — exactly one of the two. documentExternalId is used to de-duplicate on re-sync, so keep it stable.

FieldTypeRequiredDescription
documentExternalIdstringrequiredVendor's own ID for this document — used to avoid re-ingesting it.
driverExternalIdstring | nulloptionalRelated driver, if any.
vehicleExternalIdstring | nulloptionalRelated vehicle, if any.
docTypestring | nulloptionalFree-text document type hint, e.g. 'rods_certified' or 'dvir'.
fileNamestringrequiredOriginal file name, including extension.
contentTypestring | nulloptionalMIME type, e.g. application/pdf.
contentUrlstring | nulloptionalHTTPS URL to fetch the file bytes from (Bearer-authenticated). Exactly one of contentUrl/contentBase64 must be present.
contentBase64string | nulloptionalBase64-encoded file bytes, for small documents. Exactly one of contentUrl/contentBase64 must be present.
createdAtstringrequiredISO 8601 timestamp this document was generated.

Pagination & incremental sync

Every list endpoint accepts two query params and returns a shared envelope:

  • since — ISO-8601 timestamp. Return only records at or after it; omit for a full backfill.
  • cursor — opaque string from a prior response's nextCursor.
{
  "data": [ /* … page of items … */ ],
  "nextCursor": "eyJvZmZzZXQiOjUwfQ==",   // null on the last page
  "serverTime": "2026-07-08T14:05:00Z"     // your clock at response time
}

TMS drives it like this: start with since = the last sync's serverTime (omitted on the first run). While nextCursor is non-null, re-request with that cursor (not since). When it's null, the run is done, and TMS stores the last page's serverTime as the next run's since. Returning serverTime from your own clock avoids drift between our clocks.

Errors & rate limiting

Return this envelope for any non-2xx response:

{ "error": { "code": "unauthorized", "message": "Invalid or missing API key" } }
  • 401 unauthorized — bad or missing bearer token.
  • 403 forbidden — capability not enabled for this key.
  • 404 not_found — unknown resource.
  • 429 rate_limited — set Retry-After; TMS backs off and retries.
  • 5xx internal — TMS retries with exponential backoff.

Reference server

TMS ships a working, spec-conformant implementation of this API at /api/mock-eld/v1/* (the "Reference mock ELD server" a customer can connect in Settings → Integrations). Diff your responses against it — same envelope, same field names, same pagination — to confirm your integration conforms before going live.