---
title: "List vehicles"
method: GET
path: "/v1/vehicles"
tags: ["Vehicles"]
---

# List vehicles

`GET /v1/vehicles`

## Request

- Base URL: `https://api.officina.it`
- URL: `https://api.officina.it/v1/vehicles`
- Auth: HTTP bearer

Returns vehicles in the authenticated user's company, newest
first. Search by name with `?q=`; filter to a single customer
with `?customer_id=`; paginate with cursor-based `?limit=` +
`?starting_after=`.

The cursor is opaque — clients MUST NOT parse it. Pass back
the value verbatim. Today it encodes an offset; tomorrow it
may encode a `(created_at, id)` tuple without a client-visible
change.

`has_more` is `true` when more results exist beyond this page.
Drive the "load more" UI off this flag, not off `data.length`
— the last page may still contain a full `limit` worth of
results.

## Query parameters

- `q` string
- `customer_id` string
- `sort` 'date' | 'customer' | 'vehicle' | 'license'
- `direction` 'asc' | 'desc'
- `limit` integer
- `starting_after` string

## Response `200`

Page of vehicles.

- object
  - `object` 'list', required: Polymorphic discriminator.
  - `data` Vehicle[], required: Page of items. Each item carries its own `object` field.
    - `object` 'vehicle', required
    - `id` string, required
    - `license` string, nullable: License plate (regional format, free-form string).
    - `manufacturer_id` string, required
    - `manufacturer_name` string, required: Joined from the manufacturers table for display. Read-only; mutate via `manufacturer_id`.
    - `model` string, required
    - `variant` string, nullable
    - `vin` string, nullable: Vehicle Identification Number.
    - `transmission` string, nullable
    - `engine_power_kw` integer, nullable
    - `engine_displacement_ccm` integer, nullable
    - `engine_code` string, nullable: Codice motore. Stored trimmed and upper case (max 64 characters); an empty string or `null` clears it. When empty, officina.it may fill it from the vehicle's registration data. Shown in the app when `field_visibility.engine_code` is on.
    - `vehicle_weight_kg` integer, nullable
    - `engine_category` string, nullable
    - `registered_at` string, date-time, nullable
    - `comments` string, nullable
    - `customer_id` string, nullable
    - `customer_name` string, nullable: Joined from the owning customer's `full_name` for display. Read-only; mutate via `customer_id`.
    - `km` number, nullable: Most recently recorded odometer reading.
    - `km_registered_at` string, date-time, nullable
    - `last_inspection_at` string, date-time, nullable
    - `next_inspection_at` string, date-time, nullable
    - `next_appointment` string, date-time, nullable: Earliest upcoming calendar appointment for this vehicle, computed at read time.
    - `custom_fields` object, nullable: Raw stored values keyed by custom field definition `id` (see `VehicleCustomFieldValues` for the stored shapes). Includes hidden fields, and on older records keys of deleted definitions (dropped on the next PATCH that sends `custom_fields`).
    - `custom_field_values` object[]: Detail responses only. The company's visible custom field definitions (settings → Campi veicolo) resolved against this vehicle: configured order, display-ready names, "" when the vehicle has no value. Clients render these directly — no definition lookup needed.
      - `id` string, required
      - `name` string, required
      - `type` 'string' | 'integer' | 'datetime', required
      - `value` string, required
    - `documents` VehicleDocument[]: Detail responses only — the vehicle's Documenti Allegati. `url` is the authenticated download route (`/v1/vehicles/{id}/documents/{documentID}/download`).
      - `object` 'vehicle_document', required
      - `id` string, required
      - `name` string, required: Original file name.
      - `content_type` string, required
      - `description` string, nullable, required
      - `url` string, required
      - `file_size_bytes` integer, required
      - `created_at` string, date-time, required
    - `field_visibility` object: Detail responses only. The company's optional-field toggles (settings → Campi veicolo): the edit form hides — and omits from its PATCH — exactly what the web form hides. All true when never configured.
      - `km` boolean
      - `numero_telaio` boolean
      - `transmission` boolean
      - `engine_category` boolean
      - `engine_power_kw` boolean
      - `engine_displacement_ccm` boolean
      - `engine_code` boolean
      - `vehicle_weight_kg` boolean
      - `registration_date` boolean
      - `last_inspection_date` boolean
      - `next_inspection_date` boolean
    - `is_rental` boolean, required: Whether this vehicle is part of the workshop's rental fleet. Read-only here — managed by the rental setup flow.
    - `rental_daily_price_before_tax` number, nullable
    - `rental_daily_price_tax` number, nullable
    - `rental_daily_price_after_tax` number, nullable
    - `rental_vat_code` string, nullable
    - `rental_daily_discount_rate` number, nullable
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `deleted_at` string, date-time, nullable: Soft-deletion timestamp. Populated on the response from DELETE /v1/vehicles/{id}. Vehicles with a non-null deleted_at are not returned by list/detail endpoints — this field appears for the delete response only.
  - `has_more` boolean, required: True if more results exist beyond this page. Use this for the "load more" UI — `data.length` alone is unreliable because a full page may still be the last page.
  - `next_starting_after` string: Opaque cursor for the next page. Present (and non-empty) when `has_more` is `true`; omitted otherwise. Pass the value verbatim as `?starting_after=` on the follow-up request. Clients MUST NOT parse it — today it encodes an offset, tomorrow it may encode a `(created_at, id)` tuple without a wire-visible change.
  - `url` string, required: The request path that produced this list.

## Other responses

- `401`: Missing, malformed, expired, or wrong-audience bearer token.
- `402`: The company has no active subscription, or its plan does not include API access. API access is included in the Business and Illimitato plans.
- `403`: The authenticated user lacks the required permission.
- `415`: Content-Type is not `application/json`, or Accept rejects JSON.
- `422`: A request field is missing or invalid (fast-fail).
- `500`: Unhandled server error. Detail is logged, not exposed.

---

[API](https://skmtc.dev/officina/apis/officina-it-api.md) · [All operations](https://skmtc.dev/officina/apis/officina-it-api/llms.txt) · [OpenAPI document](https://skmtc.dev/officina/apis/officina-it-api/revisions/7da3fccfe551?raw)
