# Conventions

> The rules every endpoint follows: URLs, response shapes, paging, filtering, sorting and
> data formats.

## Base URL

```text title="Base URL"
https://api.parklio.com/v2
```

Every endpoint is under `/v2`; there's no other version. An unknown path under `/v2`
returns **403** with error code `13` ("Route not found").

Authenticate every request with `Authorization: Bearer <your-api-token>`; see
[Authentication](/api/authentication).

## Responses

Every response is JSON with a `success` flag.

```json title="A single resource"
{ "success": true, "data": { "id": 123, "name": "Spot 12" } }
```

```json title="A list"
{
  "success": true,
  "data": [{ "id": 123, "name": "Spot 12" }],
  "meta": {
    "total": 42,
    "per_page": 10,
    "current_page": 1,
    "last_page": 5
  }
}
```

```json title="Deletes, uploads and similar actions"
{ "success": true, "message": "<what happened>" }
```

Errors have `success: false` and an `errors` array; see [Errors](/api/errors).

Successful calls return a **2xx** status, but not always the same one: some creates
return `201`, others `200`. Check for any 2xx, or for `success: true`.

## Paging

List endpoints are paged with two query parameters:

| Parameter | Default | Rules |
| --- | --- | --- |
| `page` | `1` | Starts at 1. |
| `size` | `10` | Items per page, at most **300**. |

Page through with `meta.current_page` and `meta.last_page`:

```bash title="Second page of products, 50 per page"
curl "https://api.parklio.com/v2/products?page=2&size=50" \
  -H "Authorization: Bearer $PARKLIO_API_TOKEN"
```

:::warning Don't use the `*_page_url` fields
`meta` also has `first_page_url`, `last_page_url`, `next_page_url` and
`previous_page_url`. They're placeholders, not full URLs, and `next_page_url` can point
past the last page. Stop when `current_page` reaches `last_page`.
:::

## Filtering

Filter a list with `filter=<field>:<rule>:<value>`. Repeat `filter` to combine filters;
all must match.

```bash title="Products in zone 9 whose name contains 'spot'"
curl "https://api.parklio.com/v2/products?filter=zone_id:eq:9&filter=name:like:spot" \
  -H "Authorization: Bearer $PARKLIO_API_TOKEN"
```

| Rule | Meaning |
| --- | --- |
| `eq`, `ne` | Equal, not equal. The values `null`, `true` and `false` are read as such. |
| `gt`, `gte`, `lt`, `lte` | Greater or less than. Numbers, or ISO-8601 dates. |
| `like` | Contains the value. At most **2** `like` filters per request. |
| `in`, `nin` | In, or not in, a **comma-separated** list: `id:in:1,2,3`. |
| `rl`, `nrl`, `hr` | Relation checks, used by a few endpoints' documented filters. |

Rules for values and fields:

- Each endpoint accepts only the fields listed in its reference page.
- Values may contain only letters, digits and `_ , . @ + : -`: no spaces. Send a
  literal `+` as `%2B`.
- An invalid field, rule or value returns **400** with error code `33`, and a message
  naming the problem.

:::warning Unknown query parameters become filters
On list endpoints that support filtering, any query parameter that isn't `page`, `size`,
`sort`, `filter`, `sort_by_direction` or `timezone` is turned into an `eq` filter. So a
misspelled parameter, such as `?zone=9`, returns **400** (code `33`) instead of being
ignored.
:::

Some fields filter through a relation. For example, on
[`GET /v2/products`](/api/endpoints/products-find-all), `lot_id` matches the lot of the
product's **zone**.

## Sorting

Sort a list with a single `sort=<field>:asc` or `sort=<field>:desc` (lowercase). Each
endpoint lists its sortable fields; any other field returns **400** (code `33`).

```bash title="Products by name"
curl "https://api.parklio.com/v2/products?sort=name:asc" \
  -H "Authorization: Bearer $PARKLIO_API_TOKEN"
```

Send `sort` **once**. Repeating it fails.

## Data formats

- **Field names are snake_case** in responses. Send snake_case in request bodies too.
- **Many booleans are `0` or `1`** in responses, for example `is_autoclose` and the
  `product_error` flags. In request bodies, send JSON `true`/`false` unless an endpoint
  says otherwise.
- **Dates and times are ISO-8601 in UTC**, for example `2026-01-15T09:30:00.000Z`.
- **Request bodies are limited to 1 MB.**

## Rate limits

100 requests per second per IP address. See [Rate limits](/api/rate-limits).
