> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grdd.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Fleets

> Create, read, update and delete your fleet categories with the Connect API

A **fleet** is a vehicle category that your company offers, for example "Airport Sedan". A fleet has a type, capacity, rates, images and, optionally, linked [vehicles](/connect-api/content/vehicles) and [documents](/connect-api/content/documents).

Read [Connect API conventions](/connect-api/content/api-conventions) first. It explains the base URL, authentication, `Idempotency-Key`, `If-Match`, pending operations and the error format. This page uses `$CONNECT_API_BASE_URL` for the base URL. Ask GRiDD support ([support@grdd.net](mailto:support@grdd.net)) for the Connect API base URL for your environment.

## What is available

The Connect API README describes Fleets and Vehicles as complete production implementations in code. The route handlers validate requests and responses with the contracts in `packages/api-contracts`. This page describes those contracts. It does not say that a given environment serves them.

## Endpoints

### Owner routes

These routes act on the company in your token.

| Method | Path | Purpose | `Idempotency-Key` | `If-Match` |
| - | - | - | - | - |
| `GET` | `/api/fleets` | List your fleets. | – | – |
| `POST` | `/api/fleets` | Create a fleet. | Required | – |
| `GET` | `/api/fleets/{fleetIdOrSlug}` | Read one fleet by numeric ID or by slug. Returns an `ETag`. | – | – |
| `PATCH` | `/api/fleets/{fleetId}` | Update one fleet. | Not used | Required |
| `DELETE` | `/api/fleets/{fleetId}` | Delete one fleet, or one language version. | Not used | Required |
| `POST` | `/api/fleets/operations/{operationId}` | Resume a pending operation. See [Pending operations](/connect-api/content/api-conventions#pending-operations). | – | – |
| `GET` | `/api/fleets/documents` | List links between your fleets and your documents. | – | – |
| `DELETE` | `/api/fleets/documents` | Remove document links from one fleet. | Required | – |
| `GET` | `/api/fleets/vehicle-types` | List vehicle types. Each item has `vehicleType`, `description`, `capacity` and `hideFlg`. Hidden types are not returned. | – | – |

`PATCH` and `DELETE` for a fleet do not read an `Idempotency-Key` header. They use `If-Match` to prevent duplicate or stale changes.

### Other owner routes

These routes use the same authentication. This page does not describe their request and response bodies in full.

| Method | Path | Purpose |
| - | - | - |
| `GET` | `/api/fleets/search` | Search fleets near a point. See [Fleet search (Connect API)](/connect-api/search/fleets). |
| `POST` | `/api/fleets/validate` | Check fleet content (title, description, images) for consistency with the fleet type. The check may be skipped when the provider is not available. Body: `{ "fleet": { ... } }`. |
| `GET` | `/api/fleets/verifications` | Read the verification map of your fleets. |
| `POST` | `/api/fleets/verifications` | Request reservation verification for one fleet. Body: `{ "fleetId": ... }`. The API needs proof from a recent confirmed reservation. If proof is missing, the response is `422 domain_rejected`, or a result that says a test reservation is needed. |
| `GET`, `POST` | `/api/fleets/verifications/test` | Check the test-reservation rate limit, and run a test-reservation verification. |

### Read another company's published fleets

| Method | Path | Purpose |
| - | - | - |
| `GET` | `/api/fleets/public/{griddid}` | Read the published fleets of another company with your Bearer token. Query: `locale`, `force`. |

The response has `items`, and can have `meta`, `lockedCount` and `lockedSlugs`. Each item is a reduced record. It has no document IDs, no vehicle links and no verification data. The API leaves out inactive fleets, and fleets with `private` or `unlisted` visibility. It returns `gnet` fleets to any signed-in caller. It returns `partners` fleets only if core confirms that you are a partner of the company. If core cannot confirm the partnership, the API does not return them. A fleet with no stored visibility value is treated as visible.

### Not for external use

* `GET /api/fleets/published/{griddid}` uses the `Service` credential of the public website renderer.
* `POST /api/fleets/vehicles` and `DELETE /api/fleets/vehicles` are a retired route. They always return `503 temporarily_unavailable` with the message "Vehicle relationship operations are not available". To link vehicles, use the `vehicles` field of a fleet, or [`/api/vehicles/links`](/connect-api/content/vehicles).

## Authentication and access

* Send `Authorization: Bearer YOUR_ACCESS_TOKEN`. See [Authentication](/connect-api/content/api-conventions#authentication).
* The owner routes do not check a role in the token.
* **Subscription.** Creating a fleet checks your subscription, and so does changing an inactive fleet to `active`. In the subscription contract in source, no plan has a numeric limit for fleets. The check refuses a cancelled subscription with `403 entitlement_denied`. An active trial passes. If the plan cannot be determined, the API returns `503 temporarily_unavailable`. Updates and deletes do not count against a limit.
* **Verification.** Changing the fleet `type`, or deleting the fleet, revokes its reservation verification first. The save response has `verificationRevoked: true` when that happens. If the verification state cannot be changed safely, the API returns `409 verification_guard` (`retryable: true`).

## Fleet fields

### Read record

| Field | Type | Notes |
| - | - | - |
| `id` | integer | |
| `slug` | string | |
| `griddid` | string | Your company. |
| `name` | string | |
| `type` | string | Vehicle type code, for example `sedan_standard`. See `GET /api/fleets/vehicle-types`. |
| `display_order` | number or null | |
| `model`, `description` | string or null | |
| `max_passengers`, `max_luggage`, `max_carry_on` | number or null | |
| `image_url` | string or null | Always present. See [Image URLs](/connect-api/content/api-conventions#image-urls). |
| `extra_photos` | array of strings | Always present. |
| `colors` | array | Items are `{ "color": string, "id": string }`. |
| `attributes` | array | Items are `{ "attribute": string, "id": string }`. |
| `page` | integer, string, object or null | Linked page record. |
| `status` | `active` or `inactive` | |
| `visibility` | `private`, `partners`, `gnet`, `unlisted`, `public` | |
| `minimum_rate`, `hourly_rate` | number or null | |
| `documentIds` | array of integers | IDs of linked documents. |
| `vehicles` | array | Items are `{ "vehicle": id, "id": string }`. |
| `verifiedForReservations` | boolean or null | |
| `verificationExpiresAt`, `lastVerificationDate` | number or null | |
| `version` | integer | Use it in `If-Match`. |
| `createdAt`, `updatedAt` | string (ISO 8601) | |

### Write body

All write bodies are strict. Unknown keys, such as `id`, `griddid` or `version`, return `400 invalid_request`.

**Create** (`POST /api/fleets`):

| Field | Type | Required | Notes |
| - | - | - | - |
| `fleet` | object | Yes | The fields below. |
| `locale` | `en`, `es`, `de`, `fr` | No | Language of the new record. |
| `contentLanguage` | object | No | `{ "intent": "create_record", "language": "fr" }`. Takes priority over `locale`. |

Fields in `fleet`:

| Field | Type | Create | Notes |
| - | - | - | - |
| `name` | string | **Required** | At least 1 character. |
| `type` | string | **Required** | At least 1 character. |
| `visibility` | enum | Optional | Default `public`. |
| `slug` | string | Optional | |
| `display_order` | number or null | Optional | |
| `model`, `description` | string or null | Optional | |
| `max_passengers`, `max_luggage`, `max_carry_on` | number or null | Optional | |
| `image_url` | URL or null | Optional | |
| `extra_photos` | array of URLs | Optional | No duplicates. Must not repeat `image_url`. |
| `colors` | array | Optional | Items are `{ "color": "Black" }`. |
| `attributes` | array | Optional | Items are `{ "attribute": "Wi-Fi" }`. |
| `page` | integer, string or null | Optional | |
| `status` | `active`, `inactive` or null | Optional | |
| `minimum_rate`, `hourly_rate` | number or null | Optional | |
| `documentIds` | array of positive integers, or null | Optional | No duplicates. |
| `vehicles` | array of positive integers | Optional | IDs of your vehicles. |

**Update** (`PATCH /api/fleets/{fleetId}`): the same shape. `fleet` can hold any subset of the fields above, and none is required. `contentLanguage` must be `update_version` or `add_version`. Send `If-Match`.

* If you omit a field, the API does not change it.
* `image_url: null` and `extra_photos: []` clear the images.
* `documentIds: null` or `[]` clears the document links. If you omit `documentIds`, the links stay.
* `vehicles` is the full set of vehicle links. An empty list removes all links. If you omit `vehicles`, the links stay. The API checks that every vehicle ID belongs to your company.

**Delete** (`DELETE /api/fleets/{fleetId}`): send `If-Match`. Optional query `locale` and `languageVersion`.

### Image rules

In addition to the [general image URL rules](/connect-api/content/api-conventions#image-urls), a fleet image URL must belong to your company:

* the host is `asset.grdd.dev`, or
* the first path segment is `shared`, `global` or your company ID, or
* the first path segment is `fleet`, `fleets` or `profile` and the second is your company ID.

Another URL returns `403 forbidden`. A rule violation, such as a duplicate gallery URL, returns `422 domain_rejected` with `fieldErrors` for `image_url` or `extra_photos`.

## Create a fleet

```bash theme={null}
curl -X POST "$CONNECT_API_BASE_URL/api/fleets" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3a6c0e52-9d1b-4f27-8b0e-5d7c1a2f4e90" \
  -d '{
    "fleet": {
      "name": "Airport Sedan",
      "type": "sedan_standard",
      "minimum_rate": 75,
      "hourly_rate": 120,
      "image_url": "https://asset.grdd.dev/fleet/demo-company/airport-sedan.webp",
      "extra_photos": [
        "https://asset.grdd.dev/fleet/demo-company/airport-sedan-interior.webp"
      ]
    },
    "locale": "en"
  }'
```

`201 Created` with the header `ETag: "v1"`:

```json theme={null}
{
  "data": {
    "id": 42,
    "name": "Airport Sedan",
    "griddid": "demo-company",
    "type": "sedan_standard",
    "image_url": "https://asset.grdd.dev/fleet/demo-company/airport-sedan.webp",
    "extra_photos": [
      "https://asset.grdd.dev/fleet/demo-company/airport-sedan-interior.webp"
    ],
    "status": "active",
    "visibility": "public",
    "minimum_rate": 75,
    "hourly_rate": 120,
    "documentIds": [],
    "slug": "airport-sedan",
    "version": 1,
    "updatedAt": "2026-08-29T12:00:00.000Z",
    "createdAt": "2026-08-29T12:00:00.000Z"
  },
  "invalidated": [],
  "verificationRevoked": false,
  "status": "completed"
}
```

The save response has `data`, `invalidated`, `verificationRevoked` and `status`. It can also have `operationId` and `effects`. `status` is `completed`, `pending` or `partial`. A `pending` result returns `202`. See [Pending operations](/connect-api/content/api-conventions#pending-operations).

## Read fleets

```bash theme={null}
curl "$CONNECT_API_BASE_URL/api/fleets?locale=en" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

Response `200`. The record below is the fixture `fleet-list.json`:

```json theme={null}
{
  "items": [
    {
      "id": 42,
      "name": "Airport Sedan",
      "griddid": "demo-company",
      "type": "sedan_standard",
      "display_order": 10,
      "image_url": null,
      "extra_photos": [],
      "status": "active",
      "visibility": "public",
      "documentIds": [],
      "slug": "airport-sedan",
      "version": 1,
      "updatedAt": "2026-08-29T12:00:00.000Z",
      "createdAt": "2026-08-29T12:00:00.000Z"
    }
  ]
}
```

The list has no pagination parameters. `GET /api/fleets/42` (or `/api/fleets/airport-sedan`) returns one fleet and an `ETag`. A fleet that does not exist for your company returns `404 not_found`.

## Update a fleet

```bash theme={null}
curl -X PATCH "$CONNECT_API_BASE_URL/api/fleets/42" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "If-Match: \"v1\"" \
  -d '{
    "fleet": {
      "name": "Airport Sedan Updated",
      "hourly_rate": 130,
      "image_url": null,
      "extra_photos": []
    }
  }'
```

`200 OK` with the header `ETag: "v2"`. This is the fixture `fleet-patch-response.json`:

```json theme={null}
{
  "data": {
    "id": 42,
    "name": "Airport Sedan Updated",
    "griddid": "demo-company",
    "type": "sedan_standard",
    "status": "active",
    "visibility": "public",
    "slug": "airport-sedan",
    "image_url": null,
    "extra_photos": [],
    "documentIds": [],
    "version": 2,
    "updatedAt": "2026-08-29T12:05:00.000Z",
    "createdAt": "2026-08-01T12:00:00.000Z"
  },
  "invalidated": ["fleet-redis:demo-company"],
  "verificationRevoked": false,
  "status": "completed"
}
```

If the stored version is newer than `If-Match`, the response is `412`:

```json theme={null}
{
  "code": "version_conflict",
  "message": "The resource version is stale",
  "correlationId": "05f953af-c79d-49b4-b013-f451354b2725",
  "retryable": false,
  "currentVersion": 2
}
```

## Delete a fleet

```bash theme={null}
curl -X DELETE "$CONNECT_API_BASE_URL/api/fleets/42" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "If-Match: \"v2\""
```

* `204 No Content` when the delete is complete.
* `202` when the fleet record is deleted but follow-up work is not finished. The body is the fixture `fleet-delete-pending.json` (see [Pending operations](/connect-api/content/api-conventions#pending-operations)).

Resume a pending operation with `POST /api/fleets/operations/{operationId}`. The fixture `fleet-operation.json` shows a response that is still pending:

```json theme={null}
{
  "operationId": "fleet-00000000-0000-4000-8000-000000000042",
  "status": "pending",
  "effects": [
    {
      "name": "vehicle_links",
      "status": "failed",
      "retryable": true,
      "problemCode": "partial_success"
    }
  ]
}
```

## Errors

Error bodies use the [problem format](/connect-api/content/api-conventions#errors). These codes come from the fleet routes.

| Status | `code` | When |
| - | - | - |
| 400 | `invalid_json`, `invalid_request` | The body or query is not valid. Includes unknown keys, a bad `If-Match` or `Idempotency-Key` value, and an image URL that fails the URL rules. |
| 401 | `unauthenticated` | The token is missing, invalid or expired. |
| 403 | `entitlement_denied` | The subscription does not allow creating a fleet. |
| 403 | `forbidden` | An image URL is outside your company namespace. |
| 404 | `not_found` | No fleet with this ID or slug for your company, or no such operation. |
| 409 | `conflict` | The change conflicts with the current state, for example a language rule. |
| 409 | `verification_guard` | Verification state could not be changed safely. Retryable. |
| 409 | `idempotency_conflict` | The key was used for a different request. |
| 412 | `version_conflict` | `If-Match` is stale. The body has `currentVersion`. |
| 413, 415 | `payload_too_large`, `unsupported_media_type` | Body size or media type. |
| 422 | `domain_rejected` | An image rule was violated. |
| 428 | `version_required`, `idempotency_key_required` | A required header is missing. |
| 202 | `partial_success` | An idempotent retry found the first request still running. Use `operationId`. |
| 503 | `temporarily_unavailable` | Core, the cache or the data store is not available, or the plan cannot be determined. |
| 500 | `internal_error` | Unexpected error. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.