> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.fleetwire.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.fleetwire.io/_mcp/server.

# Overview

# Fleetwire API

Read your fleet's listings, availability and bookings, and block dates or change booking status from your own systems.

## Authentication

Create a token in the dashboard under **Settings → Security → API Tokens**: [https://app.fleetwire.io/settings/security/api-tokens](https://app.fleetwire.io/settings/security/api-tokens)

Send it as a bearer token on every request:

```
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
```

A token acts as the user who created it, scoped to that user's company. Treat it like a password: keep it server-side, never put it in browser or mobile code, and delete it from the dashboard if it leaks.

## Token scopes

When you create a token you choose its access:

| Access      | Scopes                       | Can                                              |
| ----------- | ---------------------------- | ------------------------------------------------ |
| Full access | `*`                          | Everything the user can do. Use this for Zapier. |
| Read only   | `read`                       | Every `GET` endpoint                             |
| Custom      | `read` plus any of the below | Read, plus the writes you pick                   |

Write scopes:

* `bookings:write` — booking status, blocked dates, notes, payment requests
* `customers:write` — create and update customers
* `webhooks:write` — create, change and delete webhook endpoints
* `pricing:write` — set and clear per-day listing prices

A request outside the token's scopes gets `403` with the missing scope in `message`. Scopes narrow a token; they never grant more than the user who created it has.

## Plan requirement

The API is available on the **Premium** plan (and during a free trial). Other plans receive `402 Payment Required`:

```json
{ "success": false, "message": "No subscription", "errors": ["You do not have an active subscription"] }
```

## Base URL

`https://api.fleetwire.io/api/v2`

## Rate limits

200 requests per minute per token. Over the limit you receive `429 Too Many Requests` with a `Retry-After` header.

## Errors

| Status | Meaning                                                 |
| ------ | ------------------------------------------------------- |
| 401    | Missing or invalid token                                |
| 402    | Company's plan doesn't include API access               |
| 403    | The token's user lacks permission for this action       |
| 404    | Record not found, or it belongs to another company      |
| 422    | Validation failed — see `errors` for the field messages |
| 429    | Rate limited                                            |

Validation errors look like:

```json
{ "message": "The trip end field is required.", "errors": { "trip_end": ["The trip end field is required."] } }
```

## Dates and times

`trip_start` / `trip_end` are local wall-clock times (`YYYY-MM-DD HH:MM:SS`) in the listing's timezone, returned alongside as `timezone`. `created_at` / `updated_at` are UTC ISO 8601. Money values are in **cents**.

## Webhooks

Rather than polling, register an endpoint (see the **Webhooks** folder) and Fleetwire POSTs a JSON body to it when something happens. Events:

| Event                    | When                                                                                        |
| ------------------------ | ------------------------------------------------------------------------------------------- |
| `reservation.created`    | A booking is made, or a request is approved                                                 |
| `reservation.requested`  | A booking request needs your approval                                                       |
| `reservation.canceled`   | A booking is canceled                                                                       |
| `booking.updated`        | Dates, vehicle, customer or total change. `changed` lists the fields.                       |
| `booking.status_changed` | Any status change. Includes `previous_status`.                                              |
| `trip.started`           | A booking is marked started                                                                 |
| `trip.ended`             | A started trip is returned or completed                                                     |
| `payment.processed`      | A payment succeeds                                                                          |
| `payment.failed`         | A charge is declined                                                                        |
| `payment.refunded`       | A payment is refunded                                                                       |
| `customer.created`       | A customer is added                                                                         |
| `customer.updated`       | A customer's name, email, phone, birth date or address changes. `changed` lists the fields. |

Booking events deliver the booking's `b_id`, `l_id`, `r_id`, `status`, `source`, `days`, `total` (cents), `trip_start`, `trip_end`, `timezone` and `listing_name`. Fetch anything else with `GET /bookings?b_id=…`.

**Headers on every delivery**

* `fleetwire-event` — the event name
* `fleetwire-event-id` — unique per event and the same on every retry; use it to ignore duplicates
* `fleetwire-occurred-at` — ISO 8601
* `fleetwire-delivery-attempt` — 1 to 5
* `fleetwire-signature` — `t=<unix time>,v1=<hex HMAC>`

**Verifying the signature.** Compute HMAC-SHA256 of `<t>.<raw request body>` with the endpoint's signing secret (`whsec_…`, returned once when you create the endpoint) and compare it with `v1` using a constant-time comparison. Reject deliveries whose `t` is more than 5 minutes old.

```js
const [t, v1] = header.split(',').map((part) => part.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
```

**Retries.** Answer with any `2xx` within 30 seconds. A `5xx`, `408`, `425`, `429` or a timeout is retried up to 5 attempts over about 20 minutes (a numeric `Retry-After` is honoured). Other `4xx` responses are not retried. Every attempt appears in `GET /webhooks/:id/deliveries`.

## Zapier

Prefer no-code? The Fleetwire Zapier app covers new bookings, new customers, trip reminders and more without writing code.

## Partner API

For toll, violation and fee services working with many Fleetwire hosts. Partner endpoints live under `/partners/v1` and take a partner key (`fwp_…`), not a host API token. Fleetwire issues partner keys; email [hi@fleetwire.io](mailto:hi@fleetwire.io). Sandbox: `https://staging.fleetwire.io/api/partners/v1`.

One API for every toll, violation and fee service that bills rental companies' renters through Fleetwire.

Each partner gets one key and can work with every Fleetwire rental company that turned the partner on
(in Fleetwire: **Connections → Toll and violation partners**). Rental companies don't create tokens or paste keys.

**Fleetwire bills the renter.** A charge you send is added to the rental's order and invoiced to the renter
by the rental company's own rules. You don't charge renters directly, and you never see renter names or
contact details.

### Flow

1. A rental company turns your integration on.
2. `GET /companies` lists the companies that have you on.
3. `GET /companies/{company_id}/vehicles` gives the plates to watch.
4. `GET /companies/{company_id}/rentals` (optional) says which rental had a plate at a moment.
5. `POST /companies/{company_id}/charges` sends a toll, violation or fee, with its receipt.
6. `PATCH /companies/{company_id}/charges/{external_id}` changes it later.

### Conventions

* **Money:** whole cents as integers, USD unless `currency` says otherwise. `$2.50` is `250`.
* **Times:** ISO 8601 with a UTC offset or `Z`.
* **Plates:** send them as printed. We compare case-insensitively, ignoring spaces and dashes.
* **Idempotency:** charges are keyed on your `external_id`, so sending the same charge twice is safe.
* **Rate limit:** 600 requests per minute per key. Over the limit you get `429` with `Retry-After`.
* **Turned off:** when a company turns you off, every endpoint for it answers `404 company_not_connected`
  at once. Stop sending for that company. Charges you already sent stay on their rentals.

### Planned

Webhooks from Fleetwire (`company.activated`, `company.deactivated`, `vehicle.added`, `vehicle.removed`,
`vehicle.plate_changed`, `rental.ended`). Until then, poll `GET /companies` and `GET /vehicles` (daily is plenty).