MyEZToll

Partner API

A REST API for rental operators that bill their own renters and need our data and management surface: priced tolls and violations per vehicle, plus cars, drivers, bookings, transponders and GPS devices.

Base URLs

https://owner.myeztoll.com

Production. Partner endpoints are served under the branded host — no raw API host to configure.

https://dev-owner.myeztoll.com

Sandbox for integration testing. Same surface, separate data, separate API keys.

Build against the sandbox first: same surface, separate data, separate keys. Nothing you create there is billed or charged.

Response envelope

Every endpoint answers with the same three fields, so one wrapper in your client handles all of them.

Paging

Every list endpoint returns a page, never a bare array: data is { items, page, pageSize, total, hasMore }. Pass ?page=1&pageSize=100 — pages are 1-based, pageSize defaults to 100 and is capped at 1000. total counts the whole filtered set, so keep asking for the next page while hasMore is true. The short reference lists (/car-types, /states, /toll-providers) stay plain arrays.

Money

Toll and violation rows separate the road cost from our fee: agencyAmount (tolls) and amount (violations) are exactly what the agency charged — the figure you bill your renter — while serviceFee is our flat per-transaction fee, never folded into the road cost. Amounts are in currency (USD unless stated).

Did the renter pay it back?

Every toll and violation row carries renterCharge. Its status is one of not_billed, estimate, pending, charged, declined, invoiced, reimbursed_by_platform, paid_cash, paid_manually or owner_absorbed. amount is what the renter is asked for; paidAmount and chargedAtUtc appear once it is collected, and paymentId names the card charge — tolls collected together share it. A coarse failureReason, the next automatic retry (nextAttemptAtUtc) and collectionsStatus — set while an unpaid debt is with the collections process — complete the picture. Rows inside a booking also carry bookingSource (turo, hqrental, own, …) and bookingSourceReservationId, for example the Turo trip id.

Dates

All timestamps are UTC instants in ISO-8601 (2026-07-31T14:05:00Z). In range queries from is inclusive and to is exclusive; omit them and you get the last 30 days.

GPS & telematics

If your cars carry a GPS device — ours, or one we read from your telematics provider — the /gps endpoints hand you the same feed our own fleet map uses: where every car is right now, the track and the mileage of a car or of a single rental, and whether a device is still healthy. Raw positions are kept for 30 days, so read a rental track or mileage while it is fresh; the priced GPS tolls derived from those tracks are permanent and live with the rest of the money data at /tolls?source=gps.

Integrations: one token, many owners

A platform that serves many owners — a fleet-accounting product, for example — receives ONE integration token instead of a key per owner, and may act only for owners who connected it themselves in their owner portal. Name the owner of each request in the X-Owner-Id header; list the owners you may act for with GET /owners, or receive owner.connected / owner.disconnected webhooks. Without X-Owner-Id only /me, /owners and /webhooks answer — every other endpoint returns 400; an owner who never connected you, or has disconnected, returns 401. For an owner key X-Owner-Id is ignored.

Disputes on your own Stripe account

If you charge your renters on your own Stripe account, we answer their disputes with you. Report each charge with POST /charges — which tolls, violations and fees it covered. When Stripe opens a dispute on it, POST /disputes. We collect the evidence — toll and violation report, driver’s licence, rental agreement, GPS track, messages, and for a duplicate claim the renter’s charge history — and send dispute.evidence_ready. GET /disputes/{id} returns evidence keyed by Stripe’s own evidence field names: pass the text fields through unchanged, and for each { fileId } download the file, upload it to your Stripe account with purpose dispute_evidence and put the resulting file_… id in its place. Then report submitted, and later won or lost, with PATCH /disputes/{id}. We never call your Stripe account.

Webhooks

Instead of polling, register an HTTPS endpoint with POST /webhooks. We POST one JSON event per change — { id, type, createdAtUtc, ownerId, data } — of the types toll.created, toll_charge.updated, violation.created, violation.charge_updated, owner.connected and owner.disconnected. Every call is signed: X-MyEzToll-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")> — verify it with the secret returned when you registered the endpoint, and reject stale timestamps. Answer 2xx quickly; anything else is retried with exponential back-off for about 1.5 days. De-duplicate on the event id, also sent as X-MyEzToll-Event-Id.

Disputes add dispute.evidence_ready, dispute.evidence_failed and dispute.deadline_approaching.

Verifying the signature (Node.js)
const crypto = require('crypto');

// rawBody: the exact request body as received, before any JSON parsing.
function isGenuine(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(signatureHeader.split(',').map((p) => p.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false; // stale

  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(parts.v1 || '', 'hex');
  return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}

Your own ids

Create calls accept an optional externalId — your own identifier for the car, driver or booking. Once set, you can pass your id anywhere ours is accepted, and responses carry it back, so you never have to store a second id map.

Get started

There are two ways to use the API, and the first one is smaller than most integrators expect. Pick the track that matches how much of the rental you want us to know about.

Track A — cars only, no bookings and no drivers

You keep the rental in your own system and only ask us what a plate cost.

  1. 1Ask support for a sandbox key and a production key.
  2. 2Register your cars — or match them to existing ones — with POST /cars.
  3. 3Read GET /cars/{carId}/tolls?from=&to= and GET /cars/{carId}/violations?from=&to= for one vehicle, or GET /tolls?from=&to= and GET /violations?from=&to= for the whole fleet. Bill your renters from the amounts.
Track B — add bookings so tolls land on the right rental

Worth it when you want us to say WHO was driving, and when you want GPS tolls.

  1. 1Do steps 1 and 2 above.
  2. 2Create drivers with POST /drivers and rentals with POST /bookings.
  3. 3Read GET /bookings/{bookingId}/tolls per rental, and still poll GET /tolls / GET /violations for anything outside a booking.
Booking tolls vs. period tolls — what actually differs
Both return the same row shape and the same money. A period read (/tolls, /cars/{carId}/tolls) answers “what did this plate cost between these dates” and returns AGENCY tolls only — the ones the toll operator billed to the vehicle. A booking read (/bookings/{bookingId}/tolls) answers “what did this rental cost”: the agency tolls inside the rental window PLUS the GPS tolls computed from the vehicle track, which exist only inside a booking. So the period route never contains GPS tolls — not because they are filtered out, but because without a rental there is nothing to attribute them to.
Who charges the renter depends on the owner account. If you charge your own renters, our automatic charging stays off for that owner and we never touch your customers’ cards. If we charge them, renterCharge on every toll and violation tells you whether and when the renter paid.

Make your first call: list tolls