# Parklio Developer Docs > Control Parklio parking hardware and share access through the Parklio REST API and realtime events. This file contains all documentation content in a single document following the llmstxt.org standard. ## Authentication The Parklio API authenticates every request with a bearer token. Integrations use an **API token**: you create it **once**, while logged in as your account's **Super Admin**, and then send it on every request. ### 1. Log in once, as the Super Admin Only a Super Admin user can create API tokens. Log in with the Super Admin's email and password (see [Account Setup](/get-started/what-you-need) if you don't have them yet) using [`POST /v2/auth/login`](/api/endpoints/auth-login): ```bash title="Log in (one time)" curl -X POST https://api.parklio.com/v2/auth/login \ -H "Content-Type: application/json" \ -d '{"email": "admin@example.com", "password": ""}' ``` The response's `data.token` is a **user token**. Use it only for the next step. :::danger Never log in with a person's credentials at runtime Logging in deletes that user's other tokens of the same kind. If your integration logs in "as" someone who uses the Parklio Connect app, it logs them out of the app. Log in once, by hand, to create an API token, and let your integration use only the API token. ::: ### 2. Create an API token Using the user token from step 1, call [`POST /v2/api-tokens`](/api/endpoints/api-token-create). `name` is a label for you, 3–255 characters. ```bash title="Create an API token" curl -X POST https://api.parklio.com/v2/api-tokens \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"name": "my-integration"}' ``` ```json title="Response" { "success": true, "data": { "id": 7, "name": "my-integration", "account_id": 42, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "token": "" } } ``` :::warning Save the token now The `token` field is shown **only in this response**; Parklio stores only a hash of it. API tokens **don't expire** and have no scopes. Treat the token like a password, and keep it on your server. Creating a new token doesn't invalidate old ones, so revoke the ones you no longer use with [`DELETE /v2/api-tokens/{id}`](/api/endpoints/api-token-remove). ::: An API token can't create other API tokens; only a Super Admin's user token can. ### 3. Call the API Send the API token as a bearer token on every request: ```bash title="Call the API" curl https://api.parklio.com/v2/products/123 \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" ``` Requests made with an API token act as your account's **first Super Admin**, so they see the lots that Super Admin is assigned to. ### Realtime uses the same token To receive realtime events, pass the same API token in the Socket.IO handshake query (`?token=`). See [Connecting](/realtime/connecting). ### Next step See [Errors](/api/errors) for how failures are shaped, the [API Reference](/api) for the endpoints, or [Connecting](/realtime/connecting) to receive realtime events. --- ## 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 `; 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": "" } ``` 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=::`. 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=:asc` or `sort=: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). --- ## Errors Every failed request returns `success: false` and an `errors` array, alongside an HTTP status code. ```json title="Error response" { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` Validation errors (HTTP 422, code `23`) add a `field` property naming the request field that failed, with one entry per field: ```json title="Validation error" { "success": false, "errors": [ { "code": 23, "message": "email must be an email", "field": "email" } ] } ``` ### Branch on `code`, not on the HTTP status Most domain errors use HTTP **403**, whatever the cause: an operation already in progress, a missing plan feature or an unknown route all return 403. The HTTP status alone doesn't tell you what went wrong; the `code` does. Don't parse `message`; it's meant for humans and its wording may change. ### The codes you'll meet first | Code | When | | --- | --- | | `1` | Missing, invalid or revoked token (401). | | `23` | The request body failed validation (422); see `field`. | | `30` | Your subscription doesn't include the feature this endpoint needs, for example remote control (403). See [Plans and permissions](/concepts/plans-and-permissions). | | `33` | A malformed request, for example an invalid filter or an unknown query parameter (400). See [Conventions](/api/conventions#filtering). | | `214` | An earlier operation on this device hasn't finished (403). Wait for its result before sending another. | | `704` | No Gateway has seen the device recently, so a command can't be delivered (403). | A command that was accepted can still fail on the device. That failure arrives as a realtime [`gateway_error`](/realtime/events#gateway_error) event, not as an HTTP error; see [Asynchronous operations](/concepts/asynchronous-operations). A few codes are shared by two different errors (for example `101` and `301`); the table lists each one. ### All error codes #### General | Code | HTTP status | Message | | --- | --- | --- | | `0` | 500 Internal Server Error | Something went wrong and we couldn't process your request. | | `1` | 401 Unauthorized | Unauthorized access. | | `2` | 401 Unauthorized | Unauthorized access. | | `3` | 401 Unauthorized | Authorization header value is missing. | | `4` | 403 Forbidden | No permission. | | `5` | 429 Too Many Requests | Too many attempts. | | `6` | 404 Not Found | Resource not found. | | `7` | 404 Not Found | Table doesn't exist or not set. | | `8` | 403 Forbidden | Wrong email/password combination. | | `9` | 403 Forbidden | Email not confirmed. | | `10` | 422 Unprocessable Entity | Invalid URL signature. | | `11` | 403 Forbidden | Not allowed for <role> account role. | | `12` | 403 Forbidden | Weblink generation not possible; Product doesn't have any gateways. | | `13` | 403 Forbidden | Route not found. | | `13` | 404 Not Found | Route not found. | | `14` | 403 Forbidden | Cache error. | | `15` | 403 Forbidden | Can't set start time to future date. | | `16` | 403 Forbidden | Not allowed for <role> account user role. | | `17` | 403 Forbidden | You are not a parking management system user. Please contact customer support for more info. | | `18` | 403 Forbidden | This resource already exists. | | `19` | 403 Forbidden | Not a valid json. | | `20` | 422 Unprocessable Entity | 48h time frame for password reset has expired, please request another password reset. | | `21` | 403 Forbidden | Resource not assigned to lot. | | `23` | 422 Unprocessable Entity | Validation error | | `24` | 403 Forbidden | Wrong old password. | | `25` | 403 Forbidden | Account with that account_id is not a PMS user. | | `26` | 422 Unprocessable Entity | 48h time frame account activation has expired. Contact your admin. | | `27` | 422 Unprocessable Entity | The file you uploaded is malformed | | `28` | 400 Bad Request | No file was uploaded | | `29` | 400 Bad Request | Not enough products to subscribe | | `30` | 403 Forbidden | Account missing proper authorization | | `31` | 400 Bad Request | You cannot mix subscriptions and licenses | | `32` | 422 Unprocessable Entity | License has all product zeroed | | `33` | 400 Bad Request | This is a generic bad request error (the message varies) | | `34` | 403 Forbidden | Please reset your password. | | `35` | 403 Forbidden | Wrong email/password combination. | | `36` | 404 Not Found | Email not found. | | `37` | 401 Unauthorized | No token provided. | | `38` | 400 Bad Request | malformed json | | `40` | 503 Service Unavailable | the message to the remote device timed out | | `41` | 401 Unauthorized | User needs to be authorized | | `43` | 400 Bad Request | Logout is only allowed for account users. | | `44` | 400 Bad Request | Too many failed attempts | | `45` | 400 Bad Request | Lots filter is required | #### Gateways | Code | HTTP status | Message | | --- | --- | --- | | `100` | 403 Forbidden | Communication with gateway failed. Please try again later. | | `101` | 404 Not Found | Gateway is not connected. | | `101` | 404 Not Found | Gateway not found. | | `102` | 403 Forbidden | Please try again with another signed challenge. | | `103` | 403 Forbidden | This gateway can't be used. | | `104` | 403 Forbidden | Gateway owner already set. | | `105` | 403 Forbidden | Invalid challenge. | #### Products and licenses | Code | HTTP status | Message | | --- | --- | --- | | `200` | 403 Forbidden | Communication with product failed. Please try again later. | | `201` | 404 Not Found | Product not found. | | `202` | 403 Forbidden | Wrong product signature. | | `204` | 403 Forbidden | Not a v2 barrier. | | `205` | 403 Forbidden | Master key already exists. | | `206` | 403 Forbidden | Master key not found. | | `209` | 404 Not Found | Product not found. | | `210` | 403 Forbidden | Product operation doesn't exist. | | `211` | 403 Forbidden | Product can't be assigned to a lot that doesn't have any gateways. | | `212` | 403 Forbidden | Product weblink not valid. | | `213` | 403 Forbidden | Product can't handle this request. | | `214` | 403 Forbidden | Another product operation is in process. | | `215` | 403 Forbidden | Product weblink is not yet valid or has expired. | | `216` | 403 Forbidden | Product update properties are missing. | | `217` | 403 Forbidden | You have reached maximum products for this license. | | `218` | 403 Forbidden | You don't have licenses for this product. | | `219` | 403 Forbidden | You can't reduce license purchased count. | | `220` | 403 Forbidden | You can't add lpr and product at the same time. | | `221` | 403 Forbidden | You can't add license without any products. | | `222` | 403 Forbidden | You can't purchase license that is unavailable. | | `223` | 403 Forbidden | Lpr camera owner already set. | | `224` | 403 Forbidden | You can't update license that has been purchased. | | `225` | 403 Forbidden | License with those products already exists. | | `226` | 403 Forbidden | You can't request license without any products. | | `227` | 403 Forbidden | Previous subscription for that lpr camera doesn't have an activation date set. | | `228` | 403 Forbidden | Can't extend lpr storage subscription that is ON HOLD. | | `229` | 403 Forbidden | You don't have lpr storage subscription for this camera. | | `230` | 403 Forbidden | You can't set end time if is_unlimited is true. | | `232` | 400 Bad Request | Lpr Channel not implemented. | | `233` | 404 Not Found | No product online. | | `234` | 404 Not Found | No product eligible. | | `235` | 400 Bad Request | LPR camera is not a HIK device. | | `236` | 400 Bad Request | LPR camera is not a AXIS device. | | `237` | 403 Forbidden | Product weblink has reached its maximum number of uses. | #### Lots and zones | Code | HTTP status | Message | | --- | --- | --- | | `300` | 403 Forbidden | Communication with lot failed. Please try again later. | | `301` | 404 Not Found | Lot not found | | `301` | 409 Conflict | LPR Camera not assigned to zone | | `302` | 403 Forbidden | Lot maximum products reached. Please add another gateway to lot. | | `303` | 404 Not Found | Zone not found. | | `304` | 400 Bad Request | No Parking detection active for lot. | | `305` | 400 Bad Request | Duplicate parking detection. | | `306` | 403 Forbidden | End time is before start time. | | `307` | 400 Bad Request | Zone does not belong to lot. | | `308` | 400 Bad Request | At zone must have a parking detection camera for live camera view type. | | `309` | 400 Bad Request | Zones do not have a vector map. | | `310` | 404 Not Found | Zone does not have a vector map. | #### Sharing and operation keys | Code | HTTP status | Message | | --- | --- | --- | | `400` | 500 Internal Server Error | Something went wrong and we couldn't process your request. | | `401` | 404 Not Found | Operation key not found. | | `402` | 403 Forbidden | End time is required when 'is_unlimited' is false. | | `403` | 403 Forbidden | End time must be a future date. | | `404` | 403 Forbidden | You can't set <time> time after 2038. | | `405` | 403 Forbidden | End time is before start time. | | `406` | 404 Not Found | Key file not found. | | `407` | 403 Forbidden | Max file rows is <allowedRows> while your file has <uploadedFileRows>. | | `408` | 403 Forbidden | <first> time is before <second> time. | | `409` | 403 Forbidden | Start time must be a future date. | | `410` | 403 Forbidden | Recurring start time must be a future date. | #### Updates | Code | HTTP status | Message | | --- | --- | --- | | `500` | 500 Internal Server Error | Something went wrong with uploading the file. | | `501` | 404 Not Found | Update not found. | #### Account users | Code | HTTP status | Message | | --- | --- | --- | | `600` | 403 Forbidden | Account user invitation already exists. | | `601` | 400 Bad Request | Account user already exists. | | `602` | 403 Forbidden | Phone number has been taken. | #### Delivery to devices | Code | HTTP status | Message | | --- | --- | --- | | `701` | 403 Forbidden | Emit unsuccessful. | | `702` | 403 Forbidden | No available gateway was able to execute the command. | | `703` | 403 Forbidden | All gateways are offline. | | `704` | 403 Forbidden | No rssi signal. | #### LPR cameras | Code | HTTP status | Message | | --- | --- | --- | | `800` | 422 Unprocessable Entity | Socket data invalid. | | `801` | 422 Unprocessable Entity | Socket data type handler not found. | | `802` | 503 Service Unavailable | LPR camera is offline. | | `803` | 403 Forbidden | LPR camera socket cache not found. | #### Other | Code | HTTP status | Message | | --- | --- | --- | | `901` | 404 Not Found | Entry image not found. | | `1000` | 400 Bad Request | Cannot move to different lot. | #### Terminals | Code | HTTP status | Message | | --- | --- | --- | | `1100` | 404 Not Found | Terminal challenge not found | | `1101` | 401 Unauthorized | Bad challenge signature | | `1102` | 400 Bad Request | Challenge expired | | `1103` | 503 Service Unavailable | no viable connection to the terminal | | `1104` | 428 Precondition Required | Terminal not assigned to zone | #### Receivers | Code | HTTP status | Message | | --- | --- | --- | | `1300` | 403 Forbidden | Receiver not allowed on this zone. | #### Tariffs | Code | HTTP status | Message | | --- | --- | --- | | `1400` | 400 Bad Request | Tariff cannot be deleted because some groups contain members. | #### RGB displays | Code | HTTP status | Message | | --- | --- | --- | | `1500` | 403 Forbidden | Rgb display owner already set. | | `1501` | 400 Bad Request | Serial number is not unique. | | `1502` | 400 Bad Request | valueTo is required when operator is between. | | `1503` | 404 Not Found | RGB display does not exist. | #### Sessions | Code | HTTP status | Message | | --- | --- | --- | | `1501` | 404 Not Found | Associated session not found | | `1501` | 409 Conflict | Maximum members per tariff exceeded | | `1502` | 403 Forbidden | Member not allowed | | `1503` | 422 Unprocessable Entity | Unkown direction | | `1505` | 400 Bad Request | No plate reading associated with session | | `1506` | 404 Not Found | Session not found | ### Next step See [Rate Limits](/api/rate-limits) for the `429` case, or the [API Reference](/api) for the errors each endpoint can return. --- ## OpenAPI definition The Endpoints reference in these docs is generated from a **curated** copy of the Parklio API's OpenAPI definition. Download it and use it directly: **[Download the curated OpenAPI definition](pathname:///openapi/parklio.json)** (`https://docs.parklio.com/openapi/parklio.json`) It's curated, which means: - it lists only the operations meant for integrators, grouped the same way as this reference; internal and device-facing operations are left out; - operation ids are stable and readable, for example `products-change-state`; - documentation errors in the raw definition are corrected, for example the error examples and the API-token create response. ### Using the definition Import the JSON into Postman, Insomnia or any other OpenAPI-compatible tool, or generate a typed client from it, for example: ```bash title="Generate a TypeScript client" npx @openapitools/openapi-generator-cli generate \ -i https://docs.parklio.com/openapi/parklio.json \ -g typescript-fetch \ -o ./parklio-client ``` Most endpoints require a bearer token. See [Authentication](./authentication.md) for how to get one. ### The raw definition The API also serves its own, uncurated definition. It includes operations that aren't for integrators, and it changes with every API deploy. Prefer the curated file above. | | Link | | --- | --- | | Interactive Swagger UI | [api.parklio.com/api](https://api.parklio.com/api) | | Raw OpenAPI JSON | [api.parklio.com/api-json](https://api.parklio.com/api-json) | --- ## Rate limits The API allows **100 requests per second per IP address**. The limit is counted on each API instance separately, so treat 100/s as the most you can rely on, not a guarantee. Every response carries three headers describing your current window: ```text title="Rate-limit headers" X-RateLimit-Limit: 100 X-RateLimit-Remaining: 99 X-RateLimit-Reset: 1 ``` | Header | Meaning | | --- | --- | | `X-RateLimit-Limit` | Maximum requests allowed in the current window. | | `X-RateLimit-Remaining` | Requests left before you're throttled with a `429`. | | `X-RateLimit-Reset` | Seconds until the window resets and `Remaining` refills. | When you go over the limit, the API returns **429** with error code `5` and a `Retry-After` header: the number of seconds to wait. :::note Browsers can't read these headers The API doesn't expose the rate-limit headers through CORS, so code running in a browser can't see them. API tokens belong on your server anyway; call the API from there. ::: ### Handling `429`s - Check `X-RateLimit-Remaining` before firing a burst of requests, not just after you've already been throttled. - On a `429`, wait at least `Retry-After` seconds, with jitter, before retrying. Don't retry immediately in a loop. - Prefer subscribing over [WebSockets](/realtime/connecting) to anything that changes in realtime (occupancy, gateway state) instead of polling the REST API for it. ### Next step See [Errors](/api/errors) for how failed requests, including a `429`, are shaped, or the [API Reference](/api) for individual endpoints. --- ## Access and keys Parklio has several ways to let someone other than you operate a product: a guest, an employee, your own app, a vehicle. They differ in who receives them, whether they work offline, whether Parklio emails anyone, and how fast you can revoke them. This page compares them so you can pick one. ### Where access comes from When a product is paired with the Connect app, it gets a **master key**, a secret shared by the product and Parklio. Every credential below is derived from it, and the product checks them itself over Bluetooth, which is why some of them work without a network. You never handle the master key. Pairing a product again, or removing it from its lot, invalidates every credential derived from the old one. ### Comparison | Mechanism | Who gets it | What they need | Reaches the product via | Works offline | Revocation | Parklio emails | Plan feature | | --- | --- | --- | --- | --- | --- | --- | --- | | **Operation token** | Your backend, which passes it to your app | Your app, built with the [Parklio SDK](/sdk) | The phone, over Bluetooth | ✅ | **Not immediate**: see below | No | — | | **Operation key** | A Parklio Connect user, by email | The Connect app | The phone, over Bluetooth | ✅ | Block it through its key log | Only an invitation, when the email has no account yet | `KEY_SHARE` | | **Key invitation** | An email address with no Parklio account | To sign up for the Connect app | Becomes an operation key on sign-up | — | Block it before it's accepted | Yes, the invitation | `KEY_SHARE` | | **Weblink** | An email recipient | A browser | The cloud and a Gateway | ❌ | **Immediate** | Yes, always | — | | **QR code** | Whoever holds the image | | | | **Can't be revoked** | No | `QR_CODE_SHARE` | | **License plate** | A vehicle | Nothing | An LPR camera, the cloud and a Gateway | ❌ | Delete or expire the whitelist entry, or unbind the plate | No | — | Two paths are built for integrations and have full guides: [operation tokens](/guides/mobile-app-operation-tokens) for your own app, and [license plates](/guides/plate-based-access) for vehicles. The others are covered by the [API reference](/api). ### Operation tokens For your own mobile app. Your backend creates a token for one product and a time window with [`POST /v2/operation-tokens`](/api/endpoints/operation-tokens-create), and hands it to your app, which uses the Parklio SDK to operate the product over Bluetooth. - Give either an `end_time` or `"is_unlimited": true`. Unlimited tokens are valid until 2038-01-01. - The response contains three signed commands: `token_open`, `token_close` and `token_status`. Your app passes them to the product through the SDK. - **Revocation isn't immediate.** Blocking a token ([`PATCH /v2/operation-tokens/{id}`](/api/endpoints/operation-tokens-update) with `{"status": "blocked"}`) marks it blocked on the server, but nothing is pushed to the device, so a copy your app already holds can keep working until `end_time`. **Create tokens just in time, with short windows.** - The product's clock must be set (`rtcc_invalid` is `0`), or time-limited credentials fail. See [SDK](/sdk). ### Operation keys and key invitations For people who use the Parklio Connect app. Create one with [`POST /v2/operation-keys`](/api/endpoints/operation-keys-create-many), naming the receiver by email: - If the email belongs to a Connect user, they get an **operation key** in their app. - If not, Parklio emails them a **key invitation**. When they sign up for the Connect app with that email, it becomes an operation key. To revoke an operation key, block its key log ([`PATCH /v2/key-logs/{id}`](/api/endpoints/key-logs-update)); Parklio emails the receiver. Block a pending invitation with [`PATCH /v2/key-invitations/{id}`](/api/endpoints/key-invitations-update). ### Weblinks A link Parklio emails to the receiver; opening it in a browser lets them operate the product through the cloud and a Gateway. Create one with [`POST /v2/weblinks`](/api/endpoints/weblinks-bulk-create): `start_time` and `end_time` are both required, and `max_uses` defaults to `1` (`null` for unlimited). Revocation is immediate, because the server checks every use. Parklio always sends the email; you can't send the link through your own channel. ### QR codes An image that grants opening for a time window. Create one with [`POST /v2/qr-codes`](/api/endpoints/qr-codes-create). **A QR code can't be revoked:** deleting it only removes the record, so give it the shortest window that works. ### License plates Vehicles whose plate is whitelisted on a lot can be let in by an LPR camera: 1. Whitelist the plate on the lot with [`POST /v2/lot-whitelists`](/api/endpoints/lot-whitelist-create), for a time window or unlimited. 2. Bind it to the products it should open with [`POST /v2/products/{id}/bind-licenses`](/api/endpoints/products-bind-license-for-product), using the `license_id` from step 1. 3. When a camera in the zone reads the plate on entry, Parklio opens a bound product that's online and closed, through a Gateway. Each read is recorded as a [plate reading](/api/endpoints/plate-readings-find-all), with an `access_decision` (`triggered` or `denied`) and the reason. See [Plate-based access](/guides/plate-based-access). ### Next - [Operation tokens for your app](/guides/mobile-app-operation-tokens) - [Plate-based access](/guides/plate-based-access) - [SDK](/sdk): the operation-token flow end to end. - [Plans and permissions](/concepts/plans-and-permissions): the features each mechanism needs. --- ## Asynchronous operations Commands to a product travel from the API through a Gateway and over Bluetooth, which takes seconds, not milliseconds. So the API answers straight away with an `event_id`, and tells you the result later, over the [realtime connection](/realtime/connecting). This page covers the whole lifecycle. It applies to [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state) and [`POST /v2/products/{id}/settings`](/api/endpoints/products-change-settings). ### The lifecycle | Step | Who | What happens | | --- | --- | --- | | 1 | You | Connect to realtime, and wait until you're connected. | | 2 | You → API | Send the command, for example `{"operation": "open"}`. | | 3 | API | Checks the command can run. If not, answers with an HTTP error (below) and stops. | | 4 | API → you | Answers `200` with `{"success": true, "data": {"event_id": "a1b2c3d4"}}`. The device hasn't moved yet. | | 5 | API → Gateway | Sends the command to the first candidate Gateway and waits up to **30 seconds** for its answer. | | 6 | Gateway → product | Relays the command over Bluetooth; the product acts and reports back. | | 7a | API → you | **Success:** a `lot_change` event with the same `event_id` and the product's new state. | | 7b | API → you | **Failure:** a `gateway_error` event with the same `event_id`. | `event_id` is 8 hexadecimal characters. ### How the Gateway is chosen The candidates are the Gateways that currently see the product over Bluetooth. The API tries them **one at a time, strongest signal first**, waiting up to 30 seconds for each: - If a Gateway answers that the operation worked, you get `lot_change`. - If it times out, or fails for a reason another Gateway might not hit (for example a Bluetooth error), the API moves on to the next Gateway. - If the product itself reports a problem that no other Gateway can fix (`type` 0, 1, 5, 6, 7 or 8 below: for example jammed, or a car on top), it stops there and you get `gateway_error`. - If every Gateway fails, you get `gateway_error` with the last error. So with several Gateways in range, a result can take a multiple of 30 seconds. ### Errors before anything is sent These come back as HTTP errors in step 3, and nothing is sent to the device: | HTTP | Code | Meaning | What to do | | --- | --- | --- | --- | | 403 | `30` | The product's subscription doesn't include remote control. | See [Plans and permissions](/concepts/plans-and-permissions). | | 403 | `4` | The product isn't visible to your token. | Check the product is on a lot your token can see; see [Data model](/concepts/data-model#what-an-api-token-sees). | | 404 | `6` | No product with that id. | Check the id. | | 403 | `214` | An earlier operation on this product hasn't finished. | Wait for its result; don't retry in a loop. | | 403 | `704` | No Gateway currently sees the product. | Check the Gateway is online, on the same lot and in range. | | 403 | `206` | The product isn't paired. | Pair it with the Connect app. | | 422 | `23` | Invalid body, for example an unknown `operation`. | Fix the request. | ### `gateway_error` types ```json title="gateway_error" { "type": 5, "event_id": "a1b2c3d4", "data": { "id": 123, "name": "Spot 12" }, "message": "Device jammed" } ``` When no Gateway answered in time, `type` is the **string** `"Timeout"` and there's no `message`. Otherwise it's a number. These are **not** the HTTP error codes. | `type` | `message` | Tries the next Gateway? | | --- | --- | --- | | `0` | Auth error | No | | `1` | Invalid guest key | No | | `2` | Device disconnected | Yes | | `3` | Bluetooth error | Yes | | `4` | Device not found | Yes | | `5` | Device jammed | No | | `6` | Car on top | No | | `7` | Sensor dirty | No | | `8` | Device not initialized | No | | `9` | Rtcc not set | Yes | | `10` | Device busy | Yes | | `11` | Unknown error | Yes | | `"Timeout"` | (none) | Yes | ### `wait_for_completion` `POST /v2/products/{id}/state` accepts an optional `wait_for_completion` (default `true`). It's passed on to the Gateway, which by default waits until the product reports it has reached the new state. Set it to `false` for a **brain without input sensors**, which can't report an open or closed state. It doesn't change the HTTP response: you always get the `event_id` straight away. ### Writing a client 1. **Connect to realtime before sending commands**, with the same API token, so you can't miss the result. 2. **Keep the pending `event_id`s** in a map, and resolve each on the `lot_change` or `gateway_error` that carries it. The result can arrive before your HTTP call returns: buffer results you haven't matched yet. 3. **Ignore `event_id`s you didn't send.** Your connection also receives operations started by others (the Connect app, other users) and automatic ones, such as Parklio setting a device's clock. 4. **Give up client-side** after a timeout of your choice, and re-read the product with [`GET /v2/products/{id}`](/api/endpoints/products-find-one) to see where it ended up. 5. **One operation per product at a time.** A second command while the first is pending gets `214`. There's no REST endpoint to look up a result by `event_id`, and no replay: if you were disconnected when the event was sent, re-read the product's state over REST. A working example is in the [Quickstart](/get-started/quickstart). --- ## Data model What an account contains, how the pieces relate, and what each kind of user (and your API token) can see. ![Parklio account structure](/img/diagrams/data_overview.svg) ### Accounts An **account** is your organization in Parklio. It owns everything else: account users, API tokens, lots, zones, products and Gateways. For an integration, Parklio provisions a dedicated API account; see [What you need](/get-started/what-you-need). ### Account users and roles **Account users** are the people who log in to an account, each identified by an email address and assigned to one or more lots. Each has one of three roles: | Role | Can do | | --- | --- | | **Super Admin** | Everything in the account: create and delete lots, see every lot, manage all account users and invitations, and create **API tokens**. New lots are assigned to every Super Admin automatically. | | **Admin** | On the lots assigned to them: update lot details, manage members, groups and tariffs, and see users with the same or a lower role. | | **Operator** | On the lots assigned to them: work with products and access, and update a lot's or zone's spot counts. Can't create or delete zones, or manage lot share views. | On the lots they're assigned to, every role can add and manage products, LPR cameras, operation tokens and lot whitelists, and share access. Only a Super Admin can create API tokens. See [Authentication](/api/authentication). #### What an API token sees Requests made with an API token act as the account's **first Super Admin**: - **Lots and Gateways:** every one in the account. - **Products, zones, LPR cameras and parking places:** those in the lots that Super Admin is assigned to. New lots are assigned to Super Admins automatically, but a Super Admin can be removed from a lot ([`DELETE /v2/lots/{id}/managers/{managerId}`](/api/endpoints/lots-remove-lot-manager)), and its products then disappear from your token's view. Keep your first Super Admin on every lot you integrate with. - **A product with no zone** isn't visible to any token. ### Lots and zones A **lot** is a parking location. Its fields include `name`, `address`, `latitude`, `longitude`, `timezone`, and the occupancy counters `spot_count` and `occupied_count`. A lot is split into **zones**, for example levels or pricing areas. Each zone belongs to one lot and has its own `spot_count` and `occupied_count`. Products and LPR cameras sit in a zone; Gateways belong to a lot. ### Products **Products** are the hardware you control: barrier, gate, chain, bollard and brain. A product has a `lot_id` and a `zone_id`. They talk only over Bluetooth, so the cloud reaches them through a Gateway, and phones reach them directly. Product types, states and the operations each supports are in [Products and operations](/concepts/products-and-operations). LPR cameras are a separate resource, also placed in a zone; see the [LPR cameras reference](/api/endpoints/lpr-cameras-find-all). ### Gateways A **Gateway** belongs to a lot. It connects to the Parklio cloud over Ethernet or a mobile network, and relays commands and status to the products it can reach over Bluetooth. A Gateway's `status` is `online` while it's connected to the cloud and `offline` otherwise. Changes arrive as the realtime [`gateway_status`](/realtime/events#gateway_status) event. ### What's paired where | Thing | Added to the account by | Where it lives | | --- | --- | --- | | Product | The Connect app (pairing) | A zone in a lot | | Gateway | The Connect app | A lot | | LPR camera | The API ([`POST /v2/lpr-cameras`](/api/endpoints/lpr-cameras-assign)) | A zone | | Parking detection (cameras, mapping) | Parklio technicians | A lot and its zones | The API has no endpoints for pairing products or Gateways. ### Next - [Products and operations](/concepts/products-and-operations) - [Access and keys](/concepts/access-and-keys) --- ## Glossary These docs use the API's names for things. Other Parklio material (the website, the Knowledge Base, the apps) sometimes uses different words for the same thing; the **also called** column lists them. | Term | Also called | Meaning | | --- | --- | --- | | **Account** | | Your organization in Parklio. Owns lots, products, Gateways, users and API tokens. See [Data model](/concepts/data-model). | | **Account user** | user | A person who logs in to an account, with a role: Super Admin, Admin or Operator. | | **API token** | API key | A long-lived bearer token for server-to-server calls. Created by a Super Admin. See [Authentication](/api/authentication). | | **Connect app** | "Parklio" (its name in the app stores) | Parklio's mobile app. Pairs products and Gateways with an account, and operates products over Bluetooth. | | **`event_id`** | | The id of an asynchronous operation, returned when you send a command and repeated on the realtime event that reports its result. See [Asynchronous operations](/concepts/asynchronous-operations). | | **Gateway** | | An on-site box that relays commands and status between the Parklio cloud and nearby products over Bluetooth. Needed for remote control. | | **Key invitation** | pending access, invitation | Access sent to an email address that has no Parklio account yet. Becomes an operation key when the person signs up. See [Access and keys](/concepts/access-and-keys). | | **License-plate binding** | bound licenses | A link between a whitelisted license plate and a product, so a plate read can open it. | | **Lot** | parking lot, site | A parking location. Holds zones, products and Gateways. | | **Lot whitelist** | allowlist, ANPR access | The license plates allowed on a lot, each with a time window or unlimited. | | **LPR camera** | ANPR camera | A license-plate recognition camera. | | **Master key** | | The secret a product shares with Parklio, set when the product is paired with the Connect app. Every access credential for the product depends on it. You never handle it directly. | | **Operation key** | digital key, virtual key, shared key, guest key | Access to a product shared with a Parklio Connect user, used from the Connect app. | | **Operation token** | | A time-limited, signed set of commands (open, close, status) for one product, minted by your backend for your own app built with the Parklio SDK. | | **Parking detection** | Detect, occupancy detection | Camera-based tracking of which parking places are free or occupied. | | **PMS** | Parking Management System, "CMS" | Parklio's web app for managing lots, products and access. | | **Product** | device | A piece of Parklio hardware you control: a barrier, gate, chain, bollard or brain. See [Products and operations](/concepts/products-and-operations). | | **QR code** | | An open-only access credential in an image. See [Access and keys](/concepts/access-and-keys). | | **Session** | | One vehicle's stay in a zone, from entry to exit, used for parking payments. | | **Tariff** | | Pricing for a zone: price per interval, free time, maximums. | | **Terminal** | Payment Machine, POF, pay-on-foot | An entry, exit or payment machine in a lot. | | **Weblink** | | A link, emailed by Parklio, that lets someone operate a product from a browser for a limited time. See [Access and keys](/concepts/access-and-keys). | | **Zone** | | A part of a lot, for example a level or a pricing area. | --- ## Plans and permissions Besides the user's role, a valid request can be stopped by a **feature** your subscriptions don't include. ### Features Some endpoints need a feature that comes with your subscriptions. Parklio sets your account's features from what you subscribe to; you don't manage them through the API. When a feature is missing, the endpoint returns **403** with error code **`30`** ("Account missing proper authorization"). | Feature | Needed for | | --- | --- | | `REMOTE_CONTROL` | Operating products: [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state), [`POST /v2/products/{id}/settings`](/api/endpoints/products-change-settings) | | `KEY_SHARE` | Creating operation keys: [`POST /v2/operation-keys`](/api/endpoints/operation-keys-create-many) and its single and file-upload variants | | `QR_CODE_SHARE` | Creating QR codes: [`POST /v2/qr-codes`](/api/endpoints/qr-codes-create) | | `LPR_CONTROL` | Opening and rebooting LPR cameras: `POST /v2/lpr-cameras/{id}/state` and `/reboot` | | `LPR_RECOGNITION` | License-plate alerts (`/v2/license-plate-alerts`) | | `PARKING_DETECTION` | Creating and updating parking places, parking-place history, violation zones and their events, and parking-detection camera frames | | `PARKING_DETECTION` or `LPR_RECOGNITION` | Proactive alerts (`/v2/proactive-alerts`) | Features follow the licenses on your account: - licenses for **products** (barriers, gates, chains, bollards, brains) bring the remote-control and sharing features; - licenses for **LPR cameras** bring `LPR_CONTROL` and `LPR_RECOGNITION`; - licenses for **parking places** bring `PARKING_DETECTION`. If you get code `30`, check the product's subscription at [subscriptions.parklio.com](https://subscriptions.parklio.com/?product=api), or contact [info@parklio.com](mailto:info@parklio.com). ### Roles Separately, each account user's role (Super Admin, Admin or Operator) decides what they can do, and on which lots. A role problem returns **403** with code `4` ("No permission"). API tokens act as the account's first Super Admin. See [Data model](/concepts/data-model#account-users-and-roles). ### Next - [Access and keys](/concepts/access-and-keys) - [Errors](/api/errors) --- ## Products and operations The product types, how to read a product's state, and which operations each type supports. ### Product types | `product_type_id` | Type | Type block in the response | Device id | | --- | --- | --- | --- | | `2` | Barrier | `barrier` | `B` + 6 characters | | `3` | Gate | `gate` | `G` + 6 characters | | `4` | Brain | `brain` | `R` + 6 characters | | `5` | Chain | `chain` | `C` + 6 characters | | `6` | Bollard | `bollard` | `P` + 6 characters | The 6 characters are uppercase letters and digits, for example `B1A2B3C`. [`GET /v2/products`](/api/endpoints/products-find-all) lists your products, except products that aren't paired (no active pairing with the Connect app). ### Reading a product Every product has these fields, among others: | Field | Values | | --- | --- | | `status` | `online`: a connected Gateway can see the product, so commands can reach it. `offline`: none can. | | `power_type` | `battery` or `grid` | | `battery_status` | Battery level in percent; `null` while offline | | `rssi` | Bluetooth signal strength to the best Gateway, in dBm; `null` while offline | | `lot_id`, `zone_id` | Where the product is | | `product_error` | The device's error flags (below), or `null` | Plus exactly **one type block**, named after the type: **`barrier`** | Field | Values | | --- | --- | | `state` | `opened` or `closed` | | `is_autoclose` | `1` if auto-close is on, else `0` | | `autoclose_time` | The auto-close delay, in seconds | | `is_car_on_top` | `1` while a car is over the barrier, else `0` | **`gate`, `chain`, `bollard`, `brain`** | Field | Values | | --- | --- | | `state` | `opened`, `closed`, `error`, `unknown`, `reserved` or `disabled` | | `permanent_position` | `disabled` (normal operation), `stay_open` or `stay_close` | ### What open and close mean | Product | `open` | `close` | | --- | --- | --- | | **Barrier** | The arm **lowers**: the spot can be used. | The arm **rises**: the spot is blocked. | | **Gate, chain, bollard, brain** | The passage opens: vehicles can pass. | The passage closes. | A barrier protects a single parking space, so "open" means the space is open to park in. ### Operations Send operations with [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state): ```json title="Request body" { "operation": "open" } ``` | Operation | Barrier | Gate, chain, bollard, brain | | --- | --- | --- | | `open` | ✅ arm lowers | ✅ passage opens | | `close` | ✅ arm rises | ✅ passage closes | | `force_close` | ✅ | — | | `stay_open` | — | ✅ holds the passage open | | `stay_close` | — | ✅ holds the passage closed | | `clear_stay_open` | — | ✅ ends `stay_open` | | `clear_stay_close` | — | ✅ ends `stay_close` | The API doesn't check the combination for you: **check the matrix in your code** before sending an operation. Combinations marked — aren't supported. The call returns an `event_id` straight away; the result arrives later over the realtime connection. See [Asynchronous operations](/concepts/asynchronous-operations). ### Settings (barriers) [`POST /v2/products/{id}/settings`](/api/endpoints/products-change-settings) changes a barrier's auto-close. Send **one** setting per call: ```json title="Turn auto-close on or off" { "is_autoclose": true } ``` ```json title="Set the auto-close delay (5–60 seconds)" { "autoclose_time": 30 } ``` - Send `is_autoclose` as a JSON boolean, `true` or `false`, not `1`/`0`. - If you send both, only `is_autoclose` is applied. - An empty body fails. Like a state change, it returns an `event_id`, and the result arrives as a realtime event. ### Error flags `product_error` reports problems the device itself detects. Each flag is `0` or `1`: | Flag | Meaning | | --- | --- | | `jammed` | The arm or mechanism is blocked. | | `sensor_dirty` | The car-detection sensor is dirty. | | `pin_broken` | Reported by the device; see the product's manual. | | `rtcc_invalid` | The device's clock isn't set. Time-limited credentials (operation tokens, keys) won't work until it is. | | `battery_empty` | The battery is empty. | | `master_key_not_set` | Reported by the device; see the product's manual. | `error_code` holds the same six flags as a string of `0`s and `1`s, in the order above; for example `"010010"` means a dirty sensor and an empty battery. ### Next - [Asynchronous operations](/concepts/asynchronous-operations) - [Quickstart](/get-started/quickstart) --- ## Choose your path Find the row that matches what you want to build. The path decides what hardware you need and which parts of these docs matter to you. | You want to… | Path | Needs a Gateway | Works offline | Start here | | --- | --- | --- | --- | --- | | Open and close products from your backend | REST API + realtime events | Yes | No | [Quickstart](/get-started/quickstart), then [Remote control](/guides/remote-control) | | Let users open products from **your own mobile app** | Parklio SDK + operation tokens from your backend | No | Yes | [Operation tokens for your app](/guides/mobile-app-operation-tokens), [SDK](/sdk) | | Let **vehicles in by license plate** | LPR camera + lot whitelist + plate binding | Yes | No | [Plate-based access](/guides/plate-based-access) | | **Show occupancy** of lots, zones or parking places | REST API + realtime events | No Parklio Gateway; detection is installed by Parklio | No | [Live occupancy](/guides/live-occupancy) | | Open and close products **without code** | Zapier | Yes | No | [Zapier](/integrations/zapier) | | Send a guest a **link** that opens a product | Weblinks (Parklio emails the link) | Yes | No | [Access and keys](/concepts/access-and-keys#weblinks), then the [API reference](/api/endpoints/weblinks-bulk-create) | Combining paths is common. For example, a parking app can create operation tokens for its users (SDK path) and also open products from its backend (REST path). ### What each path needs - **Every path:** an API account and API token. See [What you need](/get-started/what-you-need). - **REST, LPR, Zapier, weblinks:** an online Gateway on the same lot, in Bluetooth range of the products, and an API subscription that includes remote control. - **SDK:** access to the private SDK ([request it](mailto:info@parklio.com)), and a backend that creates operation tokens. Phones talk to products directly, so no Gateway is needed. - **Occupancy:** parking detection, set up on your lot by Parklio. ### Next [What you need](/get-started/what-you-need): the account, subscriptions and hardware to request before you write code. --- ## How Parklio works A five-minute mental model: what's in an account, how a command reaches a device, and why results arrive asynchronously. ### The pieces ```text title="Account structure" Account ├── Account users (Super Admin, Admin, Operator) ├── API tokens └── Lots ├── Gateways └── Zones ├── Products (barrier, gate, chain, bollard, brain) └── LPR cameras ``` - **Account:** your organization. Everything below belongs to it. - **Lot:** a parking location. A lot holds zones and Gateways. - **Zone:** a part of a lot, for example a level or a pricing area. Products and LPR cameras sit in a zone. - **Product:** the hardware you control. Products speak only **Bluetooth (BLE)**. - **Gateway:** an on-site box that connects to the Parklio cloud (over Ethernet or a mobile network) and to nearby products over Bluetooth. It's how the cloud reaches a product. More detail in [Data model](/concepts/data-model). Products and Gateways are added to an account with the **Parklio Connect app**, which pairs each device with your account. The API has no pairing endpoints. ### Two ways to reach a product | | Cloud path | Bluetooth path | | --- | --- | --- | | How | Your backend → Parklio API → Gateway → product over BLE | A phone with your app (built with the Parklio SDK) → product over BLE | | Credentials | Your API token | An **operation token** your backend creates, which the app hands to the product | | Needs a Gateway | Yes, online and in range | No | | Needs internet at the product | Yes (at the Gateway) | No, once the app has the token | | Result | Asynchronous, as a realtime event | Directly in the app | | Start with | [Quickstart](/get-started/quickstart) | [SDK](/sdk) | Parklio's own tools use the same paths: Zapier and the web app (PMS) go through the cloud, and the Connect app talks to products over Bluetooth. ### Why the cloud path is asynchronous When you send a command, such as [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state) with `{"operation": "open"}`: 1. The API checks it can run the command, then answers **straight away** with an `event_id`. The device hasn't moved yet. 2. The API sends the command through the Gateways that recently saw the product, **one at a time, strongest Bluetooth signal first, waiting up to 30 seconds for each**. 3. The Gateway relays it to the product over Bluetooth, and the product reports back. 4. The API pushes the result to you over the realtime connection: a `lot_change` event with your `event_id` when it worked, or a `gateway_error` event with your `event_id` when it didn't. So a cloud integration needs **both** the REST API (to send commands) and the [realtime connection](/realtime/connecting) (to learn the result). Connect to realtime first. Details in [Asynchronous operations](/concepts/asynchronous-operations). ### Status flows the other way too Products advertise their state over Bluetooth all the time: open or closed, battery, errors such as a jammed arm or a dirty sensor, and on a barrier whether a car is on top. Gateways pick these up and send them to the API, which updates the product and pushes a `lot_change` event (with no `event_id`, since nobody requested it). A product is `online` while a connected Gateway can see it, and `offline` otherwise. ### Occupancy Parklio can track which parking places are free or occupied, using cameras and on-site processing. Each lot is different, so detection is set up by Parklio's technicians: they plan camera coverage from lot photos and blueprints, then map the parking places and tie them to your account. You read the results; you don't configure detection. ![Parking detection setup](/img/diagrams/parking_detection.svg) From there: - Lots and zones carry `spot_count` and `occupied_count`; read them with [`GET /v2/lots/{id}`](/api/endpoints/lots-find-one). - Each parking place has an `occupancy_status` of `free`, `occupied` or `unknown`; list them with [`GET /v2/parking-places`](/api/endpoints/parking-places-find-all). - Changes arrive live as realtime events; see [Events](/realtime/events). ### Next - [Choose your path](/get-started/choose-your-path): which integration fits your product. - [What you need](/get-started/what-you-need): the account, subscriptions and hardware. --- ## Quickstart Go from an API token to a confirmed hardware operation: list your products, send `open`, and see the result arrive over the realtime connection. ### Prerequisites - A product and a Gateway on the same lot, paired with your account, and an API subscription for the product. See [What you need](/get-started/what-you-need). - Your account's **Super Admin** email and password. - For the code: Node.js 24+ with `socket.io-client` (the Node examples are TypeScript, which Node 24 runs directly: `node open.ts`), or Python 3 with `httpx` and `python-socketio`. ### 1. Create an API token (once) Log in as the Super Admin with [`POST /v2/auth/login`](/api/endpoints/auth-login), then create a token with [`POST /v2/api-tokens`](/api/endpoints/api-token-create). Do this by hand, once: logging in signs that user out of the Connect app. ```bash title="Create an API token" USER_TOKEN=$(curl -s -X POST https://api.parklio.com/v2/auth/login \ -H "Content-Type: application/json" \ -d '{"email": "admin@example.com", "password": ""}' | jq -r .data.token) curl -s -X POST https://api.parklio.com/v2/api-tokens \ -H "Authorization: Bearer $USER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "quickstart"}' | jq -r .data.token ``` These commands use [`jq`](https://jqlang.org) to pick fields out of the JSON. The last one prints your API token. It's shown only once: store it, and export it for the next steps. ```bash title="Environment" export PARKLIO_API_TOKEN="" ``` More in [Authentication](/api/authentication). ### 2. List your products **curl** ```bash title="List products" curl -s https://api.parklio.com/v2/products \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" ``` **Node.js** ```ts title="list-products.ts" const res = await fetch('https://api.parklio.com/v2/products', { headers: { Authorization: `Bearer ${process.env.PARKLIO_API_TOKEN}` }, }); const { data } = await res.json(); for (const p of data) console.log(p.id, p.name, p.product_type_id, p.status); ``` **Python** ```python title="list_products.py" import os import httpx res = httpx.get( "https://api.parklio.com/v2/products", headers={"Authorization": f"Bearer {os.environ['PARKLIO_API_TOKEN']}"}, ) for p in res.json()["data"]: print(p["id"], p["name"], p["product_type_id"], p["status"]) ``` ```json title="Response (shortened)" { "success": true, "data": [ { "id": 123, "device_id": "B1A2B3C", "name": "Spot 12", "lot_id": 4, "zone_id": 9, "product_type_id": 2, "status": "online", "power_type": "battery", "battery_status": 87, "rssi": -71, "barrier": { "state": "closed", "is_autoclose": 1, "is_car_on_top": 0, "autoclose_time": 30 }, "product_error": null } ], "meta": { "total": 1, "per_page": 10, "current_page": 1, "last_page": 1 } } ``` Pick a product and check two things: - **`status` is `online`.** A Gateway can see it, so a command can reach it. - **The type block.** Each product has exactly one: `barrier`, `gate`, `chain`, `bollard` or `brain`, with its current `state`. See [Products and operations](/concepts/products-and-operations). ```bash title="Environment" export PRODUCT_ID=123 ``` ### 3. Connect, send `open`, and wait for the result Results arrive only over the realtime connection, so **connect first**, then send the command, then wait for the event carrying your `event_id`. On a barrier, `open` **lowers** the arm so the spot can be used. On a gate, chain, bollard or brain, `open` opens the passage. **curl** curl can't hold the realtime connection, so this sends the command only. Use the Node or Python version to see the result. ```bash title="Send open" curl -s -X POST "https://api.parklio.com/v2/products/$PRODUCT_ID/state" \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"operation": "open"}' ``` ```json title="Response" { "success": true, "data": { "event_id": "a1b2c3d4" } } ``` **Node.js** ```ts title="open.ts" import { io } from 'socket.io-client'; const token = process.env.PARKLIO_API_TOKEN; const productId = process.env.PRODUCT_ID; interface OperationEvent { event_id?: string; type?: number | 'Timeout'; message?: string; data: Record; } type Result = { ok: boolean; event: OperationEvent }; // 1. Connect first. `connect` fires once the server has accepted the token. const socket = io('https://api.parklio.com/v2/realtime', { path: '/socketio', query: { token }, }); await new Promise((resolve, reject) => { socket.once('connect', () => resolve(undefined)); socket.once('connect_error', reject); }); // 2. Collect results by event_id. One can arrive before the HTTP response does. const results = new Map(); const waiters = new Map void>(); const settle = (ok: boolean) => (raw: string) => { const event: OperationEvent = JSON.parse(raw); // payloads are JSON strings if (!event.event_id) return; // a status update nobody requested const waiter = waiters.get(event.event_id); if (waiter) waiter({ ok, event }); else results.set(event.event_id, { ok, event }); }; socket.on('lot_change', settle(true)); socket.on('gateway_error', settle(false)); // 3. Send the command. const res = await fetch(`https://api.parklio.com/v2/products/${productId}/state`, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ operation: 'open' }), }); const body = await res.json(); if (!body.success) throw new Error(JSON.stringify(body.errors)); const eventId: string = body.data.event_id; console.log('Sent, event_id', eventId); // 4. Wait for the matching event. const { ok, event } = await new Promise((resolve) => { const early = results.get(eventId); if (early) resolve(early); else waiters.set(eventId, resolve); }); console.log(ok ? `Done: ${JSON.stringify(event.data.barrier ?? event.data)}` : `Failed: ${event.message ?? event.type}`); socket.close(); ``` **Python** ```python title="open.py" import json import os import threading import httpx import socketio TOKEN = os.environ["PARKLIO_API_TOKEN"] PRODUCT_ID = os.environ["PRODUCT_ID"] NS = "/v2/realtime" sio = socketio.Client() results = {} event_id = None done = threading.Event() lock = threading.Lock() def settle(ok): def handler(raw): global event_id event = json.loads(raw) # payloads are JSON strings if not event.get("event_id"): return # a status update nobody requested with lock: results[event["event_id"]] = (ok, event) if event["event_id"] == event_id: done.set() return handler sio.on("lot_change", settle(True), namespace=NS) sio.on("gateway_error", settle(False), namespace=NS) # 1. Connect first. connect() returns once the server has accepted the token. sio.connect( f"https://api.parklio.com?token={TOKEN}", namespaces=[NS], socketio_path="socketio", wait_timeout=10, ) # 2. Send the command. res = httpx.post( f"https://api.parklio.com/v2/products/{PRODUCT_ID}/state", headers={"Authorization": f"Bearer {TOKEN}"}, json={"operation": "open"}, ) body = res.json() if not body["success"]: raise SystemExit(body["errors"]) with lock: event_id = body["data"]["event_id"] if event_id in results: # it arrived before the HTTP response done.set() print("Sent, event_id", event_id) # 3. Wait for the matching event. done.wait(timeout=180) ok, event = results.get(event_id, (False, {"type": "no result"})) print("Done" if ok else f"Failed: {event.get('message', event.get('type'))}") sio.disconnect() ``` ### Verify it worked - The script prints the `event_id` from the response, then **Done** once a `lot_change` with the same `event_id` arrives. - The type block in that event shows the new state, for example `"state": "opened"`. - On site, the product has moved: a barrier's arm is down, a gate is open. ### Common errors | Code | Where | Meaning | Fix | | --- | --- | --- | --- | | `1` | HTTP 401 | The token is missing, invalid or revoked. | Check the `Authorization: Bearer ` header. | | `30` | HTTP 403 | The product has no API subscription that includes remote control. | Subscribe it; see [What you need](/get-started/what-you-need#2-subscribe-each-product-to-the-api). | | `214` | HTTP 403 | An earlier operation on this product hasn't finished. | Wait for its result, then try again. | | `704` | HTTP 403 | No Gateway has seen the product recently. | Check the Gateway is online, on the same lot and in range. | | `gateway_error` | realtime | The command reached the Gateway but failed, for example the arm is jammed or a car is on top. `type: "Timeout"` means no Gateway answered. | See the `type` table in [Events](/realtime/events#gateway_error). | Nothing arrives at all? Make sure you connected **before** sending the command, with the same API token. See [Troubleshooting](/resources/troubleshooting). ### Next - [Asynchronous operations](/concepts/asynchronous-operations): timeouts, retries and `wait_for_completion`. - [Products and operations](/concepts/products-and-operations): which operations each product supports. - [Events](/realtime/events): everything else you can listen to. --- ## What you need Parklio has no sandbox or simulator: every integration works against real hardware. This page lists what to request, buy and set up **before** you write code. ### Checklist - [ ] **An API account**, provisioned by Parklio, with its **Super Admin** credentials. - [ ] **An API subscription for each product** you'll control through the API. - [ ] **Hardware:** at least one Parklio product. - [ ] **A Gateway on the same lot**, online and in Bluetooth range of your products, if you'll control them remotely (from your backend, Zapier or the PMS). - [ ] **Products and Gateway paired** with your account, using the Parklio Connect app. - [ ] **An API token**, created once by the Super Admin. Which of these you need depends on your path; see [Choose your path](/get-started/choose-your-path). For example, an app built with the Parklio SDK talks to products over Bluetooth and doesn't need a Gateway. ### 1. Get an API account Contact [info@parklio.com](mailto:info@parklio.com). Parklio provisions a dedicated **API account** for your integration, and you receive the credentials of its **Super Admin**: the user who pairs your hardware in the Connect app and creates your API tokens. Use this account for your integration rather than an existing Parklio Connect account. A Connect account is meant for a single lot. ### 2. Subscribe each product to the API Controlling a product through the API needs an active API subscription **for that product**: subscriptions are per product, per month. Start one in the API tab at [subscriptions.parklio.com](https://subscriptions.parklio.com/?product=api). Without it, remote-control calls such as [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state) return 403 with error code `30`. See [Plans and permissions](/concepts/plans-and-permissions). ### 3. Get the hardware Pick products based on what you're protecting or monitoring: | Product | Use case | | --- | --- | | [Parklio Barrier](https://parklio.com/en/parking-protection/parking-barrier) | Reserve and protect a single parking space | | Parklio Gate, Chain or Bollard | Control vehicle access to a lot or zone | | Parklio Brain | Retrofit an existing gate or garage door so Parklio can control it | | Parklio LPR camera | Recognize license plates at entries and exits | | Parklio Gateway ([manual](https://parklio.com/assets/support/gateway/Parklio-Gateway-UM.pdf)) | Connects your products to the Parklio cloud; needed for remote control | Purchasing goes through the Parklio team directly; there's no self-serve storefront. Email [info@parklio.com](mailto:info@parklio.com) with the products you're interested in and a short description of your use case. The team will help size your order: how many products and how much Gateway coverage your lot needs. Hardware and API access are purchased separately: you need both. Manuals: [Barrier](https://parklio.com/assets/support/barrier/Parklio-barrier-usermanual.pdf) · [Gateway](https://parklio.com/assets/support/gateway/Parklio-Gateway-UM.pdf) ### 4. Pair the hardware with the Connect app The only way to add products and Gateways to an account is the **Parklio Connect app** (called "Parklio" in the app stores). The API has no pairing endpoints. Log in to the app with your **Super Admin** credentials. **Add a product** 1. In the **Lots** tab, select the lot and zone. 2. Add the product, following its manual (for example the [Barrier user manual](https://parklio.com/assets/support/barrier/Parklio-barrier-usermanual.pdf)). **Add a Gateway** 1. Make sure the Gateway has a network connection (Ethernet or mobile), following the [Gateway manual](https://parklio.com/assets/support/gateway/Parklio-Gateway-UM.pdf). 2. On the lot screen, before tapping **Add**, put the Gateway into pairing mode with a short press of its power button. Its LEDs start blinking. 3. Tap **Add** to link the Gateway to the lot. Add the Gateway to the **same lot** as the products it should reach. ### 5. Create an API token Log in **once** through the API as the Super Admin, and create an API token. Your integration then uses only the API token. See [Authentication](/api/authentication). :::warning Logging in through the API signs that user out of the Connect app. Never log in with a person's credentials at runtime. ::: ### Verify it worked - [`GET /v2/products`](/api/endpoints/products-find-all) with your API token lists your products. - The products you'll control remotely show `"status": "online"`. If a product stays offline, see [Troubleshooting](/resources/troubleshooting). ### Next Make your first call with the [Quickstart](/get-started/quickstart). --- ## Live occupancy Show how full your lots, zones and parking places are, and keep the numbers live. This guide covers the counts, the per-place status, the realtime events, history and analytics, and public lot share views. ### Prerequisites - An API token; see [Authentication](/api/authentication). - A source of occupancy on the lot. Parking detection (cameras or sensors) is set up by Parklio technicians; you read the results, you don't configure detection. LPR cameras and payment machines also move the counts as vehicles enter and leave. - For per-place status and history: the **`PARKING_DETECTION`** feature; see [Plans and permissions](/concepts/plans-and-permissions). ### Where the numbers come from | Level | Fields | Updated by | | --- | --- | --- | | Lot | `spot_count`, `occupied_count` | Every change in one of its zones | | Zone | `spot_count`, `occupied_count` | Detection sensors, LPR entries and exits, payment machines | | Parking place | `occupancy_status`: `free`, `occupied` or `unknown` | Detection sensors only | - Counts move by one on each entry or exit, and are kept between `0` and `spot_count`. - On lots with parking detection, Parklio also recounts every **5 minutes** from the parking places: a zone's `occupied_count` becomes the number of its places that aren't `free`, and the lot's becomes the sum of its zones. - Free spaces = `spot_count` − `occupied_count`. ### 1. Read the counts ```bash title="All zones of lot 7" curl -s "https://api.parklio.com/v2/zones?filter=lot_id:eq:7&size=100" \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" ``` ```json title="One zone in the response" { "id": 9, "lot_id": 7, "name": "Level -1", "spot_count": 120, "occupied_count": 42 } ``` The lot totals are on [`GET /v2/lots/{id}`](/api/endpoints/lots-find-one). ### 2. Read parking places With parking detection, each space is a **parking place** with its own status: ```bash title="Occupied places in zone 9" curl -s "https://api.parklio.com/v2/parking-places?filter=zone_id:eq:9&filter=occupancy_status:eq:occupied&size=300" \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" ``` [`GET /v2/parking-places`](/api/endpoints/parking-places-find-all) can also filter by `lot_id`, `name`, `uuid` and `external_id`. Without the `PARKING_DETECTION` feature, every place's `occupancy_status` reads `unknown`. ### 3. Keep it live Each time a detection sensor sees a change, three events arrive, in this order: | Event | Contains | | --- | --- | | `lot_detection_change` | The lot's new `occupied_count` | | `zone_change` | The zone, with `spot_count` and `occupied_count` | | `parking_place_change` | The place and its `occupancy_status` | Shapes are in [Events](/realtime/events#occupancy). ```ts title="live-occupancy.ts" const zones = new Map(); socket.on('zone_change', (raw: string) => { for (const zone of JSON.parse(raw).zones) { zones.set(zone.id, { spot_count: zone.spot_count, occupied_count: zone.occupied_count }); render(zone.id); // your UI } }); function render(zoneId: number) { const zone = zones.get(zoneId); if (zone) console.log(`zone ${zoneId}: ${zone.spot_count - zone.occupied_count} free`); } ``` :::warning Not every change is pushed Count changes from **LPR entries and exits** and **payment machines**, and the 5-minute recount, send **no** event. If your lots count vehicles that way, re-read the zones every minute or so, as well as listening for events. Always re-read after a reconnect. ::: ### 4. Set counts from your own system If you count vehicles yourself, you can write a zone's counts with [`PATCH /v2/zones/{id}`](/api/endpoints/zones-update); the lot follows by the difference: ```bash title="Set zone 9 to 40 occupied" curl -s -X PATCH https://api.parklio.com/v2/zones/9 \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"occupied_count": 40}' ``` - Keep `occupied_count` at or below `spot_count` yourself; this call doesn't enforce it. - On lots with parking detection, the 5-minute recount overwrites what you set. - Lot counts can't be set directly. ### 5. History and analytics - [`GET /v2/parking-place-changes`](/api/endpoints/parking-place-changes-find-all): every status change of every place, with its `duration` in seconds. Filter by `lot_id`, `zone_id`, `parking_place_id`, `occupancy_status` or `created_at`; [export as CSV](/api/endpoints/parking-place-changes-export-to-csv). Needs `PARKING_DETECTION`. - [`GET /v2/parking-analytics`](/api/endpoints/parking-analytics-get-analytics): average and total occupancy duration, turnover and peak hour, per lot, zone or place, over a date range, optionally per hour, day or month. Every option is a `filter`: ```bash title="Daily turnover per zone for March" curl -s "https://api.parklio.com/v2/parking-analytics?filter=metric:eq:turnover_rate&filter=scope:eq:zone&filter=interval:eq:day&filter=lot_ids:eq:7&filter=created_at:gte:2026-03-01T00:00:00.000Z&filter=created_at:lte:2026-04-01T00:00:00.000Z" \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" ``` `lot_ids`, `zone_ids` and `parking_place_ids` take comma-separated ids, such as `lot_ids:eq:7,8`. Metrics: `average_duration`, `average_duration_percentage`, `total_duration`, `turnover_rate` and `peak_occupancy_hour` (which can't be combined with `interval`). ### 6. Share a live view A **lot share view** is a public web page showing live occupancy for some zones of a lot, with no login. Use it for a screen at the entrance, or a link on your website. ```bash title="Create a share view" curl -s -X POST https://api.parklio.com/v2/lot-share-views \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"lot_id": 7, "zone_ids": [9, 10], "lot_share_view_type": "counts_only"}' ``` - The response's **`url`** is the public page. Anyone with it can see the page, so share it as you would a password. - `lot_share_view_type`: `counts_only` (free, occupied and total per zone), `live_camera` (adds a camera frame; needs a parking-detection camera in a zone) or `vector_map` (adds a map of the places; needs zones with a map). - Add `?navbar=hidden` or `?darkmode=true` to the URL to change how it looks. - To turn a link off, revoke it: [`PATCH /v2/lot-share-views/{id}`](/api/endpoints/lot-share-views-update) with `{"revoked": true}`. The page then returns 404. ### Verify it worked 1. `GET /v2/zones?filter=lot_id:eq:` shows `spot_count` and `occupied_count` for each zone. 2. Park a car on a detected space: `lot_detection_change`, `zone_change` and `parking_place_change` arrive, and `occupied_count` is one higher. 3. The share view's `url` opens in a browser without logging in. ### Common errors | Symptom | Cause | Fix | | --- | --- | --- | | Every place is `unknown` | No `PARKING_DETECTION` feature. | See [Plans and permissions](/concepts/plans-and-permissions). | | Counts change but no event arrives | The change came from LPR, a payment machine or the recount. | Poll zones as well (step 3). | | A count you set came back different | The 5-minute recount on a detection lot. | Don't set counts on detection lots. | | `403` code `30` on place history | No `PARKING_DETECTION` feature. | As above. | | `400` code `33` | A filter that endpoint doesn't accept. | Check the endpoint's filters. | | Share view returns 404 | It was revoked. | Create a new one. | ### Next - [Events](/realtime/events#occupancy) - [Sessions and tariffs](/guides/sessions-and-tariffs) --- ## Operation tokens for your app ## Operation tokens for your mobile app Let users of **your own app** open and close products over Bluetooth, even where the site has no network. Your backend creates a short-lived **operation token** for one product; your app hands it to the product through the Parklio SDK. This guide covers the backend side in full, and the app side as a flow. ### Prerequisites - An API token, on your backend only; see [Authentication](/api/authentication). Never ship it in the app. - Products paired with your account. A Gateway isn't needed for this path. - Access to the Parklio SDK for your app (Android, iOS): it's shared under contract. Contact [info@parklio.com](mailto:info@parklio.com). See [SDK](/sdk). ### The flow 1. A user asks your app for access to a product, for example when their booking starts. 2. Your app asks **your** backend, which checks the user is allowed. 3. Your backend creates an operation token for that product and a short window, and returns it to the app. 4. The app, through the SDK, sends the token's open or close command to the product over Bluetooth. The product checks the signature and the window itself; no network is needed at the product. 5. When the window ends, the token stops working. ### 1. Create a token, just in time Call [`POST /v2/operation-tokens`](/api/endpoints/operation-tokens-create) with the product and a window. Times are ISO-8601 in UTC. **curl** ```bash title="Create an operation token" curl -s -X POST https://api.parklio.com/v2/operation-tokens \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "product_id": 123, "start_time": "2026-03-02T08:00:00.000Z", "end_time": "2026-03-02T10:00:00.000Z" }' ``` **Node.js** ```ts title="create-token.ts" export async function createOperationToken(productId: number, minutes: number) { const start = new Date(); const end = new Date(start.getTime() + minutes * 60_000); const res = await fetch('https://api.parklio.com/v2/operation-tokens', { method: 'POST', headers: { Authorization: `Bearer ${process.env.PARKLIO_API_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ product_id: productId, start_time: start.toISOString(), end_time: end.toISOString(), }), }); const body = await res.json(); if (!body.success) throw new Error(`${body.errors[0].code}: ${body.errors[0].message}`); return body.data; } ``` **Python** ```python title="create_token.py" import os from datetime import datetime, timedelta, timezone import httpx def create_operation_token(product_id, minutes): start = datetime.now(timezone.utc) end = start + timedelta(minutes=minutes) body = httpx.post( "https://api.parklio.com/v2/operation-tokens", headers={"Authorization": f"Bearer {os.environ['PARKLIO_API_TOKEN']}"}, json={ "product_id": product_id, "start_time": start.isoformat(), "end_time": end.isoformat(), }, ).json() if not body["success"]: error = body["errors"][0] raise RuntimeError(f"{error['code']}: {error['message']}") return body["data"] ``` The response (HTTP 200): ```json title="Response" { "success": true, "data": { "id": 9001, "product_id": 123, "start_time": "2026-03-02T08:00:00.000Z", "end_time": "2026-03-02T10:00:00.000Z", "status": "active", "used_count": 0, "is_unlimited": 0, "token_open": { "token": "", "status_key": "", "challenge_key": "" }, "token_close": { "token": "", "status_key": "", "challenge_key": "" }, "token_status": { "token": "", "status_key": "", "challenge_key": "" }, "product": { "id": 123, "device_id": "B1234567" }, "created_at": "2026-03-02T07:59:58.000Z", "updated_at": "2026-03-02T07:59:58.000Z" } } ``` Return `token_open`, `token_close` and `token_status`, unchanged, plus the product's `device_id` and `end_time`, to your app. The SDK documentation explains how the app uses them. ### 2. Keep windows short **Revoking a token doesn't reach the device.** The window is signed into the token, and the product checks it on its own. Blocking a token ([`PATCH /v2/operation-tokens/{id}`](/api/endpoints/operation-tokens-update) with `{"status": "blocked"}`) or deleting it only changes Parklio's records; a copy the app already holds keeps working until `end_time`. So the window **is** your revocation: - Create tokens when the user needs access, not in advance. - Make the window as short as your use case allows: the length of a booking, or a few minutes for a single "open now". - Create a new token to extend access; a token can't be changed. - **Don't use `"is_unlimited": true`.** It sets `end_time` to 2038-01-01, and such a token can never be revoked on the device. - `start_time` may be in the past; `end_time` must be in the future. ### 3. Store nothing long-lived - Don't keep tokens in your database once delivered, and don't let the app cache them beyond `end_time`. Create a new one each time. - Keep the API token on your backend. The app should only ever see operation tokens. - Use HTTPS between your app and backend, and authenticate the user before you create a token for them: whoever holds a token can operate the product until it expires. ### 4. Check the device clock Time windows need the product's clock to be set. If a product reports `product_error.rtcc_invalid` = `1` (see [Monitor devices](/guides/monitor-devices)), its clock isn't set and time-limited tokens fail until it is. Parklio sets the clock when a Gateway sees the product; on a site with no Gateway, contact [support](/resources/support). ### What the API tracks, and what it doesn't - `status` becomes `expired` (checked hourly) after `end_time`, `blocked` after you block it, and `invalid` when the product is removed from its lot or paired again. - `used_count` **doesn't** count uses over Bluetooth: the product doesn't report them to Parklio. Log opens in your app if you need them. - [`GET /v2/operation-tokens`](/api/endpoints/operation-tokens-find-all) lists tokens, filterable by `product_id` and `status`. - Operation tokens need no plan feature and no Gateway. ### Verify it worked 1. `POST /v2/operation-tokens` returns `status: "active"` and the three commands. 2. Your app, near the product, sends `token_open`: the product opens (on a barrier, the arm lowers). 3. After `end_time`, the same command is refused by the product. ### Common errors | HTTP | Code | Cause | Fix | | --- | --- | --- | --- | | 403 | `402` | Neither `end_time` nor `is_unlimited`. | Send an `end_time`. | | 403 | `403` | `end_time` is in the past. | Use a future `end_time`. | | 403 | `405` | `end_time` is before `start_time`. | Fix the window. | | 403 | `404` | A time after 2038. | Use an earlier time. | | 403 | `206` | The product isn't paired. | Pair it with the Connect app. | | 404 | `6` | No such product, or your token can't see it. | Check the id and the product's lot. | | — | — | The product refuses the token. | Check the window, the device clock (`rtcc_invalid`), and that the product wasn't paired again since the token was created. | ### Next - [SDK](/sdk) - [Access and keys](/concepts/access-and-keys): the other ways to share access. --- ## Monitor devices Know when a product or Gateway needs attention: offline, low battery, jammed, or failing commands. This guide shows what Parklio tells you, what it doesn't, and how to combine realtime events with REST checks. ### Prerequisites - An API token on a server; see [Authentication](/api/authentication). - A realtime connection; see [Connecting](/realtime/connecting). ### What you can watch | Signal | Where | Pushed in realtime? | | --- | --- | --- | | Product state, `battery_status`, `rssi`, `product_error` | `lot_change` (no `event_id`), and the product in REST | Yes, while a Gateway sees the product | | Product online or offline | `status` in `lot_change`, and on [`GET /v2/products`](/api/endpoints/products-find-all) | Yes | | Products with an error | [`GET /v2/products/errors`](/api/endpoints/products-find-all-product-errors) | (via `lot_change`) | | Gateway online or offline | `gateway_status`, and `status` on [`GET /v2/gateways`](/api/endpoints/gateways-find-all) | Yes, for Gateways assigned to a lot | | Failed commands | `gateway_error` | Yes | ### 1. Watch status updates While a Gateway sees a product, it reports it regularly, and each report arrives as a `lot_change` **without** `event_id`, twice, whether or not anything changed. When no Gateway sees the product any more, or its Gateway disconnects, a `lot_change` with `status` `offline` follows. Keep the last value per product and alert on changes; see [Deduplicate status updates](/realtime/patterns#deduplicate-status-updates). ```ts title="watch-products.ts" // alert() and resolve() stand for your alerting. const last = new Map(); socket.on('lot_change', (raw: string) => { const { event_id, data } = JSON.parse(raw); if (event_id) return; // command results are handled elsewhere const online = data.status === 'online'; const errorCode: string | null = data.product_error?.error_code ?? null; const low = isLowBattery(data.battery_status); const prev = last.get(data.id); last.set(data.id, { online, errorCode, low }); if (!online && prev?.online) alert(data, 'offline'); if (online && prev?.online === false) resolve(data, 'back online'); if (errorCode && errorCode !== prev?.errorCode) alert(data, `error flags ${errorCode}`, data.product_error); if (!errorCode && prev?.errorCode) resolve(data, 'error cleared'); if (low && !prev?.low) alert(data, `battery at ${data.battery_status}%`); }); function isLowBattery(value: unknown) { // Only barriers report a battery level. 0–100 is percent; above 100 means grid power. return typeof value === 'number' && value <= 20; } ``` #### Online and offline `status` in `lot_change` is `online` or `offline`. `battery_status` and `rssi` are `null` while a product is offline. Don't alert on a single offline event. Products can show `offline` briefly, for example while their Gateway reconnects or during Parklio maintenance, until a Gateway sees them again. Alert when a product hasn't come back online within a few minutes. #### Error flags `product_error` is present while the device reports at least one problem, and gone when none is left. Each flag is `0` or `1`: | Flag | Products | Suggested action | | --- | --- | --- | | `jammed` | Barrier | Send someone on site. | | `sensor_dirty` | Barrier | Clean the car sensor. | | `battery_empty` | Barrier | Replace or charge the battery. | | `pin_broken` | Barrier | See the product's manual. | | `rtcc_invalid` | All | The device's clock isn't set; time-limited credentials fail until it is. Parklio sets it when a Gateway sees the device. | | `master_key_not_set` | All | See the product's manual. | For a list of every product that currently has an error, call [`GET /v2/products/errors`](/api/endpoints/products-find-all-product-errors). It accepts the same filters as the products list, such as `lot_id`. #### Battery Only **barriers** report `battery_status`. It's a percentage from 0 to 100; Parklio sets `battery_empty` below 15%. Values **above 100** mean the barrier runs on grid power, not a battery level. Alert somewhere around 20% to leave time to act. ### 2. Watch Gateways `gateway_status` tells you when a Gateway assigned to a lot connects or disconnects. It identifies the Gateway by `uuid`; match it with the `uuid` field of [`GET /v2/gateways`](/api/endpoints/gateways-find-all). ```ts title="watch-gateways.ts" socket.on('gateway_status', (raw: string) => { const { uuid, status } = JSON.parse(raw); if (status === 'offline') alert({ uuid }, 'Gateway offline'); }); ``` When a Gateway goes offline, the products only it could see become `offline` too, and commands to them fail with `704`. To check all Gateways at once, for example after a reconnect: ```bash title="Gateways that are offline" curl -s "https://api.parklio.com/v2/gateways?filter=status:eq:offline" \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" ``` ### 3. Watch failed commands A run of `gateway_error` events for the same product, especially `5` (jammed), `7` (sensor dirty) or repeated `"Timeout"`, is often the first sign of trouble on site. Count them per product and alert above a threshold. See [Remote control](/guides/remote-control#3-handle-failures). ### Alerting ideas | Alert | Rule of thumb | | --- | --- | | Product offline | `lot_change` with `status` `offline`, not back within a few minutes. | | Gateway offline | `gateway_status` offline, not back within a few minutes. | | Low battery | Barrier `battery_status` ≤ 20. | | Needs service | `jammed` or `sensor_dirty` set. | | Commands failing | Three `gateway_error`s for one product within an hour. | ### Verify it worked 1. Your monitor logs a `lot_change` without `event_id` for each product within a few minutes of starting. 2. Unplug a Gateway (or switch off its network): you get `gateway_status` with `offline`, and a `lot_change` with `status` `offline` for each of its products. 3. Plug it back in: `gateway_status` `online`, and the products' `lot_change` events resume. ### Common errors | Symptom | Cause | Fix | | --- | --- | --- | | No `lot_change` for a product at all | No Gateway sees it, or it's on a lot your token can't see. | Check its `status` and `lot_id`; see [Troubleshooting](/resources/troubleshooting). | | Duplicate alerts | Status updates repeat, and each is sent twice. | Alert on changes only. | | `battery_status` is `null` | The product is offline, or isn't a barrier. | Expected. | | `battery_status` above 100 | The barrier is on grid power. | Not a battery level; ignore. | | `gateway_status` for an unknown `uuid` | The Gateway isn't in your cached list. | Re-read `GET /v2/gateways`. | | Missed changes after a disconnect | No replay. | Re-read [`GET /v2/products`](/api/endpoints/products-find-all) after every reconnect. | ### Next - [Remote control](/guides/remote-control) - [Events](/realtime/events) --- ## Plate-based access Let known vehicles in by their license plate: an LPR camera reads the plate, Parklio checks it against your **lot whitelist**, and opens the way. This guide sets up the whitelist, binds plates to products, and shows how to follow each decision. ### Prerequisites - An API token; see [Authentication](/api/authentication). - An LPR camera installed and assigned to a **zone** of the lot. Readings from a camera with no zone are rejected. - For products to open: the product in the **same zone** as the camera, paired, and seen by an online Gateway. See [Remote control](/guides/remote-control). ### What happens on a read 1. The camera reads a plate and Parklio records a **plate reading** (`plate_reading_create` in realtime). 2. If the same plate (give or take one character) was read in the same zone in the last 20 seconds, the reading is denied with reason `debounce` and nothing else happens. 3. Parklio looks for an **active** whitelist entry for the plate on the camera's lot, whose window includes now. Matching tolerates one misread character by default. 4. **On the whitelist:** - the decision is `triggered`, reason `whitelist`; - the camera's own output is triggered (for a gate or barrier wired to the camera); - for a vehicle coming **in**, Parklio opens the first **bound** product in the camera's zone that is **online and closed**, through a Gateway; - in starts a [session](/guides/sessions-and-tariffs), out ends it. 5. **Not on the whitelist:** [tariffs](/guides/sessions-and-tariffs) decide. 6. The decision is recorded on the reading (`plate_reading_update` in realtime). A plate opens a bound product at most once every **2 minutes**, so a camera reading the same car twice doesn't open two barriers. ### 1. Add plates to the whitelist [`POST /v2/lot-whitelists`](/api/endpoints/lot-whitelist-create) adds up to 20 plates to a lot for a time window. Always send `lot_id`. ```bash title="Whitelist two plates for March" curl -s -X POST https://api.parklio.com/v2/lot-whitelists \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "lot_id": 7, "name": "Tenants", "license_plates": ["ZG1234AB", "ST-555-CD"], "start_time": "2026-03-01T00:00:00.000Z", "end_time": "2026-04-01T00:00:00.000Z" }' ``` - Plates are 3–16 characters. Parklio stores them in uppercase, without hyphens: `ST-555-CD` becomes `ST555CD`. - Send **either** `end_time` **or** `"is_unlimited": true`, not both. - `end_time` must be in the future, and after `start_time`. The response has one item per plate. Keep each **`license_id`**: you need it to bind the plate to products. ```json title="Response" { "success": true, "data": [ { "success": true, "whitelist_id": 501, "license_id": 88, "plate": "ZG1234AB", "is_unlimited": 0 }, { "success": true, "whitelist_id": 502, "license_id": 89, "plate": "ST555CD", "is_unlimited": 0 } ] } ``` A plate that couldn't be added has `"success": false`; check every item. For many plates, upload a CSV to [`POST /v2/lot-whitelists/upload/lot/{id}`](/api/endpoints/lot-whitelist-create-from-file); [`GET /v2/lot-whitelists/sample/export`](/api/endpoints/lot-whitelist-export-sample-csv) gives you the format. Entries become `expired` (checked hourly) after `end_time`. To remove access earlier, delete the entry with [`DELETE /v2/lot-whitelists/{id}`](/api/endpoints/lot-whitelist-remove). [`GET /v2/lot-whitelists`](/api/endpoints/lot-whitelist-find-all) lists entries, with their `license_plate_id` (the same as `license_id`) and `bound_products`. ### 2. Bind plates to products Binding tells Parklio which products a plate may open. Use the `license_id` values from step 1: ```bash title="Bind two plates to product 123" curl -s -X POST https://api.parklio.com/v2/products/123/bind-licenses \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"license_ids": [88, 89]}' ``` To bind the same plates to several products, use [`POST /v2/products/bind-licenses`](/api/endpoints/products-bind-license-for-products) with `product_ids` and `license_ids`. :::warning Unknown ids are ignored silently The bind call succeeds even if a `license_id` doesn't exist. Check the result with [`GET /v2/products/{id}/bound-licenses`](/api/endpoints/products-find-all-binds). ::: A binding belongs to the **plate**, not to one whitelist entry: it stays when an entry expires, and applies again when you whitelist the plate later. It only opens anything while the plate is on an active whitelist entry. Remove a binding with [`DELETE /v2/products/{id}/bound-licenses/{licenseId}`](/api/endpoints/products-unbind-license-for-product). A plate on the whitelist but bound to nothing still gets `triggered` and starts a session; only the bound-product step is skipped. ### 3. Follow decisions Each reading ends with an `access_decision` and an `access_decision_reason`: | Decision | Reason | When | | --- | --- | --- | | `triggered` | `whitelist` | Active whitelist entry on the lot. | | `denied` | `debounce` | Same plate, same zone, within 20 seconds. | | `triggered` | `tariff`, `session_exit` | Let in by a tariff, or out with an open session. See [Sessions and tariffs](/guides/sessions-and-tariffs). | | `denied` | `unknown_direction`, `direction_mismatch`, `no_tariff`, `max_capacity`, `no_active_session` | Not whitelisted, and no tariff or session allows it. | In realtime, listen for `plate_reading_update`; its `access_decision` is set (see [Events](/realtime/events#plate_reading_update)). Over REST, filter [`GET /v2/plate-readings`](/api/endpoints/plate-readings-find-all): ```bash title="Denied readings at lot 7" curl -s "https://api.parklio.com/v2/plate-readings?filter=lot_id:eq:7&filter=access_decision:eq:denied&sort=created_at:desc" \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" ``` Also filterable: `plate`, `zone_id`, `lpr_camera_id`, `direction`, `access_decision_reason` and `created_at`. When a bound product opens, its result arrives as a `lot_change`, with an `event_id` you didn't request. If it fails, a `gateway_error` arrives, but the reading still says `triggered`: the decision is about the plate, not the product. ### Verify it worked 1. Drive a whitelisted car past the camera, going in. 2. `plate_reading_create` arrives, then `plate_reading_update` with `access_decision: "triggered"` and reason `whitelist`. 3. A `lot_change` shows the bound product `opened`, and a `session_create` arrives. 4. [`GET /v2/products/{id}/bound-licenses`](/api/endpoints/products-find-all-binds) lists the plate. ### Common errors | Symptom | Cause | Fix | | --- | --- | --- | | Reason `no_tariff` or `no_active_session` for a plate you whitelisted | No active entry on **this lot** right now, or the plate was read differently. | Check the entry's `lot_id`, `status` and window; compare `plate` on the reading. | | `triggered` but the product didn't open | Not bound, not in the camera's zone, offline, already open, or opened for this plate in the last 2 minutes. | Check the binding, the product's `zone_id`, `status` and state. | | Reason `debounce` | The camera read the same plate within 20 seconds. | Expected. | | A plate item with `"success": false` | That plate couldn't be added. | Check its length and the window. | | `403` code `402` | Neither `end_time` nor `is_unlimited`. | Send one of them. | | `403` code `403` | `end_time` is in the past. | Use a future `end_time`. | ### Next - [Sessions and tariffs](/guides/sessions-and-tariffs): what happens to cars that aren't whitelisted. - [Events](/realtime/events#plate_reading_create) --- ## Remote control Open and close barriers, gates, chains, bollards and brains from your backend, and know for sure whether each command worked. This guide turns the [Quickstart](/get-started/quickstart) into something you can run in production. ### Prerequisites - An API token on a server; see [Authentication](/api/authentication). - A subscription with **remote control** for each product; see [Plans and permissions](/concepts/plans-and-permissions). - Each product paired, on a lot, with an online Gateway in Bluetooth range on the same lot. See [What you need](/get-started/what-you-need). - The Socket.IO client for your language; see [Connecting](/realtime/connecting). ### How a command works You send a command, the API answers at once with an `event_id`, and the result arrives later as a realtime event: `lot_change` on success, `gateway_error` on failure. The API tries each Gateway that sees the product, strongest signal first, for up to 30 seconds each. The details are in [Asynchronous operations](/concepts/asynchronous-operations). ### 1. Check the operation fits the product The API doesn't check that an operation makes sense for the product type; check it yourself before sending. `product_type_id` comes from [`GET /v2/products/{id}`](/api/endpoints/products-find-one). ```ts title="operations.ts" const BARRIER = 2; const BRAIN_TYPES = [3, 4, 5, 6]; // gate, brain, chain, bollard const ALLOWED: Record = { open: [BARRIER, ...BRAIN_TYPES], close: [BARRIER, ...BRAIN_TYPES], force_close: [BARRIER], stay_open: BRAIN_TYPES, stay_close: BRAIN_TYPES, clear_stay_open: BRAIN_TYPES, clear_stay_close: BRAIN_TYPES, }; export function canRun(product: { status: string; product_type_id: number }, operation: string) { return product.status === 'online' && (ALLOWED[operation] ?? []).includes(product.product_type_id); } ``` The matrix, and what `open` and `close` mean on a barrier (`open` lowers the arm), is in [Products and operations](/concepts/products-and-operations#operations). ### 2. Connect, send, and wait Connect to realtime **before** you send anything, and keep the connection open for the life of your service. Then, for each command, send it and wait for the event that carries the same `event_id`: **Node.js** ```ts title="run-operation.ts" const API = 'https://api.parklio.com/v2'; const headers = { Authorization: `Bearer ${process.env.PARKLIO_API_TOKEN}`, 'Content-Type': 'application/json', }; // Connect once, before sending anything. const socket = io(`${API}/realtime`, { path: '/socketio', query: { token: process.env.PARKLIO_API_TOKEN }, }); export async function runOperation(productId: number, operation: string) { // Listen before sending: the result can arrive before the HTTP response does. const results = new Map(); let arrived = () => {}; const record = (ok: boolean) => (raw: string) => { const event = JSON.parse(raw); if (!event.event_id) return; // a status update, not a command result results.set(event.event_id, { ok, event }); arrived(); }; const onSuccess = record(true); const onFailure = record(false); socket.on('lot_change', onSuccess); socket.on('gateway_error', onFailure); let timedOut = false; const timer = setTimeout(() => { timedOut = true; arrived(); }, 120_000); try { const res = await fetch(`${API}/products/${productId}/state`, { method: 'POST', headers, body: JSON.stringify({ operation }), }); const body = await res.json(); if (!body.success) { // Rejected before anything was sent to the device. return { ok: false, rejected: true, code: body.errors[0].code, message: body.errors[0].message }; } const eventId: string = body.data.event_id; while (!results.has(eventId) && !timedOut) { await new Promise((resolve) => (arrived = resolve)); } const result = results.get(eventId); if (!result) return { ok: false, unknown: true }; const { event } = result; if (result.ok) { const { data } = event; const block = data.barrier ?? data.gate ?? data.chain ?? data.bollard ?? data.brain; return { ok: true, state: block?.state }; } return { ok: false, type: event.type, message: event.message }; } finally { clearTimeout(timer); socket.off('lot_change', onSuccess); socket.off('gateway_error', onFailure); } } ``` **Python** ```python title="run_operation.py" import json import os import threading import httpx import socketio API = "https://api.parklio.com/v2" TOKEN = os.environ["PARKLIO_API_TOKEN"] HEADERS = {"Authorization": f"Bearer {TOKEN}"} NS = "/v2/realtime" sio = socketio.Client() results = {} # event_id -> (ok, event) arrived = threading.Condition() def record(ok): def handler(raw): event = json.loads(raw) if not event.get("event_id"): return # a status update, not a command result with arrived: results[event["event_id"]] = (ok, event) arrived.notify_all() return handler sio.on("lot_change", record(True), namespace=NS) sio.on("gateway_error", record(False), namespace=NS) # Connect once, before sending anything. sio.connect(f"https://api.parklio.com?token={TOKEN}", namespaces=[NS], socketio_path="socketio") def run_operation(product_id, operation): body = httpx.post( f"{API}/products/{product_id}/state", headers=HEADERS, json={"operation": operation} ).json() if not body["success"]: # Rejected before anything was sent to the device. error = body["errors"][0] return {"ok": False, "rejected": True, "code": error["code"], "message": error["message"]} event_id = body["data"]["event_id"] with arrived: if not arrived.wait_for(lambda: event_id in results, timeout=120): return {"ok": False, "unknown": True} ok, event = results.pop(event_id) if ok: data = event["data"] block = next( (data[k] for k in ("barrier", "gate", "chain", "bollard", "brain") if data.get(k)), None, ) return {"ok": True, "state": block and block["state"]} return {"ok": False, "type": event["type"], "message": event.get("message")} ``` The function has four outcomes: **rejected** (an HTTP error, nothing was sent), **ok**, **failed** (a `gateway_error`), and **unknown** (you gave up waiting). Handle each one. ### 3. Handle failures #### Rejected: an HTTP error Nothing reached the device. Act on the error `code`: | Code | Meaning | What to do | | --- | --- | --- | | `214` | Another operation on this product is still running. | Wait for its result, then send yours. | | `704` | No Gateway sees the product right now. | Don't retry at once; check the product's `status` and the Gateway. | | `30` | The product's subscription doesn't include remote control. | Fix the subscription; retrying won't help. | | `206` | The product isn't paired. | Pair it with the Connect app. | | `6` / `4` | The product doesn't exist, or your token can't see it. | Check the id and the product's lot. | | `23` | Invalid body. | Fix the request. | `214` can also come from operations you didn't send: someone using the Connect app, or Parklio setting the device's clock. It clears as soon as that operation's result is sent, which can take up to 30 seconds per Gateway. #### Failed: a `gateway_error` The command reached at least one Gateway and didn't succeed. `type` tells you why; the full table is in [Events](/realtime/events#gateway_error). | `type` | What to do | | --- | --- | | `6` Car on top | The barrier won't rise with a car over it. Tell the user; don't retry until the car has left. | | `5` Device jammed, `7` Sensor dirty | Needs someone on site. Alert, don't retry. | | `0` Auth error, `1` Invalid guest key, `8` Device not initialized | The device rejected the command. Retrying won't help; contact [support](/resources/support) if it persists. | | `2`, `3`, `4`, `10`, `11`, `"Timeout"` | The API already tried every Gateway that sees the product. One later retry is reasonable; alert if it fails again. | | `9` Rtcc not set | The device's clock isn't set. Parklio sets it automatically when a Gateway sees the device (at most one attempt an hour per device); retry later. | #### Unknown: you gave up waiting The command may still have run. Re-read the product with [`GET /v2/products/{id}`](/api/endpoints/products-find-one) and compare its state with what you asked for before doing anything else. Don't resend blindly: on a barrier, a second `close` after a successful one is harmless, but your users may see the arm move twice. ### 4. Send one command per product at a time Queue commands per product in your service, and send the next only when the previous one has a result (or you gave up and re-read the state). This avoids `214` and keeps results easy to match. Commands for **different** products can run in parallel. ### Change barrier settings [`POST /v2/products/{id}/settings`](/api/endpoints/products-change-settings) works the same way: send **one** setting per call, get an `event_id`, wait for the result. ```bash title="Set the auto-close delay to 30 seconds" curl -s -X POST https://api.parklio.com/v2/products/123/settings \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"autoclose_time": 30}' ``` `autoclose_time` is 5–60 seconds. To turn auto-close on or off, send `{"is_autoclose": true}` or `false` (a JSON boolean). In the `lot_change` result the barrier block calls these `auto_close_time` and `is_auto_close`; see [Events](/realtime/events#lot_change). ### Brains without input sensors A brain wired to a gate or door with no open/closed sensor can't report whether it moved. Send `"wait_for_completion": false` with the operation, so the Gateway doesn't wait for a state change that will never come: ```json title="Request body" { "operation": "open", "wait_for_completion": false } ``` ### Verify it worked 1. `runOperation` returns `ok: true`, and the state matches the operation: `opened` after `open`, `closed` after `close`. 2. [`GET /v2/products/{id}`](/api/endpoints/products-find-one) shows the same state in its type block. 3. On site, the product moved. On a barrier, `open` **lowers** the arm. ### Common errors | Symptom | Cause | Fix | | --- | --- | --- | | Always `704` | No Gateway sees the product. | Check the Gateway is online, on the same lot and in range; see [Troubleshooting](/resources/troubleshooting). | | Frequent `214` | Commands for one product overlap. | Queue per product (step 4). | | No result, then `"Timeout"` | The Gateway couldn't reach the product over Bluetooth. | Check range and power; a result can take 30 seconds per Gateway. | | Results never arrive | You connected after sending, or the connection dropped. | Connect first; resync after reconnects ([Patterns](/realtime/patterns#resync-after-a-reconnect)). | | An unexpected error (such as HTTP 500, code `0`) for `stay_open` on a barrier | Unsupported operation for the product type. | Check the matrix (step 1). | | `403` code `30` | No remote control in the subscription. | See [Plans and permissions](/concepts/plans-and-permissions). | ### Next - [Monitor devices](/guides/monitor-devices): know when a product needs attention. - [Events](/realtime/events) --- ## Sessions and tariffs A **session** is one vehicle's stay in a zone, from entry to exit. **Tariffs** decide which vehicles an LPR camera lets in when they aren't on the [whitelist](/guides/plate-based-access), and how the stay is priced. This guide covers reading and managing sessions, and setting up tariffs, members and groups. ### Prerequisites - An API token; see [Authentication](/api/authentication). - For automatic sessions: LPR cameras assigned to zones, on the way in and the way out. ### How sessions start and end | Event | Session | `entry.type` / `exit.type` | | --- | --- | --- | | A camera lets a vehicle in (whitelist or tariff) | Starts | `lpr_camera` | | You create one with [`POST /v2/sessions`](/api/endpoints/sessions-create) | Starts | `account` (API token) or `attendant` (user) | | A camera sees the vehicle leave | Ends; `possibly_exited` becomes `true` | `lpr_camera` | | You close it with [`POST /v2/sessions/{id}/close`](/api/endpoints/sessions-close) | Ends | `account` or `attendant` | Payment machines also create and close sessions, and record payments in `amount_paid`. Session fields you'll use most: | Field | Meaning | | --- | --- | | `public_id` | A 10-character code, A–Z and 0–9, for showing to the driver. Filterable. | | `duration` | Seconds so far, or the full stay once ended. | | `ended_at` | When it ended; absent or `null` while open. | | `exit_allowed` | `true` while the session is open. | | `discount_time` | Free minutes granted, or `-1` for unlimited. | | `amount_paid` | What was paid at a payment machine. Read-only. | | `entry_license_plate` | The plate read on entry. | ### 1. Follow sessions In realtime, `session_create` and `session_update` carry the whole session; see [Events](/realtime/events#session_create). Over REST, [`GET /v2/sessions`](/api/endpoints/sessions-find-all) filters by `lot_id`, `zone_id`, `license_plate`, `public_id`, `lpr_camera_id`, `created_at` and `ended_at`: ```bash title="Open sessions in lot 7" curl -s "https://api.parklio.com/v2/sessions?filter=lot_id:eq:7&filter=ended_at:eq:null&sort=created_at:desc" \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" ``` For reports, [`GET /v2/sessions/export`](/api/endpoints/sessions-export-to-csv) returns a CSV, [`GET /v2/sessions/analytics`](/api/endpoints/sessions-get-analytics) counts entries and exits (or the peak hour) per lot or zone, and [`GET /v2/sessions/vehicle-analytics`](/api/endpoints/sessions-vehicles-analytics) gives visits and time spent per plate. ### 2. Create and change sessions Create a session for a vehicle a camera missed, or one you let in by hand. Name the camera: the session is created in that camera's zone. ```bash title="Start a session" curl -s -X POST https://api.parklio.com/v2/sessions \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"license_plate": "ZG1234AB", "lpr_camera_id": 15}' ``` - Always send both `license_plate` and `lpr_camera_id`. - No whitelist, tariff or capacity check is made, and the zone's `occupied_count` doesn't change. Grant free time, or fix a misread plate, with [`PATCH /v2/sessions/{id}`](/api/endpoints/sessions-update): ```json title="Grant 60 free minutes" { "discount_time": 60 } ``` `discount_time` is in minutes; `-1` means unlimited. A value of `0` is ignored. To correct the entry plate, send `license_plate` instead. End a session with [`POST /v2/sessions/{id}/close`](/api/endpoints/sessions-close) (no body). Close only open sessions (check `ended_at` first): closing doesn't check whether the session already ended, and doesn't change the zone's `occupied_count`. ### 3. Set up tariffs A tariff is a set of rules for a zone: when it applies, who it applies to, and how the stay is priced. Create one with [`POST /v2/tariffs`](/api/endpoints/tariffs-create); only `name` is required: ```bash title="Weekday tariff, 2 per hour after 15 free minutes" curl -s -X POST https://api.parklio.com/v2/tariffs \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Weekdays", "interval": 60, "price": 2, "entry_free_time_interval": 15, "price_progression_type": "flat", "schedules": [ { "weekday": 0, "start_time": "07:00:00", "end_time": "19:00:00" }, { "weekday": 1, "start_time": "07:00:00", "end_time": "19:00:00" }, { "weekday": 2, "start_time": "07:00:00", "end_time": "19:00:00" }, { "weekday": 3, "start_time": "07:00:00", "end_time": "19:00:00" }, { "weekday": 4, "start_time": "07:00:00", "end_time": "19:00:00" } ] }' ``` | Field | Meaning | | --- | --- | | `interval`, `price` | Price per interval of minutes (at least 15; default 60). | | `price_progression_type` | `flat` (default), `linear`, `exponential` or `incremental`. | | `entry_free_time_interval`, `exit_free_time_interval`, `gratis_time` | Free minutes. | | `maxima`, `max_time_of_stay` | Maximum price, maximum stay in minutes. | | `schedules` | Up to 7 `{weekday, start_time, end_time}`; `weekday` 0 is **Monday**. Times are in the lot's time zone. | | `start_time`, `end_time` | Absolute dates the tariff is valid between. | | `season_start`, `season_end` | A yearly season (`YYYY-MM-DD`; only day and month count). | | `group_ids`, `max_affected_members_per_group` | Limit the tariff to groups, and how many of a group's members can be inside at once. | | `currency_id` | Defaults to EUR. | Then assign the tariff to a zone with [`PUT /v2/zones/{id}/tariffs/{tariffId}`](/api/endpoints/zones-assign-member). A zone can have several tariffs; their order is set with [`PATCH /v2/zones/{zone_id}/tariffs/{tariff_id}`](/api/endpoints/zones-update-order) and `{"order": 0}`. A tariff can't be deleted while a zone still uses it (code `1400`). The API doesn't calculate prices; payment machines do, from these rules. ### 4. Members and groups To restrict a tariff to known vehicles, register their plates as **members** ([`POST /v2/members`](/api/endpoints/members-create), or a [CSV upload](/api/endpoints/members-create-from-file)), put them in a **group** ([`POST /v2/groups`](/api/endpoints/groups-create)), and give the tariff that group's id in `group_ids`. A tariff with no groups applies to every vehicle. Members and groups don't open anything by themselves; they only decide which tariffs apply. For access that doesn't depend on tariffs, use the [whitelist](/guides/plate-based-access). ### How a camera decides on entry For a plate that isn't on the lot's whitelist: 1. Find the zone's tariffs that apply **now** (dates, season, schedule) and to **this plate** (no groups, or a group the plate is a member of). 2. None: denied, reason `no_tariff`. 3. If every one of them is full (its group already has `max_affected_members_per_group` members with open sessions): denied, reason `max_capacity`. 4. Otherwise: let in, reason `tariff`, and a session starts. On the way out, a vehicle with an open session is let out (`session_exit`) and its session ends; one without is denied (`no_active_session`). The zone's `occupied_count` isn't part of the decision. ### Verify it worked 1. Create a tariff for a zone with a schedule covering now, and no groups. 2. Drive in past the zone's entry camera: `plate_reading_update` shows `access_decision: "triggered"`, reason `tariff`, and `session_create` arrives. 3. `GET /v2/sessions?filter=public_id:eq:` returns the session, with no `ended_at`. 4. Drive out: `session_update` arrives with `ended_at` set and `exit.type` `lpr_camera`. ### Common errors | Symptom | Cause | Fix | | --- | --- | --- | | Reason `no_tariff` | No tariff applies now, or none matches the plate's groups. | Check the zone's tariffs, schedules (Monday = 0, lot time) and groups. | | Reason `max_capacity` | Every applicable tariff's group is full. | Raise `max_affected_members_per_group`, or close stale sessions. | | Reason `no_active_session` on exit | The entry wasn't recorded as a session. | Create the session by hand, or check the entry camera. | | `discount_time: 0` has no effect | `0` is ignored. | Not supported today. | | `1400` when deleting a tariff | A zone still uses it. | Unassign it from its zones first. | | `1505` when correcting a plate | The session has no entry plate. | Not possible for that session. | ### Next - [Plate-based access](/guides/plate-based-access) - [Live occupancy](/guides/live-occupancy) --- ## Zapier Zapier lets you connect **Parklio** to thousands of other web services with no code. Automated workflows called **Zaps** start with a **trigger** in one app and run one or more **actions** in others. Parklio provides **actions only**; there are no Parklio triggers. Start your Zap with a trigger from another app (a schedule, a form, a calendar event…) and use a Parklio action, such as **Open Product**, as one of its steps. ### Prerequisites - An active **API subscription** for each product you want to control from Zapier: get one at [subscriptions.parklio.com](https://subscriptions.parklio.com/?product=api). - An online **Parklio Gateway** on the same lot as the product. Zapier actions reach your products through the Gateway, like any other API call. See [What you need](/get-started/what-you-need). - At least one **product added to your Parklio account**, on the **same lot as the Gateway**. - A **Parklio API token**, created by your account's **Super Admin** (below). ### Get an API token Zapier authenticates to Parklio with an API token, the same kind used for direct API access. See [Authentication](/api/authentication) for the full flow. In short: 1. [Log in once](/api/authentication#1-log-in-once-as-the-super-admin) with your account's **Super Admin** credentials. Only a Super Admin can create API tokens. 2. [Create an API token](/api/authentication#2-create-an-api-token). 3. Copy the `token` from the response. :::warning The token is only shown once, at creation time: save it before navigating away. If you lose it, create a new one; it won't invalidate the previous token, so revoke old tokens you no longer need. ::: ### Connect Parklio to Zapier 1. Log in to your [Zapier account](https://zapier.com/sign-up), or create one. 2. Navigate to **My Apps** from the top menu bar. 3. Click **Connect a new account** and search for **Parklio**. 4. Paste your Parklio API token to authorize the connection. 5. Start creating automations: use a [pre-made Zap for Parklio](https://zapier.com/apps/parklio/integrations) or build your own in the Zap Editor. For questions about Zapier itself, open a ticket with [Zapier Support](https://zapier.com/app/get-help). ### Supported actions | Action | What it does | | --- | --- | | **Open Product** | Remotely opens a Parklio product. On a barrier, the arm **lowers** so the spot can be used; on a gate, chain, bollard or brain, the passage opens. | | **Close Product** | Remotely closes a Parklio product. On a barrier, the arm **rises** so the spot is blocked; on a gate, chain, bollard or brain, the passage closes. | | **List Owned Products** | Retrieves the Parklio products on your account. Use it to populate options in other Zap steps dynamically. | | **Create Shared Key** | Shares access to a product with a user, existing or new. | Open and Close are sent to the product through the Gateway on its lot. ### Use cases - **Open for a booked visitor:** open a barrier when a visitor books an appointment in a scheduling tool (for example Calendly or Google Calendar). - **Close overnight:** close all barriers at a set time each night with a scheduled trigger (for example Zapier's Schedule tool), securing the spaces after business hours. ### Reliability tips - **Use List Owned Products** at the start of a Zap when you need dynamic device selection. - **Add conditional steps or filters** to make sure the product is available and in the right state before acting on it. - **Log every action** (for example, to Google Sheets or Slack) for monitoring and troubleshooting. ### FAQ **Why isn't my device opening or closing when the action runs in Zapier?** Check that your Parklio Gateway is online and properly connected. Also confirm the device responds in the Parklio Connect app: if it can't be operated from the app, the Zap will fail too. See [Troubleshooting](/resources/troubleshooting). **Where do I find the `product_id` for the Open and Close actions?** In the Parklio Parking Management System, select the product and open its Details tab. Alternatively, run the **List Owned Products** action in Zapier and take each product's `data.id` value from the response. **Which products work with the Open and Close actions?** Barrier, Chain, Bollard and Parklio Brain. **Who do I contact about issues with Parklio products?** [Contact Support](https://tickets.parklio.com/). --- ## Parklio Developer Docs Parklio makes parking hardware (barriers, gates, chains, bollards, LPR cameras) that you can control and monitor from your own software. The Parklio API lets you open and close products remotely, share access, recognize license plates, and read live occupancy. ### What do you want to build? Open and close from your backendRemote control through a Gateway, with realtime confirmation. Your own mobile appOperate products over Bluetooth with the Parklio SDK and operation tokens. Plate-based accessLet whitelisted vehicles in when an LPR camera reads their plate. Live occupancyRead lot, zone and parking-place occupancy as it changes. No codeOpen and close products from Zapier. - **[Open and close from your backend](/get-started/quickstart):** remote control through a Gateway, with realtime confirmation. - **[Your own mobile app](/sdk):** operate products over Bluetooth with the Parklio SDK and operation tokens. - **[Plate-based access](/concepts/access-and-keys):** let whitelisted vehicles in when an LPR camera reads their plate. - **[Live occupancy](/get-started/how-parklio-works#occupancy):** read lot, zone and parking-place occupancy as it changes. - **[No code](/integrations/zapier):** open and close products from Zapier. Not sure which fits? See [Choose your path](/get-started/choose-your-path). Before you start Parklio has no sandbox: you integrate against real hardware. You need: - an **API account**, provisioned by Parklio; - an **API subscription** for each product you control; - the **hardware**, paired with your account in the Parklio Connect app; - a **Gateway** on the same lot, for remote control. See [What you need](/get-started/what-you-need). ### Your first call ```bash title="List your products" curl https://api.parklio.com/v2/products \ -H "Authorization: Bearer $PARKLIO_API_TOKEN" ``` New here? Read [How Parklio works](/get-started/how-parklio-works), then follow the [Quickstart](/get-started/quickstart) from an API token to a confirmed open. ### Reference - [API reference](/api): every endpoint, plus [conventions](/api/conventions), [authentication](/api/authentication), [errors](/api/errors) and [rate limits](/api/rate-limits). - [Realtime](/realtime/connecting): connect to the event stream, and the [event catalog](/realtime/events). - [Concepts](/concepts/data-model): accounts, lots, products, operations and access. --- ## Connecting Parklio pushes results and status changes over a [Socket.IO](https://socket.io/docs/v4/) connection. Connect once from your server, keep the connection open, and listen for the [events](/realtime/events) you need. ### Prerequisites - An API token; see [Authentication](/api/authentication). Realtime uses the same token as the REST API. - A Socket.IO **v4** client: `socket.io-client` 4 for Node.js, or `python-socketio` 5 for Python. ### Connection settings | Setting | Value | | --- | --- | | URL | `https://api.parklio.com` | | Namespace | `/v2/realtime` | | Path | `/socketio` | | Auth | your API token in the handshake query parameter `token` | ### Connect **Node.js** ```ts title="connect.ts" import { io } from 'socket.io-client'; const socket = io('https://api.parklio.com/v2/realtime', { path: '/socketio', query: { token: process.env.PARKLIO_API_TOKEN }, }); socket.on('connect', () => console.log('Connected')); socket.on('authorized', () => console.log('Token accepted')); socket.on('connect_error', (err) => console.error('Could not connect:', err.message)); socket.on('disconnect', (reason) => console.warn('Disconnected:', reason)); // Every payload is a JSON string: parse it before reading fields. socket.on('lot_change', (raw: string) => { const event = JSON.parse(raw); console.log('lot_change', event.lot_id, event.event_id, event.data.status); }); ``` **Python** ```python title="connect.py" import json import os import socketio NS = "/v2/realtime" sio = socketio.Client() # reconnects automatically @sio.event(namespace=NS) def connect(): print("Connected") @sio.on("authorized", namespace=NS) def on_authorized(_): print("Token accepted") @sio.event(namespace=NS) def connect_error(data): print("Could not connect:", data) @sio.on("lot_change", namespace=NS) def on_lot_change(raw): event = json.loads(raw) # every payload is a JSON string print("lot_change", event["lot_id"], event.get("event_id"), event["data"]["status"]) sio.connect( f"https://api.parklio.com?token={os.environ['PARKLIO_API_TOKEN']}", namespaces=[NS], socketio_path="socketio", ) sio.wait() ``` ### Authentication The server checks the token while the connection is being set up: - **Accepted:** it sends an `authorized` event (with an empty payload), and the connection completes. Your client's `connect` fires. - **Rejected** (missing, invalid or revoked token): the connection is refused and your client gets `connect_error`. Don't retry in a loop with the same token; fix it first. Either `authorized` or `connect` tells you you're in. `authorized` can arrive just before `connect`. The server adds your connection to its rooms (below) a moment **after** that, once it has looked up your token. Events sent in that moment don't reach you, so read the state you track over REST once you're connected, not before. ### What you receive There are no subscribe messages, and the server doesn't accept any messages from you: the events you receive are fixed when you connect, by the kind of token. If a user is assigned to another lot later, their connection gets that lot's events only after it reconnects. | Token | You receive events for | | --- | --- | | API token | everything in your **account** | | User token | that **user**, plus every **lot** the user is assigned to | Use an API token for integrations. Events for things not attached to a lot (for example a product with no lot) aren't sent at all. Your connection also receives events nobody on your side requested: changes made from the Connect app, by other users, or by Parklio itself. Filter on what you care about. ### Payloads are JSON strings Every event's payload is a **string** of JSON, not an object. Call `JSON.parse` (Node) or `json.loads` (Python) first. The [event catalog](/realtime/events) shows the parsed objects. ### Reconnecting Both clients reconnect automatically, with backoff, and the server puts you back in the same rooms when you're back. But **events sent while you were disconnected are lost**: there's no replay. After every reconnect, re-read what you track over REST. See [Patterns](/realtime/patterns#resync-after-a-reconnect). ### Connect before you send commands Results of remote-control commands arrive only as realtime events. Connect, and wait until you're connected, **before** calling [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state); otherwise you can miss the result. ### Next - [Events](/realtime/events): the catalog. - [Patterns](/realtime/patterns): correlating results, deduplicating, resyncing. --- ## Events Every event the realtime connection sends, with its payload. Connect first; see [Connecting](/realtime/connecting). ### Before you read the payloads - **Payloads are JSON strings.** Parse them first. The shapes below are the parsed objects. - **Keys are snake_case**, dates are ISO-8601 strings, and many flags are `0` or `1`. - **A key whose value is missing may be left out** rather than sent as `null`. The interfaces mark those keys with `?`. - **Who receives what:** every event below except `authorized` goes to the lot it concerns and to the account that owns the lot. With an API token you get the events for your whole account; with a user token, those for the lots the user is assigned to. Something that isn't in a lot (a product with no lot, for example) sends nothing. | Event | Sent when | | --- | --- | | [`authorized`](#authorized) | Your token is accepted | | [`lot_change`](#lot_change) | A command succeeded, or a Gateway reported a product's state | | [`gateway_error`](#gateway_error) | A command failed | | [`gateway_status`](#gateway_status) | A Gateway went online or offline | | [`plate_reading_create`](#plate_reading_create) | An LPR camera read a plate | | [`plate_reading_update`](#plate_reading_update) | An access decision was made for a reading | | [`session_create`](#session_create) | A parking session started | | [`session_update`](#session_update) | A parking session changed or ended | | [`lpr_camera_status`](#lpr_camera_status) | An LPR camera reported its status | | [`lot_detection_change`](#lot_detection_change) | A lot's occupied count changed | | [`zone_change`](#zone_change) | A zone's occupied count changed | | [`parking_place_change`](#parking_place_change) | A parking place became free or occupied | | [`parking_gateway_state_change`](#parking_gateway_state_change) | A parking-detection gateway went online or offline | | [`lpr_event`](#lpr_event) | Legacy: a lot entry was recorded from a plate reading | ### Connection #### `authorized` Sent once, only to your connection, when the server accepts your token. The payload is an empty string. See [Authentication](/realtime/connecting#authentication). ### Products and Gateways #### `lot_change` The state of one product. Despite the name, it's about a product, not a lot. It's sent in two cases: - **A command succeeded.** After [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state) or [`POST /v2/products/{id}/settings`](/api/endpoints/products-change-settings), the result carries the same `event_id` the endpoint returned. You may also see `event_id`s for operations you didn't start; ignore them. - **A Gateway reported what it sees.** These have **no `event_id`**. They're sent for every product in each report, whether or not anything changed, and **each one is sent twice**. See [Deduplicate status updates](/realtime/patterns#deduplicate-status-updates). The product carries one block that matches its type: `barrier`, `gate`, `chain`, `bollard` or `brain`. ```ts title="lot_change" interface LotChange { lot_id: number | null; event_id?: string; // 8 hex characters; only for command results data: { id: number; device_id: string; name: string; lot_id: number | null; product_type_id: number; latitude: number | null; longitude: number | null; status: 'online' | 'offline'; battery_status: number | null; rssi: number | null; product_error_id?: number; // only when the product reports an error product_error?: ProductError; // only when the product reports an error deleted_at: null; created_at: string; updated_at: string; barrier?: BarrierBlock; gate?: BrainBlock; chain?: BrainBlock; bollard?: BrainBlock; brain?: BrainBlock; lot: { id: number; lot_cluster_id: number; // same as id account_id: number; name: string; address: string; latitude: number | null; longitude: number | null; timezone: string; // IANA name, e.g. "Europe/Zagreb" created_at: string; updated_at: string; } | null; }; } interface BarrierBlock { id: number; product_id: number; state: 'opened' | 'closed'; is_auto_close: 0 | 1; // REST calls this is_autoclose is_car_on_top: 0 | 1; auto_close_time: number; // seconds; REST calls this autoclose_time create_at: string; // sic: REST calls this created_at updated_at: string; } interface BrainBlock { id: number; product_id: number; state: 'opened' | 'closed' | 'error' | 'unknown' | 'reserved' | 'disabled'; permanent_position: 'disabled' | 'stay_open' | 'stay_close'; deleted_at: string | null; created_at: string; updated_at: string; } interface ProductError { id: number; error_code: string; jammed: 0 | 1; sensor_dirty: 0 | 1; pin_broken: 0 | 1; rtcc_invalid: 0 | 1; battery_empty: 0 | 1; master_key_not_set: 0 | 1; created_at: string; updated_at: string; } ``` For a barrier, `opened` means the arm is **down** (cars can pass over it) and `closed` means the arm is **up**. See [Products and operations](/concepts/products-and-operations). :::warning The barrier block's keys differ from the REST API In `lot_change`, the barrier block uses `is_auto_close`, `auto_close_time` and `create_at`. The REST API's product responses use `is_autoclose`, `autoclose_time` and `created_at`. Read the realtime keys as they are shown here. ::: ```json title="lot_change: a command result for a barrier" { "lot_id": 7, "event_id": "a1b2c3d4", "data": { "id": 123, "device_id": "B1234567", "name": "Entrance barrier", "lot_id": 7, "product_type_id": 2, "latitude": 45.815, "longitude": 15.982, "status": "online", "battery_status": 80, "rssi": -70, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-03-02T08:12:44.000Z", "barrier": { "id": 41, "product_id": 123, "state": "opened", "is_auto_close": 1, "is_car_on_top": 0, "auto_close_time": 45, "create_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-03-02T08:12:44.000Z" }, "lot": { "id": 7, "lot_cluster_id": 7, "account_id": 3, "name": "Main garage", "address": "Example street 1", "latitude": 45.815, "longitude": 15.982, "timezone": "Europe/Zagreb", "created_at": "2026-01-10T10:00:00.000Z", "updated_at": "2026-01-10T10:00:00.000Z" } } } ``` #### `gateway_error` A command failed. `event_id` matches the one [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state) or [`POST /v2/products/{id}/settings`](/api/endpoints/products-change-settings) returned. See [Asynchronous operations](/concepts/asynchronous-operations). ```ts title="gateway_error" interface GatewayError { type: number | 'Timeout'; // see the table below event_id: string; // 8 hex characters data: { id: number; // product id name: string; // product name }; message?: string; // absent when type is "Timeout" } ``` ```json title="gateway_error" { "type": 6, "event_id": "a1b2c3d4", "data": { "id": 123, "name": "Entrance barrier" }, "message": "Car on top" } ``` When no Gateway got an answer from the product in time, `type` is the **string** `"Timeout"` and there's no `message`. Otherwise `type` is one of these numbers. They are **not** the HTTP [error codes](/api/errors), even where the numbers overlap. | `type` | `message` | | --- | --- | | `0` | Auth error | | `1` | Invalid guest key | | `2` | Device disconnected | | `3` | Bluetooth error | | `4` | Device not found | | `5` | Device jammed | | `6` | Car on top | | `7` | Sensor dirty | | `8` | Device not initialized | | `9` | Rtcc not set | | `10` | Device busy | | `11` | Unknown error | #### `gateway_status` A Gateway connected to or disconnected from Parklio. Only Gateways assigned to a lot send it. The payload identifies the Gateway by its `uuid`, not its numeric id. ```ts title="gateway_status" interface GatewayStatus { uuid: string; status: 'online' | 'offline'; } ``` ```json title="gateway_status" { "uuid": "xgobgwEG7rmZ", "status": "online" } ``` ### License plates and sessions #### `plate_reading_create` An LPR camera read a license plate. When it's sent, the access decision hasn't been made yet: `access_decision`, `access_decision_reason` and `session_id` are `null`. Plate readings created by [`POST /v2/sessions`](/api/endpoints/sessions-create) don't send it. ```ts title="plate_reading_create and plate_reading_update" interface PlateReading { id: number; plate: string; confidence: number | null; image_url: string | null; // temporary link to the full image cropped_image_url: string | null; // temporary link to the plate crop direction: 'in' | 'out' | 'undefined'; type: 'bus' | 'car' | 'motorcycle' | 'truck' | 'van' | null; make: string | null; color: string | null; model: string | null; country_code: string | null; session_id: number | null; access_decision: 'denied' | 'triggered' | null; access_decision_reason: | 'debounce' | 'direction_mismatch' | 'max_capacity' | 'no_active_session' | 'no_tariff' | 'session_exit' | 'tariff' | 'unknown_direction' | 'whitelist' | null; lpr_camera_id: number; zone_id: number; plate_side: 'front' | 'rear'; lpr_camera_direction: 'in' | 'out' | 'in/out'; // the camera's position at read time created_at: string; updated_at: string; } ``` ```json title="plate_reading_create" { "id": 9001, "plate": "ZG1234AB", "confidence": 0.92, "image_url": null, "cropped_image_url": null, "direction": "in", "type": "car", "make": null, "color": null, "model": null, "country_code": "HR", "session_id": null, "access_decision": null, "access_decision_reason": null, "lpr_camera_id": 15, "zone_id": 9, "plate_side": "front", "lpr_camera_direction": "in", "created_at": "2026-03-02T08:12:40.000Z", "updated_at": "2026-03-02T08:12:40.000Z" } ``` `direction` is the string `'undefined'` when the camera couldn't tell; such readings get no access decision. #### `plate_reading_update` Same shape as `plate_reading_create`. Sent when: - an access decision is recorded: `triggered` (reason `whitelist`, `tariff` or `session_exit`) or `denied` (any other reason, including `debounce` for a repeat read of the same plate); - the camera refines a reading after the fact (new plate, confidence or direction). `session_id` is usually still `null` here: the decision is recorded before the reading is linked to a session. To follow the session, listen for [`session_create`](#session_create) and [`session_update`](#session_update). Editing a reading through [`PATCH /v2/plate-readings/{id}`](/api/endpoints/plate-readings-update) doesn't send this event. #### `session_create` A parking session started: either you or a user created it through [`POST /v2/sessions`](/api/endpoints/sessions-create), or an LPR camera let a vehicle in (by whitelist or tariff). ```ts title="session_create and session_update" interface Session { id: number; public_id: string; // 10 characters, A-Z and 0-9 ended_at?: string | null; // left out while the session is new duration: number; // seconds discount_time?: number | null; // minutes; -1 means unlimited amount_paid: number; possibly_exited?: boolean | null; exit_allowed: boolean; // true while the session hasn't ended entry_license_plate: { id: number; plate: string; image_url: string | null; cropped_image_url: string | null; } | null; created_at: string; entry: { id: number; type: SessionSourceType }; exit?: { id: number; type: SessionSourceType } | null; // left out or null while open zone_id: number; } // entry.id / exit.id is the id of that source: the camera, the account or the user type SessionSourceType = 'lpr_camera' | 'account' | 'attendant' | 'terminal' | 'product' | 'payment'; ``` `entry.type` is `account` when an API token created the session, `attendant` when a user did, and `lpr_camera` when a camera let the vehicle in. ```json title="session_create" { "id": 501, "public_id": "K7Q2M9X4TB", "duration": 0, "amount_paid": 0, "exit_allowed": true, "entry_license_plate": { "id": 9001, "plate": "ZG1234AB", "image_url": null, "cropped_image_url": null }, "created_at": "2026-03-02T08:12:41.000Z", "entry": { "id": 15, "type": "lpr_camera" }, "zone_id": 9 } ``` #### `session_update` Same shape as `session_create`. Sent when: - a session is changed through [`PATCH /v2/sessions/{id}`](/api/endpoints/sessions-update) (discount time or entry plate); - a session is closed through [`POST /v2/sessions/{id}/close`](/api/endpoints/sessions-close): `ended_at` and `exit` are set, with `exit.type` `account` or `attendant`; - an LPR camera matches a vehicle leaving to an open session: `ended_at` is set, `possibly_exited` is `true` and `exit.type` is `lpr_camera`. #### `lpr_camera_status` An LPR camera reported its status. It's sent for every camera in each report, even when the status didn't change, so keep the last value per camera. Not every camera model sends it. ```ts title="lpr_camera_status" interface LprCameraStatus { id: number; device_id: string; status: 'online' | 'offline'; } ``` ```json title="lpr_camera_status" { "id": 15, "device_id": "L123456789", "status": "online" } ``` ### Occupancy A parking-detection sensor that changes state sends three events, in this order: `lot_detection_change`, `zone_change`, then `parking_place_change`. Occupied counts also change when LPR sessions start and end, and those changes **don't** send any event; re-read the lot or zone over REST if you need exact counts. #### `lot_detection_change` ```ts title="lot_detection_change" interface LotDetectionChange { lots: [ { id: number; // lot id detection_status: 'online'; occupied_count: number; }, ]; // always one element } ``` ```json title="lot_detection_change" { "lots": [{ "id": 7, "detection_status": "online", "occupied_count": 42 }] } ``` #### `zone_change` ```ts title="zone_change" interface ZoneChange { zones: [ { id: number; account_id: number; lot_id: number; name: string; description: string | null; spot_count: number; occupied_count: number; deleted_at: string | null; created_at: string; updated_at: string; }, ]; // always one element } ``` ```json title="zone_change" { "zones": [ { "id": 9, "account_id": 3, "lot_id": 7, "name": "Level -1", "description": null, "spot_count": 120, "occupied_count": 42, "deleted_at": null, "created_at": "2026-01-10T10:00:00.000Z", "updated_at": "2026-03-02T08:12:40.000Z" } ] } ``` #### `parking_place_change` ```ts title="parking_place_change" interface ParkingPlaceChange { places: [ { id: number; lot_id: number; zone_id: number; name: string | null; occupancy_status: 'free' | 'occupied'; }, ]; // always one element } ``` ```json title="parking_place_change" { "places": [ { "id": 301, "lot_id": 7, "zone_id": 9, "name": "Spot 12", "occupancy_status": "occupied" } ] } ``` #### `parking_gateway_state_change` A parking-detection gateway (not a product Gateway) went online or offline. Not every parking-detection gateway model sends it. ```ts title="parking_gateway_state_change" interface ParkingGatewayStateChange { gateways: [{ id: number; status: 'online' | 'offline' }]; // always one element } ``` ```json title="parking_gateway_state_change" { "gateways": [{ "id": 4, "status": "offline" }] } ``` ### Legacy #### `lpr_event` :::warning Legacy Use [`plate_reading_create`](#plate_reading_create) and [`plate_reading_update`](#plate_reading_update) instead. `lpr_event` is still sent, but lot entries are deprecated. ::: Sent when a plate reading with a known direction, not debounced, is recorded as a lot entry. An `out` entry that matches an earlier `in` has `related_lot_entry` and `duration` set. ```ts title="lpr_event" interface LprEvent { lot_id: number; data: LotEntry & { lpr_camera: { id: number; device_id: string; device_serial: string; name: string; account_id: number; zone_id: number; status: 'online' | 'offline' | 'auth' | 'sleep' | 'reset'; lpr_position: 'in' | 'out' | 'in/out'; connected_device_type: 'gate' | 'chain' | 'bollard' | 'other' | 'standalone'; mac: string; deleted_at: string | null; created_at: string | null; updated_at: string | null; }; related_lot_entry: | (LotEntry & { lpr_camera: { id: number; device_id: string; name: string } }) | null; }; } interface LotEntry { id: number; scan_time: string; lpr_camera_id: number; lot_id: number; related_lot_entry_id: number | null; duration: number | null; // seconds; out entries only is_allowed: 0 | 1; direction: 'in' | 'out'; license_plate: string; license_plate_image_url: string | null; // a storage key, not a usable URL full_image_url: string | null; // a storage key, not a usable URL make: string; // "" when unknown color: string; // "" when unknown model: string; // "" when unknown color_depth: number | null; created_at: string; updated_at: string; } ``` ```json title="lpr_event" { "lot_id": 7, "data": { "id": 320, "scan_time": "2026-03-02T08:12:40.000Z", "lpr_camera_id": 15, "lot_id": 7, "related_lot_entry_id": null, "duration": null, "is_allowed": 1, "direction": "in", "license_plate": "ZG1234AB", "license_plate_image_url": null, "full_image_url": null, "make": "", "color": "", "model": "", "color_depth": 1, "created_at": "2026-03-02T08:12:40.000Z", "updated_at": "2026-03-02T08:12:40.000Z", "lpr_camera": { "id": 15, "device_id": "L123456789", "device_serial": "123456789", "name": "Entrance camera", "account_id": 3, "zone_id": 9, "status": "online", "lpr_position": "in", "connected_device_type": "standalone", "mac": "AA:BB:CC:DD:EE:FF", "deleted_at": null, "created_at": "2026-01-10T10:00:00.000Z", "updated_at": "2026-01-10T10:00:00.000Z" }, "related_lot_entry": null } } ``` ### Next - [Patterns](/realtime/patterns): correlating results, deduplicating, resyncing. - [Asynchronous operations](/concepts/asynchronous-operations) --- ## Patterns ## Realtime patterns Three things every realtime client needs: matching command results to the commands you sent, ignoring repeated status updates, and catching up after a disconnect. And one thing Parklio doesn't have: webhooks. ### Correlate results by `event_id` [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state) and [`POST /v2/products/{id}/settings`](/api/endpoints/products-change-settings) return an `event_id`. The result arrives later as a `lot_change` (success) or `gateway_error` (failure) with the same `event_id`. See [Asynchronous operations](/concepts/asynchronous-operations). The helper below handles the cases that trip clients up: - the result can arrive **before** the HTTP response, so unmatched results are kept for a while; - your connection also gets results for operations you didn't send, which are ignored; - a result may never come, so each wait has a timeout. **Node.js** ```ts title="pending-operations.ts" import type { Socket } from 'socket.io-client'; export interface OperationEvent { event_id?: string; type?: number | 'Timeout'; message?: string; data: Record; } export type OperationResult = | { ok: true; event: OperationEvent } | { ok: false; event: OperationEvent } | { ok: false; timedOut: true }; // Matches realtime results to the event_ids your commands returned. export class PendingOperations { #waiting = new Map void; timer: NodeJS.Timeout }>(); #early = new Map(); // results that arrived before we asked constructor(socket: Socket, { keepEarlyMs = 60_000 } = {}) { const settle = (ok: boolean) => (raw: string) => { const event: OperationEvent = JSON.parse(raw); if (!event.event_id) return; // a status update, not a command result const eventId = event.event_id; const result: OperationResult = ok ? { ok: true, event } : { ok: false, event }; const waiter = this.#waiting.get(eventId); if (waiter) { clearTimeout(waiter.timer); this.#waiting.delete(eventId); waiter.resolve(result); } else { this.#early.set(eventId, result); setTimeout(() => this.#early.delete(eventId), keepEarlyMs).unref(); } }; socket.on('lot_change', settle(true)); socket.on('gateway_error', settle(false)); } // Resolves with { ok, event }, or { ok: false, timedOut: true }. wait(eventId: string, timeoutMs: number): Promise { const early = this.#early.get(eventId); if (early) { this.#early.delete(eventId); return Promise.resolve(early); } return new Promise((resolve) => { const timer = setTimeout(() => { this.#waiting.delete(eventId); resolve({ ok: false, timedOut: true }); }, timeoutMs); this.#waiting.set(eventId, { resolve, timer }); }); } } ``` ```ts title="Usage" const pending = new PendingOperations(socket); // create it right after connecting const res = await fetch(`https://api.parklio.com/v2/products/${productId}/state`, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ operation: 'open' }), }); const { data } = await res.json(); const result = await pending.wait(data.event_id, 120_000); ``` **Python** ```python title="pending_operations.py" import json import threading import time class PendingOperations: """Matches realtime results to the event_ids your commands returned.""" def __init__(self, sio, namespace="/v2/realtime", keep_early_s=60): self._lock = threading.Lock() self._waiting = {} # event_id -> [threading.Event, result] self._early = {} # event_id -> (result, arrived_at) self._keep_early_s = keep_early_s sio.on("lot_change", self._settle(True), namespace=namespace) sio.on("gateway_error", self._settle(False), namespace=namespace) def _settle(self, ok): def handler(raw): event = json.loads(raw) event_id = event.get("event_id") if not event_id: return # a status update, not a command result result = {"ok": ok, "event": event} with self._lock: slot = self._waiting.get(event_id) if slot: slot[1] = result slot[0].set() else: now = time.monotonic() self._early = { k: v for k, v in self._early.items() if now - v[1] < self._keep_early_s } self._early[event_id] = (result, now) return handler def wait(self, event_id, timeout_s): """Returns {"ok", "event"}, or {"ok": False, "timed_out": True}.""" with self._lock: early = self._early.pop(event_id, None) if early: return early[0] slot = self._waiting[event_id] = [threading.Event(), None] slot[0].wait(timeout_s) with self._lock: self._waiting.pop(event_id, None) return slot[1] or {"ok": False, "timed_out": True} ``` ```python title="Usage" pending = PendingOperations(sio) # create it before connecting res = httpx.post( f"https://api.parklio.com/v2/products/{product_id}/state", headers={"Authorization": f"Bearer {token}"}, json={"operation": "open"}, ) result = pending.wait(res.json()["data"]["event_id"], timeout_s=120) ``` When a wait times out, the operation may still have run. Re-read the product with [`GET /v2/products/{id}`](/api/endpoints/products-find-one) before deciding what to do. ### Deduplicate status updates `lot_change` events **without** an `event_id` come from Gateways reporting what they see over Bluetooth. Two things to know: - **Each one is sent twice**, with identical payloads. - **They repeat even when nothing changed**: a Gateway's report re-sends every product it sees. So treat them as "current state of product X", not as "product X changed". Keep the last state per product, and act only when something you care about differs: ```ts title="Ignore repeats" const lastState = new Map(); // product id -> fingerprint socket.on('lot_change', (raw: string) => { const { event_id, data } = JSON.parse(raw); if (event_id) return; // command results are handled by PendingOperations const block = data.barrier ?? data.gate ?? data.chain ?? data.bollard ?? data.brain; const fingerprint = JSON.stringify([data.status, block?.state, data.product_error]); if (lastState.get(data.id) === fingerprint) return; lastState.set(data.id, fingerprint); onProductChanged(data); // your code }); ``` Pick the fields for the fingerprint from what you act on; `battery_status` and `rssi` change often and are rarely worth an alert. ### Resync after a reconnect There's no replay: anything sent while you were disconnected is gone. So every time you connect, re-read the state you track over REST, page by page. That covers the first connect too, where events sent in the first moment can be missed (see [Authentication](/realtime/connecting#authentication)). ```ts title="Resync products of a lot on every connect" socket.on('connect', () => resyncLot(lotId)); // fires on the first connect and after each reconnect async function resyncLot(lotId: number) { for (let page = 1, last = 1; page <= last; page++) { const url = `https://api.parklio.com/v2/products?filter=lot_id:eq:${lotId}&page=${page}&size=100`; const body = await (await fetch(url, { headers: { Authorization: `Bearer ${token}` } })).json(); for (const product of body.data) { lastState.delete(product.id); onProductChanged(product); // note: REST and realtime product shapes differ slightly } last = body.meta.last_page; } } ``` Also treat every command that was pending during the disconnect as unknown: re-read those products too. In Python, `python-socketio` calls your `connect` handler on the first connect and after each reconnect too; start the resync from there. ### No webhooks Parklio doesn't call your URLs. Realtime events are delivered only over this Socket.IO connection, to clients that are connected at the time. If other systems need the events, run one small service that holds the connection and forwards what you need (to a queue, a database or your own webhooks), and resyncs after reconnects as above. ### Next - [Events](/realtime/events): every event and its payload. - [Asynchronous operations](/concepts/asynchronous-operations) --- ## Examples Complete scripts for the common scenarios, in Node.js (TypeScript) and Python. Each one is type-checked or compiled on every docs build, and the code on the guide pages comes from the same files. ### Setup - An API token; see [Authentication](/api/authentication). The scripts read it from `PARKLIO_API_TOKEN`, and ids from `PRODUCT_ID` and `LOT_ID`. - **Node.js 24+**, which runs TypeScript files directly, and `socket.io-client` 4. - **Python 3.10+**, with `httpx` and `python-socketio` 5. ```bash title="Install and configure" npm install socket.io-client # Node.js pip install httpx "python-socketio[client]" # Python export PARKLIO_API_TOKEN="" export PRODUCT_ID=123 export LOT_ID=7 ``` Run a script with `node open.ts` or `python3 open.py`. ### Open a product and wait for the result Connect to realtime, send `open`, and wait for the `lot_change` or `gateway_error` with your `event_id`. Explained in the [Quickstart](/get-started/quickstart). **Node.js** ```ts title="open.ts" import { io } from 'socket.io-client'; const token = process.env.PARKLIO_API_TOKEN; const productId = process.env.PRODUCT_ID; interface OperationEvent { event_id?: string; type?: number | 'Timeout'; message?: string; data: Record; } type Result = { ok: boolean; event: OperationEvent }; // 1. Connect first. `connect` fires once the server has accepted the token. const socket = io('https://api.parklio.com/v2/realtime', { path: '/socketio', query: { token }, }); await new Promise((resolve, reject) => { socket.once('connect', () => resolve(undefined)); socket.once('connect_error', reject); }); // 2. Collect results by event_id. One can arrive before the HTTP response does. const results = new Map(); const waiters = new Map void>(); const settle = (ok: boolean) => (raw: string) => { const event: OperationEvent = JSON.parse(raw); // payloads are JSON strings if (!event.event_id) return; // a status update nobody requested const waiter = waiters.get(event.event_id); if (waiter) waiter({ ok, event }); else results.set(event.event_id, { ok, event }); }; socket.on('lot_change', settle(true)); socket.on('gateway_error', settle(false)); // 3. Send the command. const res = await fetch(`https://api.parklio.com/v2/products/${productId}/state`, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ operation: 'open' }), }); const body = await res.json(); if (!body.success) throw new Error(JSON.stringify(body.errors)); const eventId: string = body.data.event_id; console.log('Sent, event_id', eventId); // 4. Wait for the matching event. const { ok, event } = await new Promise((resolve) => { const early = results.get(eventId); if (early) resolve(early); else waiters.set(eventId, resolve); }); console.log(ok ? `Done: ${JSON.stringify(event.data.barrier ?? event.data)}` : `Failed: ${event.message ?? event.type}`); socket.close(); ``` **Python** ```python title="open.py" import json import os import threading import httpx import socketio TOKEN = os.environ["PARKLIO_API_TOKEN"] PRODUCT_ID = os.environ["PRODUCT_ID"] NS = "/v2/realtime" sio = socketio.Client() results = {} event_id = None done = threading.Event() lock = threading.Lock() def settle(ok): def handler(raw): global event_id event = json.loads(raw) # payloads are JSON strings if not event.get("event_id"): return # a status update nobody requested with lock: results[event["event_id"]] = (ok, event) if event["event_id"] == event_id: done.set() return handler sio.on("lot_change", settle(True), namespace=NS) sio.on("gateway_error", settle(False), namespace=NS) # 1. Connect first. connect() returns once the server has accepted the token. sio.connect( f"https://api.parklio.com?token={TOKEN}", namespaces=[NS], socketio_path="socketio", wait_timeout=10, ) # 2. Send the command. res = httpx.post( f"https://api.parklio.com/v2/products/{PRODUCT_ID}/state", headers={"Authorization": f"Bearer {TOKEN}"}, json={"operation": "open"}, ) body = res.json() if not body["success"]: raise SystemExit(body["errors"]) with lock: event_id = body["data"]["event_id"] if event_id in results: # it arrived before the HTTP response done.set() print("Sent, event_id", event_id) # 3. Wait for the matching event. done.wait(timeout=180) ok, event = results.get(event_id, (False, {"type": "no result"})) print("Done" if ok else f"Failed: {event.get('message', event.get('type'))}") sio.disconnect() ``` ### Monitor devices Alert on offline products, error flags and low batteries from status updates, and watch Gateways. Explained in [Monitor devices](/guides/monitor-devices). **Node.js** ```ts title="monitor-devices.ts" // The monitor-devices guide: status updates, offline products, Gateways. import { io } from 'socket.io-client'; const socket = io('https://api.parklio.com/v2/realtime', { path: '/socketio', query: { token: process.env.PARKLIO_API_TOKEN }, }); // alert() and resolve() stand for your alerting. const last = new Map(); socket.on('lot_change', (raw: string) => { const { event_id, data } = JSON.parse(raw); if (event_id) return; // command results are handled elsewhere const online = data.status === 'online'; const errorCode: string | null = data.product_error?.error_code ?? null; const low = isLowBattery(data.battery_status); const prev = last.get(data.id); last.set(data.id, { online, errorCode, low }); if (!online && prev?.online) alert(data, 'offline'); if (online && prev?.online === false) resolve(data, 'back online'); if (errorCode && errorCode !== prev?.errorCode) alert(data, `error flags ${errorCode}`, data.product_error); if (!errorCode && prev?.errorCode) resolve(data, 'error cleared'); if (low && !prev?.low) alert(data, `battery at ${data.battery_status}%`); }); function isLowBattery(value: unknown) { // Only barriers report a battery level. 0–100 is percent; above 100 means grid power. return typeof value === 'number' && value <= 20; } socket.on('gateway_status', (raw: string) => { const { uuid, status } = JSON.parse(raw); if (status === 'offline') alert({ uuid }, 'Gateway offline'); }); // Your alerting. function alert(subject: Record, what: string, details?: unknown) { console.warn('ALERT', subject.id ?? subject.uuid, what, details ?? ''); } function resolve(subject: Record, what: string) { console.info('RESOLVED', subject.id ?? subject.uuid, what); } ``` **Python** ```python title="monitor_devices.py" """The monitor-devices guide: status updates, offline products, Gateways.""" import json import os import socketio NS = "/v2/realtime" TOKEN = os.environ["PARKLIO_API_TOKEN"] sio = socketio.Client() def alert(subject, what, details=None): print("ALERT", subject.get("id", subject.get("uuid")), what, details or "") def resolve(subject, what): print("RESOLVED", subject.get("id", subject.get("uuid")), what) # alert() and resolve() stand for your alerting. last = {} # product id -> (online, error_code, low) def is_low_battery(value): # Only barriers report a battery level. 0–100 is percent; above 100 means grid power. return isinstance(value, (int, float)) and value <= 20 @sio.on("lot_change", namespace=NS) def on_lot_change(raw): event = json.loads(raw) if event.get("event_id"): return # command results are handled elsewhere data = event["data"] online = data["status"] == "online" error_code = (data.get("product_error") or {}).get("error_code") low = is_low_battery(data.get("battery_status")) prev_online, prev_code, prev_low = last.get(data["id"], (None, None, False)) last[data["id"]] = (online, error_code, low) if not online and prev_online: alert(data, "offline") if online and prev_online is False: resolve(data, "back online") if error_code and error_code != prev_code: alert(data, f"error flags {error_code}", data["product_error"]) if not error_code and prev_code: resolve(data, "error cleared") if low and not prev_low: alert(data, f"battery at {data['battery_status']}%") @sio.on("gateway_status", namespace=NS) def on_gateway_status(raw): event = json.loads(raw) if event["status"] == "offline": alert({"uuid": event["uuid"]}, "Gateway offline") if __name__ == "__main__": sio.connect(f"https://api.parklio.com?token={TOKEN}", namespaces=[NS], socketio_path="socketio") sio.wait() ``` ### Create an operation token for your app Mint a short-lived token for one product, to hand to your app. Explained in [Operation tokens for your app](/guides/mobile-app-operation-tokens). **Node.js** ```ts title="operation-token.ts" // The operation-tokens guide: mint a short-lived token for your app. export async function createOperationToken(productId: number, minutes: number) { const start = new Date(); const end = new Date(start.getTime() + minutes * 60_000); const res = await fetch('https://api.parklio.com/v2/operation-tokens', { method: 'POST', headers: { Authorization: `Bearer ${process.env.PARKLIO_API_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ product_id: productId, start_time: start.toISOString(), end_time: end.toISOString(), }), }); const body = await res.json(); if (!body.success) throw new Error(`${body.errors[0].code}: ${body.errors[0].message}`); return body.data; } if (import.meta.main) { const token = await createOperationToken(Number(process.env.PRODUCT_ID), 15); // Hand these to your app, unchanged. const { token_open, token_close, token_status, end_time, product } = token; console.log({ device_id: product.device_id, end_time, token_open, token_close, token_status }); } ``` **Python** ```python title="create_token.py" import os from datetime import datetime, timedelta, timezone import httpx def create_operation_token(product_id, minutes): start = datetime.now(timezone.utc) end = start + timedelta(minutes=minutes) body = httpx.post( "https://api.parklio.com/v2/operation-tokens", headers={"Authorization": f"Bearer {os.environ['PARKLIO_API_TOKEN']}"}, json={ "product_id": product_id, "start_time": start.isoformat(), "end_time": end.isoformat(), }, ).json() if not body["success"]: error = body["errors"][0] raise RuntimeError(f"{error['code']}: {error['message']}") return body["data"] ``` ### Whitelist plates and bind them to a product Add plates to a lot's whitelist, bind them to a product, and check the binding. Explained in [Plate-based access](/guides/plate-based-access). **Node.js** ```ts title="plate-access.ts" // The plate-based-access guide: whitelist plates on a lot, bind them to a product. const API = 'https://api.parklio.com/v2'; const headers = { Authorization: `Bearer ${process.env.PARKLIO_API_TOKEN}`, 'Content-Type': 'application/json', }; async function call(method: string, path: string, body?: unknown) { const init = body === undefined ? { method, headers } : { method, headers, body: JSON.stringify(body) }; const res = await fetch(`${API}${path}`, init); const json = await res.json(); if (!json.success) throw new Error(`${path}: ${JSON.stringify(json.errors)}`); return json; } export async function whitelistAndBind(lotId: number, productId: number, plates: string[], until: Date) { // 1. Whitelist the plates on the lot. One result per plate. const { data: results } = await call('POST', '/lot-whitelists', { lot_id: lotId, name: 'Tenants', license_plates: plates, start_time: new Date().toISOString(), end_time: until.toISOString(), }); const failed = results.filter((r: { success: boolean }) => !r.success); if (failed.length) console.warn('Not whitelisted:', failed); const licenseIds = results.filter((r: { success: boolean }) => r.success).map((r: { license_id: number }) => r.license_id); // 2. Bind them to the product. Unknown ids are ignored silently... await call('POST', `/products/${productId}/bind-licenses`, { license_ids: licenseIds }); // 3. ...so check what's bound. const { data: bound } = await call('GET', `/products/${productId}/bound-licenses?size=300`); const boundIds = new Set(bound.map((b: { id: number }) => b.id)); return licenseIds.filter((id: number) => !boundIds.has(id)); // ids that didn't bind } if (import.meta.main) { const until = new Date(Date.now() + 30 * 24 * 3600_000); const missing = await whitelistAndBind(Number(process.env.LOT_ID), Number(process.env.PRODUCT_ID), ['ZG1234AB'], until); console.log(missing.length ? `Not bound: ${missing}` : 'Whitelisted and bound'); } ``` **Python** ```python title="plate_access.py" """The plate-based-access guide: whitelist plates on a lot, bind them to a product.""" import os from datetime import datetime, timedelta, timezone import httpx API = "https://api.parklio.com/v2" HEADERS = {"Authorization": f"Bearer {os.environ['PARKLIO_API_TOKEN']}"} def call(method, path, body=None): res = httpx.request(method, f"{API}{path}", headers=HEADERS, json=body) data = res.json() if not data["success"]: raise RuntimeError(f"{path}: {data['errors']}") return data def whitelist_and_bind(lot_id, product_id, plates, until): # 1. Whitelist the plates on the lot. One result per plate. results = call("POST", "/lot-whitelists", { "lot_id": lot_id, "name": "Tenants", "license_plates": plates, "start_time": datetime.now(timezone.utc).isoformat(), "end_time": until.isoformat(), })["data"] failed = [r for r in results if not r["success"]] if failed: print("Not whitelisted:", failed) license_ids = [r["license_id"] for r in results if r["success"]] # 2. Bind them to the product. Unknown ids are ignored silently... call("POST", f"/products/{product_id}/bind-licenses", {"license_ids": license_ids}) # 3. ...so check what's bound. bound = call("GET", f"/products/{product_id}/bound-licenses?size=300")["data"] bound_ids = {b["id"] for b in bound} return [i for i in license_ids if i not in bound_ids] # ids that didn't bind if __name__ == "__main__": until = datetime.now(timezone.utc) + timedelta(days=30) missing = whitelist_and_bind( int(os.environ["LOT_ID"]), int(os.environ["PRODUCT_ID"]), ["ZG1234AB"], until ) print(f"Not bound: {missing}" if missing else "Whitelisted and bound") ``` ### Live occupancy Keep free-space counts per zone up to date from `zone_change` events, and re-read the zones every minute for changes that send no event. Explained in [Live occupancy](/guides/live-occupancy). **Node.js** ```ts title="live-occupancy.ts" // The live-occupancy guide: read zone counts, keep them live, re-read regularly. import { io } from 'socket.io-client'; const LOT_ID = Number(process.env.LOT_ID); const headers = { Authorization: `Bearer ${process.env.PARKLIO_API_TOKEN}` }; const socket = io('https://api.parklio.com/v2/realtime', { path: '/socketio', query: { token: process.env.PARKLIO_API_TOKEN }, }); const zones = new Map(); socket.on('zone_change', (raw: string) => { for (const zone of JSON.parse(raw).zones) { zones.set(zone.id, { spot_count: zone.spot_count, occupied_count: zone.occupied_count }); render(zone.id); // your UI } }); function render(zoneId: number) { const zone = zones.get(zoneId); if (zone) console.log(`zone ${zoneId}: ${zone.spot_count - zone.occupied_count} free`); } // LPR, payment-machine and recount changes send no event: re-read every minute. async function readZones(lotId: number) { const url = `https://api.parklio.com/v2/zones?filter=lot_id:eq:${lotId}&size=300`; const body = await (await fetch(url, { headers })).json(); for (const zone of body.data) { zones.set(zone.id, { spot_count: zone.spot_count, occupied_count: zone.occupied_count }); render(zone.id); } } socket.on('connect', () => readZones(LOT_ID).catch(console.error)); setInterval(() => readZones(LOT_ID).catch(console.error), 60_000); ``` **Python** ```python title="live_occupancy.py" """The live-occupancy guide: read zone counts, keep them live, re-read regularly.""" import json import os import threading import httpx import socketio NS = "/v2/realtime" TOKEN = os.environ["PARKLIO_API_TOKEN"] LOT_ID = int(os.environ["LOT_ID"]) zones = {} # zone id -> {"spot_count", "occupied_count"} lock = threading.Lock() sio = socketio.Client() def render(zone_id): zone = zones[zone_id] print(f"zone {zone_id}: {zone['spot_count'] - zone['occupied_count']} free") def remember(zone): with lock: zones[zone["id"]] = { "spot_count": zone["spot_count"], "occupied_count": zone["occupied_count"], } render(zone["id"]) @sio.on("zone_change", namespace=NS) def on_zone_change(raw): for zone in json.loads(raw)["zones"]: remember(zone) def read_zones(lot_id): # LPR, payment-machine and recount changes send no event: re-read regularly. res = httpx.get( f"https://api.parklio.com/v2/zones?filter=lot_id:eq:{lot_id}&size=300", headers={"Authorization": f"Bearer {TOKEN}"}, ) for zone in res.json()["data"]: remember(zone) @sio.event(namespace=NS) def connect(): read_zones(LOT_ID) # on the first connect and after every reconnect if __name__ == "__main__": sio.connect(f"https://api.parklio.com?token={TOKEN}", namespaces=[NS], socketio_path="socketio") while True: sio.sleep(60) read_zones(LOT_ID) ``` ### Next - [Patterns](/realtime/patterns): the `PendingOperations` helper, deduplication and resync. - [Troubleshooting](/resources/troubleshooting) --- ## Support | You need | Go to | | --- | --- | | Help with a problem, an error you can't resolve, or a device that won't come online | [Open a support ticket](https://tickets.parklio.com) | | Product guides, manuals and FAQs | [Parklio Knowledge Base](https://support.parklio.com/portal/en/home) | | An API account, API access, SDK access or a hardware quote | [info@parklio.com](mailto:info@parklio.com) | Before opening a ticket, check [Troubleshooting](/resources/troubleshooting). When you do, include: - your account id; - the product and Gateway ids involved; - the endpoint you called, the response's error `code`, and the `event_id` if there was one; - roughly when it happened, with the time zone. Never include your API token in a ticket. --- ## Troubleshooting ### Product status is never `online` The [product endpoint](/api/endpoints/products-find-one) keeps returning a product with `status !== "online"`, so state-change calls have nothing to act on. - Confirm the product's Gateway is added to the **same lot** as the product (see [Device Registration](/get-started/what-you-need)). - Confirm the Gateway itself is online — check for a [`gateway_status`](/realtime/events#gateway_status) event, or its LED state per the [Gateway manual](https://parklio.com/assets/support/gateway/Parklio-Gateway-UM.pdf). - Try operating the product manually from the Parklio Connect App — if it doesn't respond there either, this is a hardware/Bluetooth range issue, not an API issue. ### Gateway won't pair - Gateways only accept pairing for a short window after a short press of the power button (LEDs blinking). If the window has passed, press again and retry immediately in the Connect App. - Make sure the gateway has network connectivity (Ethernet link or GSM signal) before pairing — a gateway with no uplink can pair locally but won't be able to relay commands to/from the API. ### The state-change call returns an error [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state) checks a few things before it sends anything to the device. These errors come back straight away: | Code | HTTP status | What it means | What to do | | --- | --- | --- | --- | | `214` | 403 | An earlier operation on this device hasn't finished. | Wait for the earlier operation's `lot_change` or `gateway_error`, then send the next one. Don't retry in a loop. | | `704` | 403 | No Gateway has seen the device recently, so there's no way to reach it. | Check that a Gateway on the same lot is online and in range of the product (see [above](#product-status-is-never-online)). | | `30` | 403 | Your subscription doesn't include remote control for this product. | Check the product's API subscription; see [Account Setup](/get-started/what-you-need). | ### A state-change call never resolves You called [`POST /v2/products/{id}/state`](/api/endpoints/products-change-state), got back an `event_id`, but never see a matching [`lot_change`](/realtime/events#lot_change) event on your WebSocket connection. How delivery works: the API tries the Gateways that recently saw the device **one at a time, strongest signal first, waiting up to 30 seconds for each**. So a result can take 30 seconds or more per Gateway on the lot. When every Gateway fails, or the device reports an error, you get a [`gateway_error`](/realtime/events#gateway_error) with your `event_id`; a `type` of `"Timeout"` means no Gateway answered in time. - Make sure you're connected to the WebSocket **before** issuing the state-change call, using the same account/token; see [Connecting](/realtime/connecting). - Check for a [`gateway_error`](/realtime/events#gateway_error) event instead. It reports the same `event_id` when the operation fails (for example, a barrier jamming while closing) rather than succeeding. - If neither event ever arrives, the Gateway may have lost connectivity mid-operation; check gateway status as above. ### Still stuck? [Contact Support](https://tickets.parklio.com/) with your account ID, the product/gateway IDs involved, and roughly when the issue occurred. --- ## SDK ## Parklio SDK The Parklio SDK lets **your own mobile app** operate Parklio products directly over Bluetooth, without a Gateway and without a network connection at the product. It's the right path for offline or low-connectivity sites, and for apps that put access in your users' hands. :::info Access The SDK is private and shared under contract. To request access (Android and iOS), contact [info@parklio.com](mailto:info@parklio.com). This page explains the flow; the SDK's own documentation comes with access. ::: ### How it works ![Parklio SDK integration](/img/diagrams/sdk.svg) 1. **Pairing.** The product is paired with your account through the Parklio Connect app, once. From then on, the product and Parklio share a secret that every credential is derived from. 2. **Your backend creates an operation token** for one product and a time window, with [`POST /v2/operation-tokens`](/api/endpoints/operation-tokens-create), using your API token. The response holds three signed commands: `token_open`, `token_close` and `token_status`. 3. **Your backend gives the token to your app**, for example when a user's booking starts. Never put your API token in the app. 4. **Your app sends the command to the product over Bluetooth**, through the SDK. The product checks the signature and the time window itself, so no network is needed at that point. 5. **The product's state** (open or closed, battery, errors) is broadcast over Bluetooth; the SDK reads it, and `token_status` asks the product for a fresh status. An operation token can't be changed once it's issued. To extend access, create a new token. ### Rules for your backend - **Create tokens just in time, with short windows.** Blocking a token (`PATCH /v2/operation-tokens/{id}` with `{"status": "blocked"}`) only marks it on the server; a copy already in the app keeps working until `end_time`. - **Don't store tokens long-term**; create a new one when you need it. - **Give either an `end_time` or `"is_unlimited": true`.** Unlimited tokens are valid until 2038-01-01, and can't be revoked on the device: avoid them. - **Check the product's clock.** If `product_error.rtcc_invalid` is `1`, the product's clock isn't set and time-limited tokens fail. Operation tokens don't need a Gateway. The backend side is covered step by step in [Operation tokens for your app](/guides/mobile-app-operation-tokens). See [Access and keys](/concepts/access-and-keys) for how operation tokens compare with the other ways to share access. # Log in `POST https://api.parklio.com/v2/auth/login` Logs an account user in with email and password, and returns a **user token**. Use it **once**, by hand, as the account's Super Admin, to [create an API token](/api/endpoints/api-token-create). Don't log in at runtime with a person's credentials: logging in deletes that user's other tokens of the same type, which signs them out of the Connect app. An unconfirmed email returns `403` with code `9`. See [Authentication](/api/authentication). **Auth:** none (public) ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `email` | string | yes | | | `password` | string | yes | | ```json { "email": "john.doe@mail.com", "password": "somesecretpassword" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 501, "type": "bearer", "token": "", "name": "Site Admin", "email": "admin@example.com", "account_id": 3, "account_role_id": 2, "account_user_role_id": 1, "account_user_id": 17 } } ``` ### 403 — User has entered a wrong password. Due to security reason we should not specify this and instead say that the email/password combination is wrong. ```json { "success": false, "errors": [ { "code": 8, "message": "Wrong email/password combination." } ] } ``` --- # Create an API token `POST https://api.parklio.com/v2/api-tokens` Creates an API token for the account. Call it with a **Super Admin's user token** (from [Log in](/api/endpoints/auth-login)); API tokens can't create API tokens. The `token` is returned **only in this response**: store it in your secret manager. Tokens don't expire; revoke one with [Delete an API token](/api/endpoints/api-token-remove). An API token acts as the account's first Super Admin, and sees the lots that user is assigned to. See [Authentication](/api/authentication). **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | | ```json { "name": "dev laptop" } ``` ## Responses ### 200 — The new API token. `token` is only ever returned here; store it now. ```json { "success": true, "data": { "id": 12, "name": "booking-backend", "account_id": 3, "token": "", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # List API tokens `GET https://api.parklio.com/v2/api-tokens` Lists the account's API tokens: names and ids only, never the token values. Only a Super Admin can call it. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`, `created_at`, `updated_at`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "data": [ { "id": 12, "name": "booking-backend", "account_id": 3, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ], "meta": { "total": 1, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=1", "last_page_url": "/?page=1", "next_page_url": "/?page=2", "previous_page_url": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get api-tokens `GET https://api.parklio.com/v2/api-tokens/{id}` This endpoint allows the user to get api-tokens by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "dev-laptop", "account_id": 3, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Delete an API token `DELETE https://api.parklio.com/v2/api-tokens/{id}` Revokes an API token. Requests made with it fail from then on. Only a Super Admin can call it. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The API token's `id` (not the token value). | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Accept auto extend `POST https://api.parklio.com/v2/accounts/auto-extend` Accept auto extend barrier option **Auth:** `Authorization: Bearer ` ## Responses ### 200 — Auto extended accepted. ```json { "success": true, "message": "Auto extended accepted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get profile `GET https://api.parklio.com/v2/accounts/profile` Get self account profile data **Auth:** `Authorization: Bearer ` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "account_role_id": 2, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 555 5555", "auto_extend": "2026-01-15T09:30:00.000Z", "billing_title": "monthly", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "infinite_shares": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account_role": { "id": 2, "name": "PMS" }, "authorities": [ "string" ], "organisation_id": "string", "additional_custom_fee": 0, "my_pos_client_id": "string", "my_pos_secret": "string" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update profile `PATCH https://api.parklio.com/v2/accounts/profile` Update self account profile data **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `account_role_id` | number | no | | | `name` | string | no | | | `contact_name` | string | no | | | `contact_email` | string | no | | | `contact_phone` | string | no | | | `billing_title` | string | no | | | `vat` | string | no | | | `address` | string | no | | | `city` | string | no | | | `zip_code` | string | no | | | `organisation_id` | string | no | | | `additional_custom_fee` | number | no | | | `my_pos_client_id` | string | no | | | `my_pos_secret` | string | no | | ```json { "account_role_id": 2, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 555 5555", "billing_title": "monthly", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "organisation_id": "2481289123", "additional_custom_fee": "2.55", "my_pos_client_id": "NDQ5XjTbF3G5Yk9qwu7Yzg", "my_pos_secret": "ryIOXX5zVbJ2cmzxx6s6Sw" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "account_role_id": 2, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 555 5555", "auto_extend": "2026-01-15T09:30:00.000Z", "billing_title": "monthly", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "infinite_shares": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account_role": { "id": 2, "name": "PMS" }, "authorities": [ "string" ], "organisation_id": "string", "additional_custom_fee": 0, "my_pos_client_id": "string", "my_pos_secret": "string" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get product count `GET https://api.parklio.com/v2/accounts/product-counts` Get produt count for authorized account **Auth:** `Authorization: Bearer ` ## Responses ### 200 ```json { "success": true, "data": { "products": 0, "barriers": 0, "gates": 0, "bollards": 0, "chains": 0, "brains": 0, "lprs": 0, "parking_places": 0 } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get product licenses for account `GET https://api.parklio.com/v2/accounts/product-licenses` Get product licenses for authorized account **Auth:** `Authorization: Bearer ` ## Responses ### 200 ```json { "success": true, "data": { "account_id": 0, "barriers": 0, "bollards": 0, "brains": 0, "chains": 0, "gates": 0, "lpr_cameras": 0, "parking_places": 0 } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` --- # Get self account user `GET https://api.parklio.com/v2/account-users/profile` Get self account user data **Auth:** `Authorization: Bearer ` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "account_user_role_id": 1, "account_id": 0, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "language_tag": "fr-BE", "country_code": "BE", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account": { "id": 1, "account_role_id": 1, "auto_extend": {}, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 666 6666", "billing_title": "string", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "created_at": {}, "updated_at": {}, "infinite_shares": 0, "account_role": { "id": 2, "name": "PMS", "created_at": {}, "updated_at": {} }, "authorities": [] }, "account_user_role": { "id": 1, "name": "Super Admin", "created_at": {}, "updated_at": {} }, "lots": { "id": 1, "name": "Default Lot", "address": "Put Stinica 12" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Update account users profile `PATCH https://api.parklio.com/v2/account-users/profile` This endpoint allows the user to update account users profile by id **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `password` | string | no | | | `old_password` | string | no | | | `phone` | string | no | | ```json { "name": "John Doe", "password": "string", "old_password": "string", "phone": "+3855555555" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "account_user_role_id": 1, "account_id": 0, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "language_tag": "fr-BE", "country_code": "BE", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account": { "id": 1, "account_role_id": 1, "auto_extend": {}, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 666 6666", "billing_title": "string", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "created_at": {}, "updated_at": {}, "infinite_shares": 0, "account_role": { "id": 2, "name": "PMS", "created_at": {}, "updated_at": {} }, "authorities": [] }, "account_user_role": { "id": 1, "name": "Super Admin", "created_at": {}, "updated_at": {} }, "lots": { "id": 1, "name": "Default Lot", "address": "Put Stinica 12" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Invite Account user `POST https://api.parklio.com/v2/account-users` Invite an account user to Parklio™ system under specific account user role **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `account_user_role_id` | number | yes | | | `email` | string | yes | | | `name` | string | yes | | | `phone` | string | no | | | `target_lots` | string[] | no | An optional array of lot IDs to assign to the invited user. Each ID must correspond to a lot that belongs to the same account as the inviter. If any ID does not belong to the account, the request will be rejected. | ```json { "account_user_role_id": 1, "email": "john.doe@example.com", "name": "John Doe", "phone": "12345678", "target_lots": "[1]" } ``` ## Responses ### 200 — Account user successfully invited. ```json { "success": true, "message": "Account user successfully invited." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all account users `GET https://api.parklio.com/v2/account-users` This endpoint allows the user to list all account users **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `name`, `email`, `account_role_id`, `account_name`, `lots`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`, `email`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "account_user_role_id": 1, "account_id": 0, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "language_tag": "fr-BE", "country_code": "BE", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account": { "id": 1, "account_role_id": 1, "auto_extend": {}, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 666 6666", "billing_title": "string", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "created_at": {}, "updated_at": {}, "infinite_shares": 0, "account_role": { "id": 2, "name": "PMS", "created_at": {}, "updated_at": {} }, "authorities": [] }, "account_user_role": { "id": 1, "name": "Super Admin", "created_at": {}, "updated_at": {} }, "lots": { "id": 1, "name": "Default Lot", "address": "Put Stinica 12" } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get account users `GET https://api.parklio.com/v2/account-users/{id}` This endpoint allows the user to get account users by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "account_user_role_id": 1, "account_id": 0, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "language_tag": "fr-BE", "country_code": "BE", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account": { "id": 1, "account_role_id": 1, "auto_extend": {}, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 666 6666", "billing_title": "string", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "created_at": {}, "updated_at": {}, "infinite_shares": 0, "account_role": { "id": 2, "name": "PMS", "created_at": {}, "updated_at": {} }, "authorities": [] }, "account_user_role": { "id": 1, "name": "Super Admin", "created_at": {}, "updated_at": {} }, "lots": { "id": 1, "name": "Default Lot", "address": "Put Stinica 12" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update account users `PATCH https://api.parklio.com/v2/account-users/{id}` This endpoint allows the user to update account users by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `account_user_role_id` | number | no | | | `lots` | number[] | no | | | `email` | string | no | | | `password` | string | no | | | `name` | string | no | | | `phone` | string | no | | | `account_user_role` | string (`Super Admin`, `Admin`, `Operator`) | no | | ```json { "account_user_role_id": 2, "lots": [ 1, 2, 3 ], "email": "john.doe@example.com", "password": "string", "name": "John Doe", "phone": "+385 555 5555", "account_user_role": "Super Admin" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "account_user_role_id": 1, "account_id": 0, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "language_tag": "fr-BE", "country_code": "BE", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account": { "id": 1, "account_role_id": 1, "auto_extend": {}, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 666 6666", "billing_title": "string", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "created_at": {}, "updated_at": {}, "infinite_shares": 0, "account_role": { "id": 2, "name": "PMS", "created_at": {}, "updated_at": {} }, "authorities": [] }, "account_user_role": { "id": 1, "name": "Super Admin", "created_at": {}, "updated_at": {} }, "lots": { "id": 1, "name": "Default Lot", "address": "Put Stinica 12" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete account users `DELETE https://api.parklio.com/v2/account-users/{id}` This endpoint allows the user to delete account users by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 400 — This is a generic bad request error ```json { "success": false, "errors": [ { "code": 33, "message": "This is a generic bad request error" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create lots `POST https://api.parklio.com/v2/lots` This endpoint allows the user to create lots **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | | | `description` | string | no | | | `address` | string | yes | | | `latitude` | number | no | | | `longitude` | number | no | | ```json { "name": "Lot name", "description": "Lot description", "address": "Lot address", "latitude": 43.52652359, "longitude": 16.450813293 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "name": "John Doe", "description": "Lot description", "address": "Lot address", "latitude": 45.14270079621355, "longitude": 24.789809374999987, "timezone": "Europe/Zagreb", "spot_count": 10, "occupied_count": 0, "detection_status": "string", "detection_implementation_status": "string", "lot_state_representation_vector": "string", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # List lots `GET https://api.parklio.com/v2/lots` Lists the lots your token can see. Each lot has its own `spot_count` and `occupied_count` (see [Live occupancy](/guides/live-occupancy)) and its `timezone`. `detection_status` is `online` for lots with parking detection and `unkown` (sic) otherwise; `detection_implementation_status` is `implemented`, `not_implemented`, `pending` or `blocked`. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `anpr_presence`, `detection_implementation_status`, `lot_parking_detection`, `name`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "data": [ { "id": 7, "account_id": 3, "name": "Main garage", "description": null, "address": "Example street 1", "latitude": 45.815, "longitude": 15.982, "timezone": "Europe/Zagreb", "spot_count": 120, "occupied_count": 42, "detection_status": "unkown", "detection_implementation_status": "not_implemented", "lot_state_representation_vector": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ], "meta": { "total": 1, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=1", "last_page_url": "/?page=1", "next_page_url": "/?page=2", "previous_page_url": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Update lots `PATCH https://api.parklio.com/v2/lots/{id}` This endpoint allows the user to update lots by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `description` | string | no | | | `address` | string | no | | | `timezone` | string | no | | | `latitude` | number | no | | | `longitude` | number | no | | ```json { "name": "John Doe", "description": "Lot description", "address": "Lot address", "timezone": "Europe/Zagreb", "latitude": 43.52652359, "longitude": 16.450813293 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "name": "John Doe", "description": "Lot description", "address": "Lot address", "latitude": 45.14270079621355, "longitude": 24.789809374999987, "timezone": "Europe/Zagreb", "spot_count": 10, "occupied_count": 0, "detection_status": "string", "detection_implementation_status": "string", "lot_state_representation_vector": "string", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } } ``` ### 400 — You cannot update lot detection parameters without an valid lot detection on the lot. Please add parking detection to the lot first. ```json { "success": false, "errors": [ { "code": 304, "message": "No Parking detection active for lot." } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete lots `DELETE https://api.parklio.com/v2/lots/{id}` This endpoint allows the user to delete lots by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get a lot `GET https://api.parklio.com/v2/lots/{id}` Returns one lot, with its occupancy totals. See [Live occupancy](/guides/live-occupancy). **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The lot's `id`. | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "name": "John Doe", "description": "Lot description", "address": "Lot address", "latitude": 45.14270079621355, "longitude": 24.789809374999987, "timezone": "Europe/Zagreb", "spot_count": 10, "occupied_count": 0, "detection_status": "string", "detection_implementation_status": "string", "lot_state_representation_vector": "string", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get all lot managers `GET https://api.parklio.com/v2/lots/{id}/managers` This endpoint allows the user to list all lot managers **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `email`, `name`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `email`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "account_user": { "id": 0, "account_user_role_id": 1, "account_id": 0, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "language_tag": "fr-BE", "country_code": "BE", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account": { "id": 1, "account_role_id": 1, "auto_extend": {}, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 666 6666", "billing_title": "string", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "created_at": {}, "updated_at": {}, "infinite_shares": 0, "account_role": { "id": 2, "name": "PMS", "created_at": {}, "updated_at": {} }, "authorities": [] }, "account_user_role": { "id": 1, "name": "Super Admin", "created_at": {}, "updated_at": {} }, "lots": { "id": 1, "name": "Default Lot", "address": "Put Stinica 12" } }, "account_user_id": 0, "lot_id": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Update lot managers `PATCH https://api.parklio.com/v2/lots/{id}/managers` This endpoint allows the user to update lot managers by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `managers` | number[] | yes | Set of Account User IDs targeted for updates. | ```json { "managers": [ 1, 2, 3 ] } ``` ## Responses ### 200 — Successful response ```json { "success": true, "data": [ { "success": true, "id": 1, "account_user_id": 1 } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete lot managers `DELETE https://api.parklio.com/v2/lots/{id}/managers/{managerId}` This endpoint allows the user to delete lot managers by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `managerId` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create zones `POST https://api.parklio.com/v2/zones` This endpoint allows the user to create zones **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | The name of the zone | | `lot_id` | number | yes | The ID of the lot this zone belongs to | | `description` | string | no | A brief description of the zone | | `max_levenshtein_distance` | number | no | The maximum Levenshtein distance allowed when fuzzy matching scanned license plates against whitelisted plates | ```json { "name": "Main Entrance Zone", "lot_id": 1, "description": "Zone covering the main entrance and visitor parking area", "max_levenshtein_distance": 1 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "name": "Default Zone", "description": "Zone covering the main entrance and visitor parking area", "spot_count": 10, "occupied_count": 0, "tariffs": [ { "id": 1, "name": "Basic Tariff", "order": 1 } ], "max_levenshtein_distance": 1, "vector_map": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # List zones `GET https://api.parklio.com/v2/zones` Lists zones: the areas of a lot that products, cameras, tariffs and counts belong to. Use `filter=lot_id:eq:` for one lot's zones, and `spot_count` − `occupied_count` for free spaces. See [Live occupancy](/guides/live-occupancy). **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `anpr_presence`, `lot_id`, `lot_ids`, `name`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "data": [ { "id": 9, "account_id": 3, "lot_id": 7, "name": "Level -1", "description": null, "spot_count": 120, "occupied_count": 42, "tariffs": [ { "id": 4, "name": "Weekdays", "order": 0 } ], "max_levenshtein_distance": 1, "vector_map": null, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ], "meta": { "total": 1, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=1", "last_page_url": "/?page=1", "next_page_url": "/?page=2", "previous_page_url": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get a zone `GET https://api.parklio.com/v2/zones/{id}` Returns one zone, with its counts and assigned tariffs. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The zone's `id`. | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "name": "Default Zone", "description": "Zone covering the main entrance and visitor parking area", "spot_count": 10, "occupied_count": 0, "tariffs": [ { "id": 1, "name": "Basic Tariff", "order": 1 } ], "max_levenshtein_distance": 1, "vector_map": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update zones `PATCH https://api.parklio.com/v2/zones/{id}` This endpoint allows the user to update zones by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | The name of the zone | | `lot_id` | number | no | The ID of the lot this zone belongs to | | `description` | string | no | A brief description of the zone | | `occupied_count` | number | no | The current number of occupied spots in the zone | | `spot_count` | number | no | The total number of parking spots in the zone | | `max_levenshtein_distance` | number | no | The maximum Levenshtein distance allowed when fuzzy matching scanned license plates against whitelisted plates | ```json { "name": "Main Entrance Zone", "lot_id": 1, "description": "Zone covering the main entrance and visitor parking area", "occupied_count": 42, "spot_count": 100, "max_levenshtein_distance": 1 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "name": "Default Zone", "description": "Zone covering the main entrance and visitor parking area", "spot_count": 10, "occupied_count": 0, "tariffs": [ { "id": 1, "name": "Basic Tariff", "order": 1 } ], "max_levenshtein_distance": 1, "vector_map": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete zones `DELETE https://api.parklio.com/v2/zones/{id}` This endpoint allows the user to delete zones by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get populated vector map for a zone `GET https://api.parklio.com/v2/zones/{id}/vector` Returns the zone's vector map as a raw SVG, with each parking place dot colored according to its current occupancy status. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — The populated vector map SVG ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Assign zone tariffs `PUT https://api.parklio.com/v2/zones/{id}/tariffs/{tariffId}` This endpoint allows the user to assign zone tariffs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | | `tariffId` | number | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "name": "Default Zone", "description": "Zone covering the main entrance and visitor parking area", "spot_count": 10, "occupied_count": 0, "tariffs": [ { "id": 1, "name": "Basic Tariff", "order": 1 } ], "max_levenshtein_distance": 1, "vector_map": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Unassign zone tariffs `DELETE https://api.parklio.com/v2/zones/{id}/tariffs/{tariffId}` This endpoint allows the user to unassign zone tariffs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `tariffId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "name": "Default Zone", "description": "Zone covering the main entrance and visitor parking area", "spot_count": 10, "occupied_count": 0, "tariffs": [ { "id": 1, "name": "Basic Tariff", "order": 1 } ], "max_levenshtein_distance": 1, "vector_map": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk assign zone tariffs `POST https://api.parklio.com/v2/zones/{id}/tariffs` This endpoint allows the user to bulk assign zone tariffs by using their ids **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | ```json {} ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "name": "Default Zone", "description": "Zone covering the main entrance and visitor parking area", "spot_count": 10, "occupied_count": 0, "tariffs": [ { "id": 1, "name": "Basic Tariff", "order": 1 } ], "max_levenshtein_distance": 1, "vector_map": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk unassign zone tariffs `DELETE https://api.parklio.com/v2/zones/{id}/tariffs` This endpoint allows the user to bulk unassign zone tariffs by using their ids **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | ```json {} ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "name": "Default Zone", "description": "Zone covering the main entrance and visitor parking area", "spot_count": 10, "occupied_count": 0, "tariffs": [ { "id": 1, "name": "Basic Tariff", "order": 1 } ], "max_levenshtein_distance": 1, "vector_map": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Replace zone tariffs `PUT https://api.parklio.com/v2/zones/{id}/tariffs` This endpoint allows the user to replace zone tariffs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | ```json {} ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "name": "Default Zone", "description": "Zone covering the main entrance and visitor parking area", "spot_count": 10, "occupied_count": 0, "tariffs": [ { "id": 1, "name": "Basic Tariff", "order": 1 } ], "max_levenshtein_distance": 1, "vector_map": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Update zone tariff order `PATCH https://api.parklio.com/v2/zones/{zone_id}/tariffs/{tariff_id}` This endpoint allows the user to update zone tariff order by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `zone_id` | string | yes | | | `tariff_id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | ```json {} ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "name": "Default Zone", "description": "Zone covering the main entrance and visitor parking area", "spot_count": 10, "occupied_count": 0, "tariffs": [ { "id": 1, "name": "Basic Tariff", "order": 1 } ], "max_levenshtein_distance": 1, "vector_map": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Create a lot share view `POST https://api.parklio.com/v2/lot-share-views` Creates a **public** web page with live occupancy for some zones of a lot, viewable without logging in. The response's `url` is the page: share it as you would a password. - `zone_ids`: at least one zone of the lot. - `lot_share_view_type`: `counts_only`, `live_camera` (needs a parking-detection camera in a zone) or `vector_map` (needs zones with a map). Revoke it with `PATCH /v2/lot-share-views/{id}` and `{"revoked": true}`. See the [Live occupancy](/guides/live-occupancy#6-share-a-live-view) guide. **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `lot_id` | number | yes | The ID of the lot this lot share view belongs to. | | `zone_ids` | number[] | no | The IDs of the zones to include in this lot share view. At least one zone must be provided. | | `lot_share_view_type` | string (`counts_only`, `live_camera`, `vector_map`) | yes | The type of view rendered when the share link is opened. Determines what information is displayed to the public. | ```json { "lot_id": 1, "zone_ids": [ 1, 2 ], "lot_share_view_type": "counts_only" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 21, "revoked": false, "url": "https://api.parklio.com/v2/lot-share-views/display/", "lot_share_view_type": "counts_only", "lot": { "id": 7, "name": "Main garage", "spot_count": 120, "occupied_count": 42 }, "zones": [ { "id": 9, "name": "Level -1", "spot_count": 80, "occupied_count": 30 }, { "id": 10, "name": "Level -2", "spot_count": 40, "occupied_count": 12 } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all lot share views `GET https://api.parklio.com/v2/lot-share-views` This endpoint allows the user to list all lot share views **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `is_revoked`, `zone_id`, `lot_id`, `lot_share_view_type`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "revoked": false, "url": "string", "lot_share_view_type": "counts_only", "lot": { "id": 1, "name": "Default Lot", "spot_count": 10, "occupied_count": 0 }, "zones": [ { "id": 1, "name": "Zone A", "spot_count": 10, "occupied_count": 0 } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Assign zones to lot share view `POST https://api.parklio.com/v2/lot-share-views/{id}/zones` This endpoint allows the user to assign zones to lot share view by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | The IDs of the zones to assign to this lot share view. | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "revoked": false, "url": "string", "lot_share_view_type": "counts_only", "lot": { "id": 1, "name": "Default Lot", "spot_count": 10, "occupied_count": 0 }, "zones": [ { "id": 1, "name": "Zone A", "spot_count": 10, "occupied_count": 0 } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Replace zones for lot share view `PUT https://api.parklio.com/v2/lot-share-views/{id}/zones` This endpoint allows the user to replace zones for lot share view by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | The IDs of the zones to assign to this lot share view, replacing any previously assigned zones. | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "revoked": false, "url": "string", "lot_share_view_type": "counts_only", "lot": { "id": 1, "name": "Default Lot", "spot_count": 10, "occupied_count": 0 }, "zones": [ { "id": 1, "name": "Zone A", "spot_count": 10, "occupied_count": 0 } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Unassign zones from lot share view `DELETE https://api.parklio.com/v2/lot-share-views/{id}/zones` This endpoint allows the user to unassign zones from lot share view by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | The IDs of the zones to unassign from this lot share view. | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "revoked": false, "url": "string", "lot_share_view_type": "counts_only", "lot": { "id": 1, "name": "Default Lot", "spot_count": 10, "occupied_count": 0 }, "zones": [ { "id": 1, "name": "Zone A", "spot_count": 10, "occupied_count": 0 } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get lot share views `GET https://api.parklio.com/v2/lot-share-views/{id}` This endpoint allows the user to get lot share views by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "revoked": false, "url": "string", "lot_share_view_type": "counts_only", "lot": { "id": 1, "name": "Default Lot", "spot_count": 10, "occupied_count": 0 }, "zones": [ { "id": 1, "name": "Zone A", "spot_count": 10, "occupied_count": 0 } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update lot share views `PATCH https://api.parklio.com/v2/lot-share-views/{id}` This endpoint allows the user to update lot share views by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `revoked` | boolean | no | Set to true to revoke the lot share view, immediately blocking further access. Set to false to restore access to a previously revoked lot share view. | ```json { "revoked": true } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "revoked": false, "url": "string", "lot_share_view_type": "counts_only", "lot": { "id": 1, "name": "Default Lot", "spot_count": 10, "occupied_count": 0 }, "zones": [ { "id": 1, "name": "Zone A", "spot_count": 10, "occupied_count": 0 } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete lot share views `DELETE https://api.parklio.com/v2/lot-share-views/{id}` This endpoint allows the user to delete lot share views by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Render the current visual state of a lot for a given share link token (public, unauthenticated) `GET https://api.parklio.com/v2/lot-share-views/display/{token}` Returns an HTML page showing the current occupancy state of a parking lot. The view type (counts only, live camera, or vector map) is determined by the share link configuration. This endpoint is public and requires no authentication. **Auth:** none (public) ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `token` | string | yes | The unique token used for the share link | ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `darkmode` | boolean | no | Pass darkmode=true to force dark mode in the rendered page | | `navbar` | string (`hidden`) | no | Pass navbar=hidden to hide the header/navbar in the rendered page | ## Responses ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Assign zone to lot share view `PUT https://api.parklio.com/v2/lot-share-views/{id}/zones/{zoneId}` This endpoint allows the user to assign zone to lot share view by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | | `zoneId` | number | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "revoked": false, "url": "string", "lot_share_view_type": "counts_only", "lot": { "id": 1, "name": "Default Lot", "spot_count": 10, "occupied_count": 0 }, "zones": [ { "id": 1, "name": "Zone A", "spot_count": 10, "occupied_count": 0 } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Unassign zone from lot share view `DELETE https://api.parklio.com/v2/lot-share-views/{id}/zones/{zoneId}` This endpoint allows the user to unassign zone from lot share view by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `zoneId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "revoked": false, "url": "string", "lot_share_view_type": "counts_only", "lot": { "id": 1, "name": "Default Lot", "spot_count": 10, "occupied_count": 0 }, "zones": [ { "id": 1, "name": "Zone A", "spot_count": 10, "occupied_count": 0 } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # List products `GET https://api.parklio.com/v2/products` Lists the products your token can see, with their current state. - Each product has exactly one **type block**, named after its type: `barrier`, `gate`, `chain`, `bollard` or `brain`. See [Products and operations](/concepts/products-and-operations). - `status` is `online` while a Gateway sees the product. `battery_status` (barriers only) and `rssi` are `null` while offline. - `lot_id` filters on the lot of the product's **zone**. - Products that aren't paired aren't listed. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`, `created_at`, `updated_at`. See [Sorting](/api/conventions#sorting). | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `lot_id`, `name`, `product_type_id`, `zone_id`, `device_id`. See [Filtering](/api/conventions#filtering). | ## Responses ### 200 ```json { "success": true, "data": [ { "id": 123, "device_id": "B1234567", "name": "Entrance barrier", "power_type": "battery", "lot_id": 7, "zone_id": 9, "product_type_id": 2, "hardware_version": null, "firmware_version": null, "latitude": 45.815, "longitude": 15.982, "status": "online", "advertisement_data": "", "battery_status": 80, "rssi": -70, "product_error": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "barrier": { "id": 41, "product_id": 123, "state": "closed", "is_autoclose": 1, "is_car_on_top": 0, "autoclose_time": 45, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ], "meta": { "total": 1, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=1", "last_page_url": "/?page=1", "next_page_url": "/?page=2", "previous_page_url": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get a product `GET https://api.parklio.com/v2/products/{id}` Returns one product with its current state, type block and error flags. Re-read a product with it when you gave up waiting for a command's result. See [Products and operations](/concepts/products-and-operations). **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The product's `id`. | ## Responses ### 200 ```json { "success": true, "data": { "id": 124, "device_id": "G7654321", "name": "Main gate", "power_type": "grid", "lot_id": 7, "zone_id": 9, "product_type_id": 3, "hardware_version": null, "firmware_version": null, "latitude": 45.815, "longitude": 15.982, "status": "online", "advertisement_data": "", "battery_status": null, "rssi": -64, "product_error": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "gate": { "id": 12, "product_id": 124, "state": "closed", "permanent_position": "disabled", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Patch a product `PATCH https://api.parklio.com/v2/products/{id}` This endpoint allows the user to patch a product assigned to his account **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | The user set product name | | `longitude` | number | no | The product longitude, acquired during setup | | `latitude` | number | no | The product latitude, acquired during setup | | `zone_id` | number | no | The the id of the target zone where we want to move the product | ```json { "name": "B123456", "longitude": 16.450813293, "latitude": 43.52652359, "zone_id": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "product_error": { "id": 0, "jammed": 0, "sensor_dirty": 0, "pin_broken": 0, "rtcc_invalid": 0, "battery_empty": 0, "master_key_not_set": 0, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "error_code": "string" }, "id": 0, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 0, "zone_id": 0, "product_type_id": 2, "hardware_version": "1.0.0", "firmware_version": "1.0.0", "latitude": 43.52652359, "longitude": 16.450813293, "status": "online", "advertisement_data": "string", "battery_status": null, "product_error_id": null, "rssi": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "barrier": { "id": 0, "product_id": 0, "state": "opened", "is_autoclose": 0, "is_car_on_top": 0, "autoclose_time": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "gate": { "id": 0, "product_id": 0, "state": "closed", "permanent_position": "disabled", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "chain": { "id": 0, "product_id": 0, "state": "closed", "permanent_position": "disabled", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "bollard": { "id": 0, "product_id": 0, "state": "closed", "permanent_position": "disabled", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "brain": { "id": 0, "product_id": 0, "state": "closed", "permanent_position": "disabled", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } } ``` ### 400 — Cannot be moved between different lots. ```json { "success": false, "errors": [ { "code": 1000, "message": "Cannot move to different lot." } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Unassign a product `DELETE https://api.parklio.com/v2/products/{id}` This endpoint allows the user to unassign a product from his account. Allowing the other users to add it to their accounts. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Operate a product `POST https://api.parklio.com/v2/products/{id}/state` Sends an operation to a product through a Gateway. **Asynchronous:** the response is an `event_id`, and the result arrives later over [realtime](/realtime/connecting): a `lot_change` with the same `event_id` on success, or a `gateway_error` on failure. Connect to realtime **before** you call this. | Operation | Barrier | Gate, chain, bollard, brain | | --- | --- | --- | | `open` | arm lowers | passage opens | | `close` | arm rises | passage closes | | `force_close` | ✅ | — | | `stay_open`, `stay_close`, `clear_stay_open`, `clear_stay_close` | — | ✅ | The API doesn't check the combination: check it yourself. Needs the `REMOTE_CONTROL` feature. Errors before anything is sent: `214` (an operation on this product is still running), `704` (no Gateway sees the product), `206` (not paired), `30` (no remote control in the subscription). See [Asynchronous operations](/concepts/asynchronous-operations) and the [Remote control](/guides/remote-control) guide. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The product's `id`. | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `operation` | string (`open`, `force_close`, `close`, `stay_open`, `stay_close`, `clear_stay_open`, `clear_stay_close`) | yes | The operation that we want to run on a product. | | `wait_for_completion` | boolean | no | Used to disable operation retry. In normal circumstances the API is going to wait until a operation succedes in changing the state,for example when sending a "close" command, the API will wait until the product is in a closed state. If that times out it will retry multiple times. In situations when using a brain, this can couse problems if the brain is not setup with input sensors since there will be no open/close state to read. | ```json { "operation": "open", "wait_for_completion": true } ``` ## Responses ### 200 ```json { "success": true, "data": { "event_id": "a1b2c3d4" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Change barrier settings `POST https://api.parklio.com/v2/products/{id}/settings` Changes a barrier's auto-close, through a Gateway. Send **one** setting per call: - `is_autoclose`: `true` or `false` (a JSON boolean, not `1`/`0`); - `autoclose_time`: the delay in seconds, 5–60. If you send both, only `is_autoclose` is applied. An empty body fails. **Asynchronous**, like [Operate a product](/api/endpoints/products-change-state): the response is an `event_id`, and the result arrives as a realtime `lot_change` or `gateway_error`. In `lot_change`, the barrier block calls these fields `is_auto_close` and `auto_close_time`. Needs the `REMOTE_CONTROL` feature. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The barrier's product `id`. | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `is_autoclose` | boolean | no | Turns the barrier autoclose on or off. | | `autoclose_time` | number | no | Changes the barrier autoclose time. | ```json { "is_autoclose": true, "autoclose_time": 15 } ``` ## Responses ### 200 ```json { "success": true, "data": { "event_id": "a1b2c3d4" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # List products with errors `GET https://api.parklio.com/v2/products/errors` Lists the products that currently report at least one error flag (`jammed`, `sensor_dirty`, `pin_broken`, `rtcc_invalid`, `battery_empty`, `master_key_not_set`). Use it for a maintenance view. See the [Monitor devices](/guides/monitor-devices) guide. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`, `created_at`, `updated_at`. See [Sorting](/api/conventions#sorting). | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `lot_id`, `name`, `product_type_id`, `zone_id`, `device_id`. See [Filtering](/api/conventions#filtering). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "product": { "id": 1, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 1, "zone_id": 1, "product_type_id": 2, "status": "online" }, "id": 0, "error_code": "string", "jammed": 0, "sensor_dirty": 0, "pin_broken": 0, "rtcc_invalid": 0, "battery_empty": 0, "master_key_not_set": 0, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Bind plates to a product `POST https://api.parklio.com/v2/products/{id}/bind-licenses` Lets whitelisted license plates open this product. When an LPR camera in the product's **zone** reads a bound plate on the way **in**, and the plate is on an active [lot whitelist](/api/endpoints/lot-whitelist-create) entry, Parklio opens the first bound product that's online and closed. `license_ids` are the `license_id` values returned by [Create lot whitelist entries](/api/endpoints/lot-whitelist-create). Unknown ids are ignored silently: check with [`GET /v2/products/{id}/bound-licenses`](/api/endpoints/products-find-all-binds). See the [Plate-based access](/guides/plate-based-access) guide. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The product's `id`. | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `license_ids` | number[] | yes | | ```json { "license_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 — Bind success ```json { "success": true, "message": "Bind success" } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bind plates to several products `POST https://api.parklio.com/v2/products/bind-licenses` Binds the same plates to several products at once. Same rules as [Bind plates to a product](/api/endpoints/products-bind-license-for-product): `license_ids` are `license_id` values from the lot whitelist, and unknown ids are ignored silently. **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `license_ids` | number[] | yes | | | `product_ids` | number[] | yes | | ```json { "license_ids": [ 1, 2, 3 ], "product_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 — Bind success ```json { "success": true, "message": "Bind success" } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` --- # List all bound license plates on a product `GET https://api.parklio.com/v2/products/{id}/bound-licenses` Endpoint returns all the license plates bound to the product. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`, `created_at`, `updated_at`. See [Sorting](/api/conventions#sorting). | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "license_plate": "DT-PLATE2", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Unbinds multiple Products from a License Plate `DELETE https://api.parklio.com/v2/products/bound-licenses/{licenseId}` This endpoint unbinds multiple products from a license **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `licenseId` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `product_ids` | number[] | yes | | ```json { "product_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 — Licenses have been unbound ```json { "success": true, "message": "Licenses have been unbound" } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Unbinds a Product from a License Plate `DELETE https://api.parklio.com/v2/products/{id}/bound-licenses/{licenseId}` This endpoint unbinds a product from a license **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `licenseId` | string | yes | | ## Responses ### 200 — Licence has been unbound ```json { "success": true, "message": "Licence has been unbound" } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # List Gateways `GET https://api.parklio.com/v2/gateways` Lists your Gateways. `status` is `online` while the Gateway is connected to Parklio; filter with `status:eq:offline` to find the ones that aren't. The realtime `gateway_status` event identifies a Gateway by its `uuid`. See the [Monitor devices](/guides/monitor-devices) guide. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `lot_id`, `name`, `status`, `uuid`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`, `created_at`, `updated_at`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "data": [ { "id": 5, "account_id": 3, "lot_id": 7, "uuid": "xgobgwEG7rmZ", "name": "Garage entrance", "firmware_version": "1.0.0", "ip_address": "192.0.2.10", "is_blocked": 0, "mac": "00:1A:2B:3C:4D:5E", "status": "online", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } ], "meta": { "total": 1, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=1", "last_page_url": "/?page=1", "next_page_url": "/?page=2", "previous_page_url": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get a Gateway `GET https://api.parklio.com/v2/gateways/{id}` Returns one Gateway and its `status`. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The Gateway's `id`. | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "uuid": "ga27btcxx21", "name": 1, "firmware_version": "1.0.0", "ip_address": "192.158.1.38", "is_blocked": 0, "mac": "00:1A:2B:3C:4D:5E", "status": "online", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update gateways `PATCH https://api.parklio.com/v2/gateways/{id}` This endpoint allows the user to update gateways by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `lot_id` | number | no | | ```json { "name": "ga27btcxx21", "lot_id": 5 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "lot_id": 1, "uuid": "ga27btcxx21", "name": 1, "firmware_version": "1.0.0", "ip_address": "192.158.1.38", "is_blocked": 0, "mac": "00:1A:2B:3C:4D:5E", "status": "online", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete gateways `DELETE https://api.parklio.com/v2/gateways/{id}` This endpoint allows the user to delete gateways by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Gateway successfuly unassigned. ```json { "success": true, "message": "Gateway successfuly unassigned." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Run a operation on a gateway `POST https://api.parklio.com/v2/gateways/{id}/operation` This endpoint serves as the command center for managing a gateway device, enabling users to execute essential operations such as rebooting, info or forcing a scan. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `operation` | string (`reboot`, `info`, `force_scan`) | yes | | ```json { "operation": "reboot" } ``` ## Responses ### 200 ```json { "success": true, "data": { "gateway_id": 0 } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Assign a new LPR Camera to an account `POST https://api.parklio.com/v2/lpr-cameras` This endpoint serves the purpose of assigning a parklio lpr camera to the account of the authorised user. Assigning is done by scanning the UUID sticker located on the LPR Camera. **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `device_id` | string | yes | The unique device id found on the Camera sticker | | `name` | string | yes | A user assigned name | | `connected_device_type` | string (`gate`, `chain`, `bollard`, `other`, `standalone`) | no | A enum indicating the type of device connected to the Camera | | `lpr_position` | string (`in`, `out`, `in/out`) | yes | The direction of the passage that the Camera is overlooking | | `zone_id` | number | yes | The zone we wish to assign the camera to | ```json { "device_id": "L0123456789", "name": "John Doe", "connected_device_type": "gate", "lpr_position": "in", "zone_id": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "L123456789", "device_id": "L123456789", "device_serial": "L123456789", "account_id": "1", "zone_id": "1", "status": "online", "lpr_position": "in", "connected_device_type": "gate", "input_status": "unknown", "mac": "be:a0:42:fd:21:b8", "save_image": false, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all lpr cameras `GET https://api.parklio.com/v2/lpr-cameras` This endpoint allows the user to list all lpr cameras **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `lot_id`, `name`, `position`, `status`, `zone_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`, `updated_at`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "name": "L123456789", "device_id": "L123456789", "device_serial": "L123456789", "account_id": "1", "zone_id": "1", "status": "online", "lpr_position": "in", "connected_device_type": "gate", "input_status": "unknown", "mac": "be:a0:42:fd:21:b8", "save_image": false, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Assign a new Axis LPR Camera to an account `POST https://api.parklio.com/v2/lpr-cameras/axis` This endpoint serves the purpose of assigning a parklio lpr camera to the account of the authorised user. Assigning is done by scanning the UUID sticker located on the LPR Camera. **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `mac` | string | yes | | | `name` | string | no | A user assigned name | | `connected_device_type` | string (`gate`, `chain`, `bollard`, `other`, `standalone`) | no | A enum indicating the type of device connected to the Camera | | `lpr_position` | string (`in`, `out`, `in/out`) | yes | The direction of the passage that the Camera is overlooking | | `zone_id` | number | yes | The zone we wish to assign the camera to | ```json { "mac": "00:1A:2B:3C:4D:5E", "name": "John Doe", "connected_device_type": "gate", "lpr_position": "in", "zone_id": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "L123456789", "device_id": "L123456789", "device_serial": "L123456789", "account_id": "1", "zone_id": "1", "status": "online", "lpr_position": "in", "connected_device_type": "gate", "input_status": "unknown", "mac": "be:a0:42:fd:21:b8", "save_image": false, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — LPR has owner already set and trying to set another one won't work. User should first unassign LPR owner before trying again. ```json { "success": false, "errors": [ { "code": 223, "message": "Lpr camera owner already set." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all lpr camera state changes `GET https://api.parklio.com/v2/lpr-cameras/{id}/state-changes` This endpoint allows the user to list all lpr camera state changes **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`, `created_at`, `status`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "lpr_camera_id": 1, "status": "online", "created_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get lpr cameras `GET https://api.parklio.com/v2/lpr-cameras/{id}` This endpoint allows the user to get lpr cameras by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "L123456789", "device_id": "L123456789", "device_serial": "L123456789", "account_id": "1", "zone_id": "1", "status": "online", "lpr_position": "in", "connected_device_type": "gate", "input_status": "unknown", "mac": "be:a0:42:fd:21:b8", "save_image": false, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update lpr cameras `PATCH https://api.parklio.com/v2/lpr-cameras/{id}` This endpoint allows the user to update lpr cameras by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | A user assigned name | | `connected_device_type` | string (`gate`, `chain`, `bollard`, `other`, `standalone`) | no | A enum indicating the type of device connected to the Camera | | `lpr_position` | string (`in`, `out`, `in/out`) | no | The direction of the passage that the Camera is overlooking | | `zone_id` | number | no | The zone we wish to assign the camera to | | `save_image` | boolean | no | Updates whether the camera should save captured images. | ```json { "name": "L0123456789", "connected_device_type": "gate", "lpr_position": "in", "zone_id": 0, "save_image": true } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "L123456789", "device_id": "L123456789", "device_serial": "L123456789", "account_id": "1", "zone_id": "1", "status": "online", "lpr_position": "in", "connected_device_type": "gate", "input_status": "unknown", "mac": "be:a0:42:fd:21:b8", "save_image": false, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have sufficient account role to perform the desired action. Parklio could upgrade his account role. ```json { "success": false, "errors": [ { "code": 11, "message": "Not allowed for undefined account role." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete lpr cameras `DELETE https://api.parklio.com/v2/lpr-cameras/{id}` This endpoint allows the user to delete lpr cameras by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have sufficient account role to perform the desired action. Parklio could upgrade his account role. ```json { "success": false, "errors": [ { "code": 11, "message": "Not allowed for undefined account role." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get the setup information for the LPR Camera. `GET https://api.parklio.com/v2/lpr-cameras/{id}/informations` This endpoint returns the credentials used for the LPR Camera setup. Using this information, the camera can be authenticated to the Parklio Server **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "server_url": "lpr.parklio.com", "server_port": 7660, "password": "somepassword" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Send the open signal to the camera `POST https://api.parklio.com/v2/lpr-cameras/{id}/state` This enpoint sends a OPEN operation to the camera. The open operation will trigger a signal on the LPR Camera output which can be used as input into any supported hardware, triggering an operation on the device. For example, if the LPR Cameras output is connected to a Gate input, it will trigger a change of state on the Gate. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `operation` | string (`open`, `close`) | no | The signal we want to send to the camera | | `duration` | number | no | The duration of the signal | ```json { "operation": "open", "duration": 1 } ``` ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 503 — LPR camera is not connected. ```json { "success": false, "errors": [ { "code": 802, "message": "LPR camera is offline." } ] } ``` --- # Reboot the LPR Camera `POST https://api.parklio.com/v2/lpr-cameras/{id}/reboot` This enpoint sends a REBOOT comand to the LPR Camera, the camera will reboot and come back online after a minute. Useful when an problem happens with the camera. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 503 — LPR camera is not connected. ```json { "success": false, "errors": [ { "code": 802, "message": "LPR camera is offline." } ] } ``` --- # Create rotaries parking places `POST https://api.parklio.com/v2/rotaries/parking-places` This endpoint allows the user to create rotaries parking places **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | | | `rotary_id` | number | yes | | ```json { "name": "Parking Place 1", "rotary_id": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "rotary_id": 0, "is_occupied": true, "name": "Parking Place 1", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all rotaries parking places `GET https://api.parklio.com/v2/rotaries/parking-places` This endpoint allows the user to list all rotaries parking places **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `rotary_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "rotary_id": 0, "is_occupied": true, "name": "Parking Place 1", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Bulk update a rotary parking places `PATCH https://api.parklio.com/v2/rotaries/parking-places` This endpoint updates the values of a rotary parking place by ids **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | string (`online`, `offline`) | no | | | `ids` | string[] | yes | | | `is_occupied` | boolean | yes | | ```json { "status": "online", "ids": [ 1, 2 ], "is_occupied": true } ``` ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get rotaries parking places `GET https://api.parklio.com/v2/rotaries/parking-places/{id}` This endpoint allows the user to get rotaries parking places by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "rotary_id": 0, "is_occupied": true, "name": "Parking Place 1", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update rotaries parking places `PATCH https://api.parklio.com/v2/rotaries/parking-places/{id}` This endpoint allows the user to update rotaries parking places by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `rotary_id` | number | no | | | `is_occupied` | boolean | no | | ```json { "name": "Rotary Parking Place 1", "rotary_id": 0, "is_occupied": true } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "rotary_id": 0, "is_occupied": true, "name": "Parking Place 1", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete rotaries parking places `DELETE https://api.parklio.com/v2/rotaries/parking-places/{id}` This endpoint allows the user to delete rotaries parking places by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create rotaries `POST https://api.parklio.com/v2/rotaries` This endpoint allows the user to create rotaries **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | | | `lot_id` | number | no | | ```json { "name": "Default Rotary", "lot_id": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "name": "Default Rotary", "account_id": 0, "lot_id": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all rotaries `GET https://api.parklio.com/v2/rotaries` This endpoint allows the user to list all rotaries **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `lot_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "name": "Default Rotary", "account_id": 0, "lot_id": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get rotaries `GET https://api.parklio.com/v2/rotaries/{id}` This endpoint allows the user to get rotaries by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "name": "Default Rotary", "account_id": 0, "lot_id": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update rotaries `PATCH https://api.parklio.com/v2/rotaries/{id}` This endpoint allows the user to update rotaries by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `lot_id` | number | no | | ```json { "name": "Default Rotary", "lot_id": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "name": "Default Rotary", "lot_id": 0 } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete rotaries `DELETE https://api.parklio.com/v2/rotaries/{id}` This endpoint allows the user to delete rotaries by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create an operation token `POST https://api.parklio.com/v2/operation-tokens` Creates a token that lets **your app** operate one product over Bluetooth, through the Parklio SDK, during a time window. Pass `token_open`, `token_close` and `token_status` to the app unchanged. - Send `end_time`, in the future and after `start_time`. `start_time` may be in the past. - **Revoking doesn't reach the device:** a token works until `end_time` even if you block or delete it. Create tokens just in time, with short windows, and avoid `is_unlimited` (valid until 2038). - Needs no plan feature and no Gateway. See the [Operation tokens for your app](/guides/mobile-app-operation-tokens) guide. **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `start_time` | string | yes | The starting time for the operation token | | `end_time` | string | no | The end time for the operation token | | `product_id` | number | yes | The product id to controll with the operation token | | `is_unlimited` | number | no | Should the operation key be unlimited | ```json { "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "product_id": 0, "is_unlimited": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 9001, "product_id": 123, "start_time": "2026-03-02T08:00:00.000Z", "end_time": "2026-03-02T10:00:00.000Z", "status": "active", "used_count": 0, "is_unlimited": 0, "token_open": { "token": "", "status_key": "", "challenge_key": "" }, "token_close": { "token": "", "status_key": "", "challenge_key": "" }, "token_status": { "token": "", "status_key": "", "challenge_key": "" }, "product": { "id": 123, "device_id": "B1234567" }, "created_at": "2026-03-02T07:59:58.000Z", "updated_at": "2026-03-02T07:59:58.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # List operation tokens `GET https://api.parklio.com/v2/operation-tokens` Lists operation tokens. `status` becomes `expired` (checked hourly) after `end_time`, `blocked` when you block it, and `invalid` when the product is removed or paired again. `used_count` doesn't count uses over Bluetooth. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `product_id`, `status`, `date_from`, `date_to`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "product_id": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "status": "active", "used_count": 0, "is_unlimited": 0, "token_close": { "challenge_key": "string", "status_key": "string", "token": "string" }, "token_open": { "challenge_key": "string", "status_key": "string", "token": "string" }, "token_status": { "challenge_key": "string", "status_key": "string", "token": "string" }, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": { "device_id": "B123456", "id": 1 } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get an operation token `GET https://api.parklio.com/v2/operation-tokens/{id}` Returns one operation token. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The operation token's `id`. | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "product_id": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "status": "active", "used_count": 0, "is_unlimited": 0, "token_close": { "challenge_key": "string", "status_key": "string", "token": "string" }, "token_open": { "challenge_key": "string", "status_key": "string", "token": "string" }, "token_status": { "challenge_key": "string", "status_key": "string", "token": "string" }, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": { "device_id": "B123456", "id": 1 } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Block an operation token `PATCH https://api.parklio.com/v2/operation-tokens/{id}` Marks a token `blocked`. The only accepted body is `{"status": "blocked"}`. **This doesn't reach the device:** a copy your app already holds keeps working until `end_time`. Keep windows short instead. See [Operation tokens for your app](/guides/mobile-app-operation-tokens#2-keep-windows-short). **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The operation token's `id`. | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | string (`blocked`) | yes | Change the status of the operation token | ```json { "status": "blocked" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 9001, "product_id": 123, "start_time": "2026-03-02T08:00:00.000Z", "end_time": "2026-03-02T10:00:00.000Z", "status": "blocked", "used_count": 0, "is_unlimited": 0, "token_open": { "token": "", "status_key": "", "challenge_key": "" }, "token_close": { "token": "", "status_key": "", "challenge_key": "" }, "token_status": { "token": "", "status_key": "", "challenge_key": "" }, "product": { "id": 123, "device_id": "B1234567" }, "created_at": "2026-03-02T07:59:58.000Z", "updated_at": "2026-03-02T08:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete operation tokens `DELETE https://api.parklio.com/v2/operation-tokens/{id}` This endpoint allows the user to delete operation tokens by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Download operation keys sample CSV file `GET https://api.parklio.com/v2/operation-keys/sample/export` This endpoint provides a sample CSV file formatted for creating new operation keys. Use this file as a template for uploading operation keys. **Auth:** `Authorization: Bearer ` ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Create new operation keys from a CSV file `POST https://api.parklio.com/v2/operation-keys/file/upload` **Auth:** `Authorization: Bearer ` ## Request body `multipart/form-data` with a `file` field (CSV). ## Responses ### 200 ```json { "success": true, "data": { "message": "string" } } ``` --- # Create single operation keys `POST https://api.parklio.com/v2/operation-keys/single` This endpoint allows the user to create single operation keys **Auth:** none (public) ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `start_time` | string | yes | | | `end_time` | string | no | | | `product_id` | number | yes | | | `receiver_email` | string | yes | | | `is_unlimited` | number | no | | ```json { "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "product_id": 1, "receiver_email": "john.doe@example.com", "is_unlimited": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "name": "John Doe", "product_id": 1, "sender_id": 1, "operation_token_id": 0, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receiver": { "id": 2, "email": "jane.doe1@example.com", "name": "Jane Doe" }, "sender": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" }, "product": { "device_id": "B123456", "latitude": 43.52652359, "longitude": 16.450813293, "product_type_id": 1, "lot_id": 1, "zone_id": 1, "id": 1 }, "operation_token": { "id": 1, "product_id": 1, "start_time": {}, "end_time": {}, "status": "active", "used_count": 0, "is_unlimited": 1, "token_close": { "token": "string", "status_key": "string", "challenge_key": "string" }, "token_open": { "token": "string", "status_key": "string", "challenge_key": "string" }, "token_status": { "token": "string", "status_key": "string", "challenge_key": "string" }, "deleted_at": null, "created_at": {}, "updated_at": {} } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Create operations keys `POST https://api.parklio.com/v2/operation-keys` This endpoint allows the user to create operations keys **Auth:** none (public) ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `start_time` | string | yes | | | `end_time` | string | no | | | `products` | string[] | yes | | | `receiver_emails` | string[] | yes | | | `is_unlimited` | number | no | | ```json { "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "products": [ "string" ], "receiver_emails": [ "john.doe1@example.com", "john.doe2@example.com", "john.doe3@example.com" ], "is_unlimited": 0 } ``` ## Responses ### 200 — Successful response ```json { "email": "example@example.com", "invited": true } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all operation keys `GET https://api.parklio.com/v2/operation-keys` This endpoint allows the user to list all operation keys **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `date_from`, `date_to`, `lot_id`, `product_id`, `receiver_email`, `receiver_id`, `sender_email`, `sender_id`, `status`, `zone_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "name": "John Doe", "product_id": 1, "sender_id": 1, "operation_token_id": 0, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receiver": { "id": 2, "email": "jane.doe1@example.com", "name": "Jane Doe" }, "sender": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" }, "product": { "device_id": "B123456", "latitude": 43.52652359, "longitude": 16.450813293, "product_type_id": 1, "lot_id": 1, "zone_id": 1, "id": 1 }, "operation_token": { "id": 1, "product_id": 1, "start_time": {}, "end_time": {}, "status": "active", "used_count": 0, "is_unlimited": 1, "token_close": { "token": "string", "status_key": "string", "challenge_key": "string" }, "token_open": { "token": "string", "status_key": "string", "challenge_key": "string" }, "token_status": { "token": "string", "status_key": "string", "challenge_key": "string" }, "deleted_at": null, "created_at": {}, "updated_at": {} } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get operation keys `GET https://api.parklio.com/v2/operation-keys/{id}` This endpoint allows the user to get operation keys by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "name": "John Doe", "product_id": 1, "sender_id": 1, "operation_token_id": 0, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receiver": { "id": 2, "email": "jane.doe1@example.com", "name": "Jane Doe" }, "sender": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" }, "product": { "device_id": "B123456", "latitude": 43.52652359, "longitude": 16.450813293, "product_type_id": 1, "lot_id": 1, "zone_id": 1, "id": 1 }, "operation_token": { "id": 1, "product_id": 1, "start_time": {}, "end_time": {}, "status": "active", "used_count": 0, "is_unlimited": 1, "token_close": { "token": "string", "status_key": "string", "challenge_key": "string" }, "token_open": { "token": "string", "status_key": "string", "challenge_key": "string" }, "token_status": { "token": "string", "status_key": "string", "challenge_key": "string" }, "deleted_at": null, "created_at": {}, "updated_at": {} } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update operation keys `PATCH https://api.parklio.com/v2/operation-keys/{id}` This endpoint allows the user to update operation keys by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | The new name for the Operation Key | ```json { "name": "New opertion key name" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "name": "John Doe", "product_id": 1, "sender_id": 1, "operation_token_id": 0, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receiver": { "id": 2, "email": "jane.doe1@example.com", "name": "Jane Doe" }, "sender": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" }, "product": { "device_id": "B123456", "latitude": 43.52652359, "longitude": 16.450813293, "product_type_id": 1, "lot_id": 1, "zone_id": 1, "id": 1 }, "operation_token": { "id": 1, "product_id": 1, "start_time": {}, "end_time": {}, "status": "active", "used_count": 0, "is_unlimited": 1, "token_close": { "token": "string", "status_key": "string", "challenge_key": "string" }, "token_open": { "token": "string", "status_key": "string", "challenge_key": "string" }, "token_status": { "token": "string", "status_key": "string", "challenge_key": "string" }, "deleted_at": null, "created_at": {}, "updated_at": {} } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete operation keys `DELETE https://api.parklio.com/v2/operation-keys/{id}` This endpoint allows the user to delete operation keys by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get all key logs `GET https://api.parklio.com/v2/key-logs` This endpoint allows the user to list all key logs **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `date_from`, `date_to`, `lot_id`, `name`, `product_id`, `receiver_identifier`, `status`, `zone_id`. See [Filtering](/api/conventions#filtering). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "product": { "product_error": { "product": { "id": 1, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 1, "zone_id": 1, "product_type_id": 1, "status": "online" }, "id": 1, "error_code": "string", "jammed": 1, "sensor_dirty": 1, "pin_broken": 1, "rtcc_invalid": 1, "master_key_not_set": 1, "deleted_at": null, "created_at": {}, "updated_at": {} }, "id": 1, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 1, "zone_id": 1, "product_type_id": 1, "hardware_version": "1.0.0", "firmware_version": "1.0.0.", "latitude": 1, "longitude": 1, "status": "online", "advertisement_data": "string", "battery_status": 1, "product_error_id": 1, "rssi": -80, "created_at": {}, "updated_at": {}, "barrier": { "id": 1, "product_id": 1, "state": "opened", "is_autoclose": 1, "is_car_on_top": 0, "autoclose_time": 45, "created_at": {}, "updated_at": {} }, "gate": { "id": 1, "product_id": 2, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} }, "chain": { "id": 1, "product_id": 3, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} }, "bollard": { "id": 1, "product_id": 4, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} }, "brain": { "id": 1, "product_id": 5, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} } }, "operation_token": { "id": 1, "product_id": 1, "start_time": {}, "end_time": {}, "status": "string", "used_count": 1, "is_unlimited": 1, "token_close": "any", "token_open": "any", "token_status": "any", "deleted_at": null, "created_at": {}, "updated_at": {} }, "sender": { "id": 1, "account_id": 1, "account_user_role_id": 1, "email": "john.doe@example.com", "name": "John Doe", "phone": "string", "deleted_at": null, "created_at": {}, "updated_at": {} }, "id": 0, "name": "John Doe", "sender_id": 0, "product_id": 0, "operation_token_id": 0, "receiver_identifier": "john.doe@example.com", "receiver_identifier_type": "email", "deleted_at": { "deleted_at": null }, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk update key logs `PATCH https://api.parklio.com/v2/key-logs` This endpoint allows the user to bulk update key logs by using their ids **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | | | `status` | string (`active`, `reactived`, `blocked`, `invalid`, `deleted`, `depleted`, `expired`) | yes | | ```json { "ids": [ 1, 2, 3 ], "status": "blocked" } ``` ## Responses ### 200 — Successful response ```json { "success": true, "data": [ { "success": true, "id": 1 } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get all key invitation receiver emails `GET https://api.parklio.com/v2/key-logs/receiver-emails` This endpoint allows the user to list all key invitation receiver emails **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "receiver_identifier": "john.doe@example.com" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get key logs `GET https://api.parklio.com/v2/key-logs/{id}` This endpoint allows the user to get key logs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "product": { "product_error": { "product": { "id": 1, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 1, "zone_id": 1, "product_type_id": 1, "status": "online" }, "id": 1, "error_code": "string", "jammed": 1, "sensor_dirty": 1, "pin_broken": 1, "rtcc_invalid": 1, "master_key_not_set": 1, "deleted_at": null, "created_at": {}, "updated_at": {} }, "id": 1, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 1, "zone_id": 1, "product_type_id": 1, "hardware_version": "1.0.0", "firmware_version": "1.0.0.", "latitude": 1, "longitude": 1, "status": "online", "advertisement_data": "string", "battery_status": 1, "product_error_id": 1, "rssi": -80, "created_at": {}, "updated_at": {}, "barrier": { "id": 1, "product_id": 1, "state": "opened", "is_autoclose": 1, "is_car_on_top": 0, "autoclose_time": 45, "created_at": {}, "updated_at": {} }, "gate": { "id": 1, "product_id": 2, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} }, "chain": { "id": 1, "product_id": 3, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} }, "bollard": { "id": 1, "product_id": 4, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} }, "brain": { "id": 1, "product_id": 5, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} } }, "operation_token": { "id": 1, "product_id": 1, "start_time": {}, "end_time": {}, "status": "string", "used_count": 1, "is_unlimited": 1, "token_close": "any", "token_open": "any", "token_status": "any", "deleted_at": null, "created_at": {}, "updated_at": {} }, "sender": { "id": 1, "account_id": 1, "account_user_role_id": 1, "email": "john.doe@example.com", "name": "John Doe", "phone": "string", "deleted_at": null, "created_at": {}, "updated_at": {} }, "id": 0, "name": "John Doe", "sender_id": 0, "product_id": 0, "operation_token_id": 0, "receiver_identifier": "john.doe@example.com", "receiver_identifier_type": "email", "deleted_at": { "deleted_at": null }, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update key logs `PATCH https://api.parklio.com/v2/key-logs/{id}` This endpoint allows the user to update key logs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | Change the name of the key log | | `status` | string (`active`, `reactived`, `blocked`, `invalid`, `deleted`, `depleted`, `expired`) | no | Change the status of the sent key | ```json { "name": "Key Log Name", "status": "blocked" } ``` ## Responses ### 200 ```json { "success": true, "data": { "product": { "product_error": { "product": { "id": 1, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 1, "zone_id": 1, "product_type_id": 1, "status": "online" }, "id": 1, "error_code": "string", "jammed": 1, "sensor_dirty": 1, "pin_broken": 1, "rtcc_invalid": 1, "master_key_not_set": 1, "deleted_at": null, "created_at": {}, "updated_at": {} }, "id": 1, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 1, "zone_id": 1, "product_type_id": 1, "hardware_version": "1.0.0", "firmware_version": "1.0.0.", "latitude": 1, "longitude": 1, "status": "online", "advertisement_data": "string", "battery_status": 1, "product_error_id": 1, "rssi": -80, "created_at": {}, "updated_at": {}, "barrier": { "id": 1, "product_id": 1, "state": "opened", "is_autoclose": 1, "is_car_on_top": 0, "autoclose_time": 45, "created_at": {}, "updated_at": {} }, "gate": { "id": 1, "product_id": 2, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} }, "chain": { "id": 1, "product_id": 3, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} }, "bollard": { "id": 1, "product_id": 4, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} }, "brain": { "id": 1, "product_id": 5, "state": "opened", "permanent_position": "disabled", "deleted_at": null, "created_at": {}, "updated_at": {} } }, "operation_token": { "id": 1, "product_id": 1, "start_time": {}, "end_time": {}, "status": "string", "used_count": 1, "is_unlimited": 1, "token_close": "any", "token_open": "any", "token_status": "any", "deleted_at": null, "created_at": {}, "updated_at": {} }, "sender": { "id": 1, "account_id": 1, "account_user_role_id": 1, "email": "john.doe@example.com", "name": "John Doe", "phone": "string", "deleted_at": null, "created_at": {}, "updated_at": {} }, "id": 0, "name": "John Doe", "sender_id": 0, "product_id": 0, "operation_token_id": 0, "receiver_identifier": "john.doe@example.com", "receiver_identifier_type": "email", "deleted_at": { "deleted_at": null }, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete key logs `DELETE https://api.parklio.com/v2/key-logs/{id}` This endpoint allows the user to delete key logs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Resend key logs `POST https://api.parklio.com/v2/key-logs/resend` Resend key logs **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 — Successful response ```json { "success": true, "data": [ { "success": true, "id": 1 } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get all key invitations `GET https://api.parklio.com/v2/key-invitations` This endpoint allows the user to list all key invitations **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `product_id`, `status`, `receiver_identifier`, `date_from`, `date_to`, `lot_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `status`, `receiver_identifier`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "name": "John Doe", "sender_id": 0, "product_id": 0, "receiver_identifier": "john.doe@example.com", "receiver_identifier_type": "email", "status": "activated", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "is_unlimited": 0, "deleted_at": null, "activated_at": "2026-01-15T09:30:00.000Z", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": null, "sender": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Bulk update key invitations `PATCH https://api.parklio.com/v2/key-invitations` This endpoint allows the user to bulk update key invitations by using their ids **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | | | `status` | string (`pending`, `reactived`, `blocked`, `invalid`, `deleted`, `expired`, `activated`) | yes | | ```json { "ids": [ 1, 2, 3 ], "status": "blocked" } ``` ## Responses ### 200 — Successful response ```json { "success": true, "data": [ { "success": true, "id": 1 } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all key log receiver emails `GET https://api.parklio.com/v2/key-invitations/receiver-emails` This endpoint allows the user to list all key log receiver emails **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "receiver_identifier": "john.doe@example.com" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get key invitations `GET https://api.parklio.com/v2/key-invitations/{id}` This endpoint allows the user to get key invitations by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "name": "John Doe", "sender_id": 0, "product_id": 0, "receiver_identifier": "john.doe@example.com", "receiver_identifier_type": "email", "status": "activated", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "is_unlimited": 0, "deleted_at": null, "activated_at": "2026-01-15T09:30:00.000Z", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": null, "sender": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update key invitations `PATCH https://api.parklio.com/v2/key-invitations/{id}` This endpoint allows the user to update key invitations by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | The new name of the Operation Token | | `status` | string (`pending`, `reactived`, `blocked`, `invalid`, `deleted`, `expired`, `activated`) | yes | The new status of the invite | ```json { "name": "New operation token name", "status": "blocked" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "name": "John Doe", "sender_id": 0, "product_id": 0, "receiver_identifier": "john.doe@example.com", "receiver_identifier_type": "email", "status": "activated", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "is_unlimited": 0, "deleted_at": null, "activated_at": "2026-01-15T09:30:00.000Z", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": null, "sender": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete key invitations `DELETE https://api.parklio.com/v2/key-invitations/{id}` This endpoint allows the user to delete key invitations by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Resend key invitations `POST https://api.parklio.com/v2/key-invitations/resend` This endpoint allows the user to resend key invitations by ids **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 — Successful response ```json { "success": true, "data": [ { "success": true, "id": 1 } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Create weblinks `POST https://api.parklio.com/v2/weblinks` Emails a link that lets the receiver operate products from a browser, through the cloud and a Gateway, during a time window. One weblink is created per product. - **Parklio always sends the email** to `receiver_email`; you can't send the link yourself. - `start_time` and `end_time` are both required. `max_uses` defaults to 1; `null` means unlimited. - Revocation is immediate. The response has one item per product; check `success` on each. See [Access and keys](/concepts/access-and-keys#weblinks). **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `products` | number[] | yes | List of product IDs to create weblinks for | | `receiver_email` | string | yes | Email address of the weblink recipient | | `start_time` | string | yes | ISO 8601 datetime from which the weblink becomes valid | | `end_time` | string | yes | ISO 8601 datetime at which the weblink expires | | `max_uses` | number | no | Maximum number of times the weblink can be used. Defaults to 1 if not provided. Set to null for unlimited uses. | ```json { "products": [ 5, 10, 15 ], "receiver_email": "john.doe@example.com", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "max_uses": 10 } ``` ## Responses ### 200 ```json { "success": true, "data": [ { "success": true, "product_id": 123, "receiver_email": "guest@example.com", "product_name": "Entrance barrier", "url": "" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all weblinks `GET https://api.parklio.com/v2/weblinks` This endpoint allows the user to list all weblinks **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `product_id`, `receiver_identifier`, `receiver_identifier_type`, `date_from`, `date_to`, `status`, `lot_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "product_id": 5, "sender_id": 3, "operation_token_id": 7, "receiver_identifier": "jane.doe@example.com", "receiver_identifier_type": "email", "url": "string", "max_uses": 5, "used_count": 3, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": { "id": 1, "device_id": "B123456", "name": "Barrier 1", "lot_id": 3, "zone_id": 2, "power_type": "battery", "product_type_id": 1, "hardware_version": "1.0.0", "firmware_version": "2.3.1", "latitude": 43.52652359, "longitude": 16.450813293, "status": "online", "advertisement_data": "string", "battery_status": 87, "product_error_id": null, "rssi": -88, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "sender": { "id": 1, "account_user_role_id": 2, "account_id": 5, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "operation_token": { "id": 1, "product_id": 5, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "status": "active", "used_count": 3, "is_unlimited": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "token_close": "a1b2c3d4e5f6...", "token_open": "f6e5d4c3b2a1...", "token_status": "active" } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Bulk update weblinks `PATCH https://api.parklio.com/v2/weblinks` This endpoint allows the user to bulk update weblinks by using their ids **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | string[] | yes | | | `status` | string (`active`, `reactived`, `blocked`, `invalid`, `deleted`, `depleted`, `expired`) | yes | | ```json { "ids": "[1,2,3]", "status": "blocked" } ``` ## Responses ### 200 ```json { "success": true, "data": { "ids": "[1,2,3]", "status": "blocked" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get weblinks metadata `GET https://api.parklio.com/v2/weblinks/metadata` This endpoint allows the user to get weblinks metadata. **Auth:** none (public) ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `token` | string | no | | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "product_id": 0, "sender_id": 0, "operation_token_id": 0, "receiver_identifier": "jane.doe@example.com", "receiver_identifier_type": "email", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": { "id": 1, "device_id": "B123456", "name": "Barrier 1", "lot_id": 3, "zone_id": 2, "power_type": "battery", "product_type_id": 1, "hardware_version": "1.0.0", "firmware_version": "2.3.1", "latitude": 43.52652359, "longitude": 16.450813293, "status": "online", "advertisement_data": "string", "battery_status": 87, "product_error_id": null, "rssi": -88, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "sender": { "id": 1, "account_user_role_id": 2, "account_id": 5, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User is trying to use a product weblink that has expired or has a wrong signature. User should request a new product weblink. ```json { "success": false, "errors": [ { "code": 212, "message": "Product weblink not valid." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get weblinks `GET https://api.parklio.com/v2/weblinks/{id}` This endpoint allows the user to get weblinks by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "product_id": 5, "sender_id": 3, "operation_token_id": 7, "receiver_identifier": "jane.doe@example.com", "receiver_identifier_type": "email", "url": "string", "max_uses": 5, "used_count": 3, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": { "id": 1, "device_id": "B123456", "name": "Barrier 1", "lot_id": 3, "zone_id": 2, "power_type": "battery", "product_type_id": 1, "hardware_version": "1.0.0", "firmware_version": "2.3.1", "latitude": 43.52652359, "longitude": 16.450813293, "status": "online", "advertisement_data": "string", "battery_status": 87, "product_error_id": null, "rssi": -88, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "sender": { "id": 1, "account_user_role_id": 2, "account_id": 5, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "operation_token": { "id": 1, "product_id": 5, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "status": "active", "used_count": 3, "is_unlimited": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "token_close": "a1b2c3d4e5f6...", "token_open": "f6e5d4c3b2a1...", "token_status": "active" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update weblinks `PATCH https://api.parklio.com/v2/weblinks/{id}` This endpoint allows the user to update weblinks by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `status` | string (`active`, `reactived`, `blocked`, `invalid`, `deleted`, `depleted`, `expired`) | yes | | ```json { "status": "blocked" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "product_id": 5, "sender_id": 3, "operation_token_id": 7, "receiver_identifier": "jane.doe@example.com", "receiver_identifier_type": "email", "url": "string", "max_uses": 5, "used_count": 3, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": { "id": 1, "device_id": "B123456", "name": "Barrier 1", "lot_id": 3, "zone_id": 2, "power_type": "battery", "product_type_id": 1, "hardware_version": "1.0.0", "firmware_version": "2.3.1", "latitude": 43.52652359, "longitude": 16.450813293, "status": "online", "advertisement_data": "string", "battery_status": 87, "product_error_id": null, "rssi": -88, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "sender": { "id": 1, "account_user_role_id": 2, "account_id": 5, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "operation_token": { "id": 1, "product_id": 5, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "status": "active", "used_count": 3, "is_unlimited": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "token_close": "a1b2c3d4e5f6...", "token_open": "f6e5d4c3b2a1...", "token_status": "active" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete weblinks `DELETE https://api.parklio.com/v2/weblinks/{id}` This endpoint allows the user to delete weblinks by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Resend weblinks `POST https://api.parklio.com/v2/weblinks/resend` This endpoint allows the user to resend weblinks by ids **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | string[] | yes | | ```json { "ids": "[1,2,3]" } ``` ## Responses ### 200 ```json { "success": true, "data": { "success": true, "id": 1 } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Operate product with the weblink `POST https://api.parklio.com/v2/weblinks/{id}/operation` Use the weblink to operate a Parklio™ product **Auth:** none (public) ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `token` | string | yes | | | `operation` | string (`open`, `force_close`, `close`, `stay_open`, `stay_close`, `clear_stay_open`, `clear_stay_close`) | yes | | ```json { "token": "string", "operation": "open" } ``` ## Responses ### 200 ```json { "success": true, "data": { "success": true } } ``` ### 403 — Product operation was not emitted due to no rssi signal. Gateway does not have a good enough signal with the product. ```json { "success": false, "errors": [ { "code": 704, "message": "No rssi signal." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Create qr codes `POST https://api.parklio.com/v2/qr-codes` This endpoint allows the user to create qr codes **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `product_id` | number | yes | ID of the product to associate with this QR code | | `start_time` | string | yes | The date and time from which the QR code becomes active, in ISO 8601 format | | `end_time` | string | no | The date and time at which the QR code expires, in ISO 8601 format. If omitted, the QR code does not expire | | `timezone` | string | yes | Timezone offset used to interpret the start and end times, in ±HH:MM format | | `description` | string | no | Optional description explaining the purpose of the QR code. Max 255 characters | ```json { "product_id": 1, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "timezone": "+01:00", "description": "Guest parking access for Building A" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "account_user_id": 1, "product_id": 1, "device_id": "B123456", "uuid": "2b26e2e4-51c5-4d29-bb2b-12762245e9b3", "token": "PGiTTPbwYSwQoJYETV7B5SYc710C2RjQwpGUMrKT0Qf4IS2RUFT7yskcfeyXyKmX", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "description": "Guest parking access for Building A", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all qr codes `GET https://api.parklio.com/v2/qr-codes` This endpoint allows the user to list all qr codes **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_user_id`, `product_id`, `lot_id`, `zone_id`, `start_time`, `end_time`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "account_id": 1, "account_user_id": 1, "product_id": 1, "device_id": "B123456", "uuid": "2b26e2e4-51c5-4d29-bb2b-12762245e9b3", "token": "PGiTTPbwYSwQoJYETV7B5SYc710C2RjQwpGUMrKT0Qf4IS2RUFT7yskcfeyXyKmX", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "description": "Guest parking access for Building A", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get qr codes `GET https://api.parklio.com/v2/qr-codes/{id}` This endpoint allows the user to get qr codes by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_id": 1, "account_user_id": 1, "product_id": 1, "device_id": "B123456", "uuid": "2b26e2e4-51c5-4d29-bb2b-12762245e9b3", "token": "PGiTTPbwYSwQoJYETV7B5SYc710C2RjQwpGUMrKT0Qf4IS2RUFT7yskcfeyXyKmX", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "description": "Guest parking access for Building A", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Delete qr codes `DELETE https://api.parklio.com/v2/qr-codes/{id}` This endpoint allows the user to delete qr codes by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Render a QR code image `GET https://api.parklio.com/v2/qr-codes/render/{uuid}` Generates and streams a JPEG image of the QR code identified by uuid, rendered with an optional timezone offset used for any time-based content in the code. **Auth:** none (public) ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `uuid` | string | yes | | ## Responses ### 200 --- # Bulk delete qr codes `DELETE https://api.parklio.com/v2/qr-codes/bulk` This endpoint allows the user to bulk delete qr codes by using their ids **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | List of QR code IDs to delete. Must contain between 1 and 30 IDs. | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create a member `POST https://api.parklio.com/v2/members` Registers a vehicle's license plate as a **member** of the account, optionally in groups. Groups decide which [tariffs](/api/endpoints/tariffs-create) apply to a plate; members don't open anything by themselves. See the [Sessions and tariffs](/guides/sessions-and-tariffs#4-members-and-groups) guide. **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `description` | string | no | | | `email` | string | no | | | `license_plate` | string | yes | | | `group_ids` | number[] | no | | ```json { "name": "Default Member", "description": "Default Description", "email": "Default Email", "license_plate": "Default license plate", "group_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 40, "name": "Tenant 12", "description": null, "email": null, "license_plate": "ZG1234AB", "account_id": 3, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "groups": [ { "id": 2, "name": "Tenants" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all members `GET https://api.parklio.com/v2/members` This endpoint allows the user to list all members **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `name`, `license_plate`, `email`, `created_at`, `group_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "name": "John Doe", "description": "User description", "email": "john.doe@mail.com", "license_plate": "ST1950TH", "account_id": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "groups": [ { "id": 1, "name": "Some Group" } ] } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Create new members from a CSV file `POST https://api.parklio.com/v2/members/upload` **Auth:** `Authorization: Bearer ` ## Request body `multipart/form-data` with a `file` field (CSV). ## Responses ### 200 ```json { "success": true, "data": { "message": "string" } } ``` --- # Download members sample CSV file `GET https://api.parklio.com/v2/members/sample/export` This endpoint provides a sample CSV file formatted for creating new members. Use this file as a template for uploading members. **Auth:** `Authorization: Bearer ` ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get members `GET https://api.parklio.com/v2/members/{id}` This endpoint allows the user to get members by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "John Doe", "description": "User description", "email": "john.doe@mail.com", "license_plate": "ST1950TH", "account_id": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "groups": [ { "id": 1, "name": "Some Group" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Replace members `PUT https://api.parklio.com/v2/members/{id}` This endpoint allows the user to replace members by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | | | `description` | string | yes | | | `email` | string | yes | | | `license_plate` | string | yes | | | `group_ids` | number[] | yes | | ```json { "name": "Default Member", "description": "Default Description", "email": "Default Email", "license_plate": "Default license plate", "group_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "John Doe", "description": "User description", "email": "john.doe@mail.com", "license_plate": "ST1950TH", "account_id": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "groups": [ { "id": 1, "name": "Some Group" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Update members `PATCH https://api.parklio.com/v2/members/{id}` This endpoint allows the user to update members by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `description` | string | no | | | `email` | string | no | | | `license_plate` | string | no | | ```json { "name": "Default Member", "description": "Default Description", "email": "Default Email", "license_plate": "Default license plate" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "John Doe", "description": "User description", "email": "john.doe@mail.com", "license_plate": "ST1950TH", "account_id": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "groups": [ { "id": 1, "name": "Some Group" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete members `DELETE https://api.parklio.com/v2/members/{id}` This endpoint allows the user to delete members by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create groups `POST https://api.parklio.com/v2/groups` This endpoint allows the user to create groups **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | | | `description` | string | no | | | `member_ids` | number[] | no | | | `tariff_ids` | number[] | no | | ```json { "name": "Default Group", "description": "Default Description", "member_ids": [ 1, 2, 3 ], "tariff_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all groups `GET https://api.parklio.com/v2/groups` This endpoint allows the user to list all groups **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `name`, `created_at`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get groups `GET https://api.parklio.com/v2/groups/{id}` This endpoint allows the user to get groups by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Replace groups `PUT https://api.parklio.com/v2/groups/{id}` This endpoint allows the user to replace groups by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | | | `description` | string | yes | | | `member_ids` | number[] | yes | | | `tariff_ids` | number[] | yes | | ```json { "name": "Default Group", "description": "Default Description", "member_ids": [ 1, 2, 3 ], "tariff_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Update groups `PATCH https://api.parklio.com/v2/groups/{id}` This endpoint allows the user to update groups by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `description` | string | no | | ```json { "name": "Default Group", "description": "Default Description" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete groups `DELETE https://api.parklio.com/v2/groups/{id}` This endpoint allows the user to delete groups by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Assign group members `PUT https://api.parklio.com/v2/groups/{id}/members/{memberId}` This endpoint allows the user to assign group members by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | | `memberId` | number | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Unassign group members `DELETE https://api.parklio.com/v2/groups/{id}/members/{memberId}` This endpoint allows the user to unassign group members by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `memberId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk assign group members `POST https://api.parklio.com/v2/groups/{id}/members` This endpoint allows the user to bulk assign group members by using their ids **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | ```json {} ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk unassign group members `DELETE https://api.parklio.com/v2/groups/{id}/members` This endpoint allows the user to bulk unassign group members by using their ids **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | ```json {} ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Replace group members `PUT https://api.parklio.com/v2/groups/{id}/members` This endpoint allows the user to replace group members by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | ```json {} ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Group name", "description": "Group description", "account_id": 1, "members": [ { "id": 1, "name": "Member" } ], "tariffs": [ { "id": 1, "name": "Basic Tariff" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Create lot whitelist entries `POST https://api.parklio.com/v2/lot-whitelists` Adds up to 20 license plates to a lot's whitelist for a time window. While an entry is active, LPR cameras on the lot let the plate in (`access_decision` `triggered`, reason `whitelist`), and it can open [bound products](/api/endpoints/products-bind-license-for-product). - Always send `lot_id`. - Send **either** `end_time` (in the future) **or** `"is_unlimited": true`, not both. - Plates are 3–16 characters, stored in uppercase without hyphens. - The response has one item per plate. Keep each `license_id` to bind the plate to products, and check `success` on every item. Entries become `expired` (checked hourly) after `end_time`. See the [Plate-based access](/guides/plate-based-access) guide. **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `license_plates` | string[] | yes | | | `name` | string | no | | | `lot_id` | number | no | | | `start_time` | string | yes | | | `end_time` | string | no | | | `is_unlimited` | boolean | no | | ```json { "license_plates": [ "DT-PLATE1", "DT-PLATE2", "DT-PLATE3" ], "name": "string", "lot_id": 1, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "is_unlimited": false } ``` ## Responses ### 200 ```json { "success": true, "data": [ { "success": true, "whitelist_id": 501, "license_id": 88, "plate": "ZG1234AB", "is_unlimited": 0 }, { "success": true, "whitelist_id": 502, "license_id": 89, "plate": "ST555CD", "is_unlimited": 0 } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User is trying to create an entity where end time is before start time. ```json { "success": false, "errors": [ { "code": 405, "message": "End time is before start time." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # List lot whitelist entries `GET https://api.parklio.com/v2/lot-whitelists` Lists whitelist entries, with each plate's `license_plate_id` (the `license_id` to bind) and the products it's bound to. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `end_time`, `license_plate`, `lot_id`, `name`, `start_time`, `status`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "account_user_id": 1, "lot_id": 1, "license_plate": "DT-PLATE2", "license_plate_id": 1, "name": "John Doe", "status": "active", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "is_unlimited": 0, "bound_products": [ { "id": 1, "device_id": "L123456789", "name": "Main Gate" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get lot whitelists `GET https://api.parklio.com/v2/lot-whitelists/{id}` This endpoint allows the user to get lot whitelists by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_user_id": 1, "lot_id": 1, "license_plate": "DT-PLATE2", "license_plate_id": 1, "name": "John Doe", "status": "active", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "is_unlimited": 0, "bound_products": [ { "id": 1, "device_id": "L123456789", "name": "Main Gate" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update lot whitelists `PATCH https://api.parklio.com/v2/lot-whitelists/{id}` This endpoint allows the user to update lot whitelists by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `start_time` | string | no | | | `end_time` | string | no | | | `lot_id` | number | no | | ```json { "name": "John Doe", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "lot_id": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_user_id": 1, "lot_id": 1, "license_plate": "DT-PLATE2", "license_plate_id": 1, "name": "John Doe", "status": "active", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "is_unlimited": 0, "bound_products": [ { "id": 1, "device_id": "L123456789", "name": "Main Gate" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Delete a lot whitelist entry `DELETE https://api.parklio.com/v2/lot-whitelists/{id}` Removes a plate's whitelist entry, so the plate no longer gets in through the whitelist. Its product bindings stay, and apply again if the plate is whitelisted later. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The entry's `id` (`whitelist_id` in the create response). | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create new lot whitelists from a CSV file `POST https://api.parklio.com/v2/lot-whitelists/upload/lot/{id}` **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body `multipart/form-data` with a `file` field (CSV). ## Responses ### 200 ```json { "success": true, "data": { "message": "string" } } ``` --- # Get lot whitelists CSV file `GET https://api.parklio.com/v2/lot-whitelists/export` This endpoint allows the user to get the lot whitelists CSV file **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `start_time`, `end_time`, `lot_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Download lot whitelists sample CSV file `GET https://api.parklio.com/v2/lot-whitelists/sample/export` This endpoint provides a sample CSV file formatted for creating new lot whitelists. Use this file as a template for uploading lot whitelists. **Auth:** `Authorization: Bearer ` ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Bulk delete lot whitelists `DELETE https://api.parklio.com/v2/lot-whitelists/bulk` This endpoint allows the user to bulk delete lot whitelists by using their ids **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 --- # List plate readings `GET https://api.parklio.com/v2/plate-readings` Lists license-plate reads by LPR cameras, with the access decision made for each: `access_decision` (`triggered` or `denied`) and `access_decision_reason`. The same data arrives in realtime as `plate_reading_create` and `plate_reading_update`. See the [Plate-based access](/guides/plate-based-access) guide. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `access_decision`, `access_decision_reason`, `account_id`, `plate`, `zone_id`, `lpr_camera_id`, `direction`, `type`, `created_at`, `lot_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `created_at`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "data": [ { "id": 9001, "plate": "ZG1234AB", "confidence": null, "image_url": null, "cropped_image_url": null, "direction": "in", "type": "car", "make": null, "color": null, "model": null, "country_code": "HR", "session_id": null, "access_decision": "triggered", "access_decision_reason": "whitelist", "plate_side": "front", "lpr_camera_direction": "in", "lpr_camera_id": 15, "zone_id": 9, "created_at": "2026-03-02T08:12:40.000Z", "updated_at": "2026-03-02T08:12:41.000Z" } ], "meta": { "total": 1, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=1", "last_page_url": "/?page=1", "next_page_url": "/?page=2", "previous_page_url": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get plate-readings `GET https://api.parklio.com/v2/plate-readings/{id}` This endpoint allows the user to get plate-readings by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "plate": "ST1950TH", "confidence": 0.9876, "image_url": "someurltoimage.jpg", "cropped_image_url": "someurltoimage.jpg", "direction": "in", "type": "car", "make": "Volkswagen", "color": "Blue", "model": "Golf", "country_code": "HR", "session_id": 123, "access_decision": "triggered", "access_decision_reason": "whitelist", "plate_side": "front", "lpr_camera_direction": "in", "lpr_camera_id": 5, "zone_id": 10, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update plate-readings `PATCH https://api.parklio.com/v2/plate-readings/{id}` This endpoint allows the user to update plate-readings by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `plate` | string | no | | ```json { "plate": "ST1950TH" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "plate": "ST1950TH", "confidence": 0.9876, "image_url": "someurltoimage.jpg", "cropped_image_url": "someurltoimage.jpg", "direction": "in", "type": "car", "make": "Volkswagen", "color": "Blue", "model": "Golf", "country_code": "HR", "session_id": 123, "access_decision": "triggered", "access_decision_reason": "whitelist", "plate_side": "front", "lpr_camera_direction": "in", "lpr_camera_id": 5, "zone_id": 10, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk create license plate alerts `POST https://api.parklio.com/v2/license-plate-alerts/{id}/receivers` This endpoint allows the user to bulk create license plate alerts **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `receiver_ids` | number[] | yes | IDs of the account users to assign as receivers of this license plate alert. | ```json { "receiver_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Blacklisted license plate", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 5, "zone": { "id": 1, "name": "Main Entrance" }, "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk put license plate alerts `PUT https://api.parklio.com/v2/license-plate-alerts/{id}/receivers` This endpoint allows the user to bulk put license plate alerts **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `receiver_ids` | number[] | yes | IDs of the account users to assign as receivers of this license plate alert. | ```json { "receiver_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Blacklisted license plate", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 5, "zone": { "id": 1, "name": "Main Entrance" }, "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk unassign license plate alert receivers `DELETE https://api.parklio.com/v2/license-plate-alerts/{id}/receivers` This endpoint allows the user to bulk unassign license plate alert receivers by using their ids **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `receiver_ids` | number[] | yes | IDs of the account users to unassign as receivers from this license plate alert. | ```json { "receiver_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Blacklisted license plate", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 5, "zone": { "id": 1, "name": "Main Entrance" }, "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Put license plate alerts receivers `PUT https://api.parklio.com/v2/license-plate-alerts/{alertId}/receivers/{receiverId}` This endpoint allows the user to put license plate alerts receivers by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `alertId` | string | yes | | | `receiverId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Blacklisted license plate", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 5, "zone": { "id": 1, "name": "Main Entrance" }, "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Put license plate alerts `PUT https://api.parklio.com/v2/license-plate-alerts/{id}` This endpoint allows the user to put license plate alerts by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `zone_id` | number | yes | Identifier of the zone this license plate alert belongs to. | | `receiver_ids` | number[] | yes | IDs of the account users who should receive email notifications when this license plate alert is triggered. | | `name` | string | yes | Descriptive name given to this license plate alert. | | `plates` | string[] | yes | License plate numbers that trigger this alert. | | `min_minutes_between_emails` | number | yes | Minimum number of minutes to wait between sending consecutive alert emails for this license plate. | ```json { "zone_id": 1, "receiver_ids": [ 1, 2, 3 ], "name": "Alert name", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 60 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Blacklisted license plate", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 5, "zone": { "id": 1, "name": "Main Entrance" }, "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get license plate alerts `GET https://api.parklio.com/v2/license-plate-alerts/{id}` This endpoint allows the user to get license plate alerts by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Blacklisted license plate", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 5, "zone": { "id": 1, "name": "Main Entrance" }, "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update license plate alerts `PATCH https://api.parklio.com/v2/license-plate-alerts/{id}` This endpoint allows the user to update license plate alerts by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | Descriptive name given to this license plate alert. | | `min_minutes_between_emails` | number | no | Minimum number of minutes to wait between sending consecutive alert emails for this license plate. | ```json { "name": "Blacklisted license plate", "min_minutes_between_emails": 5 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Blacklisted license plate", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 5, "zone": { "id": 1, "name": "Main Entrance" }, "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" } ] } } ``` ### 400 — Cannot be moved between different lots. ```json { "success": false, "errors": [ { "code": 1000, "message": "Cannot move to different lot." } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete license plate alerts `DELETE https://api.parklio.com/v2/license-plate-alerts/{id}` This endpoint allows the user to delete license plate alerts by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create license plate alerts `POST https://api.parklio.com/v2/license-plate-alerts` This endpoint allows the user to create license plate alerts **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `zone_id` | number | yes | Identifier of the zone this license plate alert belongs to. | | `name` | string | no | Descriptive name given to this license plate alert. | | `plates` | string[] | yes | License plate numbers that trigger this alert. | | `min_minutes_between_emails` | number | no | Minimum number of minutes to wait between sending consecutive alert emails for this license plate alert. | | `receiver_ids` | number[] | no | IDs of account users who should receive email notifications when this license plate alert is triggered. | ```json { "zone_id": 1, "name": "Blacklisted license plates", "plates": [ "DT-PLATE2", "DT-PLATE3" ], "min_minutes_between_emails": 5, "receiver_ids": [ 1, 2 ] } ``` ## Responses ### 201 --- # Get all license plate alerts `GET https://api.parklio.com/v2/license-plate-alerts` This endpoint allows the user to list all license plate alerts **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`, `plate`. See [Sorting](/api/conventions#sorting). | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `lot_id`, `name`, `plate`, `zone_id`. See [Filtering](/api/conventions#filtering). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "name": "Blacklisted license plate", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 5, "zone": { "id": 1, "name": "Main Entrance" }, "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" } ] } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Unassign license plate alert receivers `DELETE https://api.parklio.com/v2/license-plate-alerts/{id}/receivers/{reciverId}` This endpoint allows the user to unassign license plate alert receivers by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `reciverId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Blacklisted license plate", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "plates": [ "DT-PLATE2", "ST-1234-AB" ], "min_minutes_between_emails": 5, "zone": { "id": 1, "name": "Main Entrance" }, "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Start a session `POST https://api.parklio.com/v2/sessions` Starts a parking session by hand, for example for a vehicle a camera missed. The session is created in the zone of the camera you name. Send **both** `license_plate` and `lpr_camera_id`. No whitelist, tariff or capacity check is made, and the zone's `occupied_count` doesn't change. `entry.type` is `account` for an API token, `attendant` for a user token. See the [Sessions and tariffs](/guides/sessions-and-tariffs) guide. **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `license_plate` | string | no | | | `lpr_camera_id` | number | no | | ```json { "license_plate": "ST1950TH", "lpr_camera_id": 1 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 501, "public_id": "K7Q2M9X4TB", "duration": 0, "amount_paid": 0, "exit_allowed": true, "entry_license_plate": { "id": 9001, "plate": "ZG1234AB", "image_url": null, "cropped_image_url": null }, "created_at": "2026-03-02T08:12:41.000Z", "entry": { "id": 3, "type": "account" }, "zone_id": 9 } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # List sessions `GET https://api.parklio.com/v2/sessions` Lists parking sessions. Open sessions have no `ended_at`; filter them with `ended_at:eq:null`. `duration` is in seconds, `discount_time` in minutes (`-1` = unlimited). See the [Sessions and tariffs](/guides/sessions-and-tariffs) guide. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `created_at`, `ended_at`, `license_plate`, `lot_id`, `lpr_camera_id`, `zone_id`, `public_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `created_at`, `id`, `ended_at`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "data": [ { "id": 501, "public_id": "K7Q2M9X4TB", "duration": 1800, "amount_paid": 0, "exit_allowed": true, "entry_license_plate": { "id": 9001, "plate": "ZG1234AB", "image_url": null, "cropped_image_url": null }, "created_at": "2026-03-02T08:12:41.000Z", "entry": { "id": 15, "type": "lpr_camera" }, "zone_id": 9 } ], "meta": { "total": 1, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=1", "last_page_url": "/?page=1", "next_page_url": "/?page=2", "previous_page_url": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get a session `GET https://api.parklio.com/v2/sessions/{id}` Returns one session. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The session's `id`. | ## Responses ### 200 ```json { "success": true, "data": { "id": "854813550000003054", "public_id": "A1B2C3D4E5", "ended_at": "2026-01-15T09:30:00.000Z", "duration": 10800, "discount_time": 15, "amount_paid": 20, "possibly_exited": false, "exit_allowed": true, "entry_license_plate": { "plate": "DT-PLATE2", "id": 1, "image_url": "string", "cropped_image_url": "string" }, "created_at": "2026-01-15T09:30:00.000Z", "zone_id": "2", "entry": { "type": "attendant", "id": 1 }, "exit": { "type": "attendant", "id": 1 } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update sessions `PATCH https://api.parklio.com/v2/sessions/{id}` This endpoint allows the user to update sessions by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `discount_time` | number | no | | | `license_plate` | string | no | | ```json { "discount_time": 0, "license_plate": "string" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": "854813550000003054", "public_id": "A1B2C3D4E5", "ended_at": "2026-01-15T09:30:00.000Z", "duration": 10800, "discount_time": 15, "amount_paid": 20, "possibly_exited": false, "exit_allowed": true, "entry_license_plate": { "plate": "DT-PLATE2", "id": 1, "image_url": "string", "cropped_image_url": "string" }, "created_at": "2026-01-15T09:30:00.000Z", "zone_id": "2", "entry": { "type": "attendant", "id": 1 }, "exit": { "type": "attendant", "id": 1 } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Close a session `POST https://api.parklio.com/v2/sessions/{id}/close` Ends a session now: sets `ended_at`, and `exit` with type `account` (API token) or `attendant` (user token). No body. It doesn't check that the session is still open, and doesn't change the zone's `occupied_count`: check `ended_at` first. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | The session's `id`. | ## Responses ### 200 ```json { "success": true, "data": { "id": 501, "public_id": "K7Q2M9X4TB", "duration": 7200, "amount_paid": 0, "exit_allowed": false, "entry_license_plate": { "id": 9001, "plate": "ZG1234AB", "image_url": null, "cropped_image_url": null }, "created_at": "2026-03-02T08:12:41.000Z", "entry": { "id": 3, "type": "account" }, "zone_id": 9, "ended_at": "2026-03-02T10:12:41.000Z", "exit": { "id": 3, "type": "account" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get vehicle analytics `GET https://api.parklio.com/v2/sessions/vehicle-analytics` Returns paginated analytics grouped by license plate, including visit count, total/average/longest stay duration, and last seen time. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `plate`, `visits`, `total_time_seconds`, `avg_stay_seconds`, `longest_stay_seconds`, `last_seen`. See [Sorting](/api/conventions#sorting). | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `created_at`, `plate`, `lot_ids`, `zone_ids`. See [Filtering](/api/conventions#filtering). | ## Responses ### 200 — Paginated vehicle analytics, grouped by plate. ```json { "success": true, "data": [ { "plate": "DTPLATE2", "vehicle_label": "Red Peugeot 206SW", "visits": 3, "total_time_seconds": 10800, "avg_stay_seconds": 5400, "longest_stay_seconds": 7200, "last_seen": "2026-08-24T14:00:00.000Z" } ], "meta": { "total": 1, "perPage": 10, "currentPage": 1, "lastPage": 1, "firstPage": 1, "firstPageUrl": "/?page=1", "lastPageUrl": "/?page=1", "nextPageUrl": null, "previousPageUrl": null } } ``` --- # Get vehicle analytics CSV file `GET https://api.parklio.com/v2/sessions/vehicle-analytics/export` This endpoint allows the user to get the vehicle analytics CSV file **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `created_at`, `plate`, `lot_ids`, `zone_ids`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `plate`, `visits`, `total_time_seconds`, `avg_stay_seconds`, `longest_stay_seconds`, `last_seen`. See [Sorting](/api/conventions#sorting). | ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get session analytics `GET https://api.parklio.com/v2/sessions/analytics` Returns parking analytics data based on the provided filters. --- ### Metrics (`metric` filter) | Metric | Description | |--------|-------------| | `entry_count` | The total number of vehicles that entered over a given period | | `exit_count` | The total number of vehicles that exited over a given period | | `peak_entry_time` | The hour of the day (0–23) with the highest number of vehicle entries | | `peak_exit_time` | The hour of the day (0–23) with the highest number of vehicle exits | --- ### Scopes (`scope` filter) | Scope | Description | ID filter | |-------|-------------|-----------| | `lot` | Aggregates data per parking lot | `lot_ids` | | `zone` | Aggregates data per zone within a lot | `zone_ids` | | *(none)* | No scope provided — returns a single aggregated result across all matched entities | — | --- ### Intervals (`interval` filter) | Interval | Description | Response shape | |----------|-------------|----------------| | *(none)* | No interval — returns a single cumulative value | `number` | | `hour` | Breaks data down by hour of the day | `number[]` with 24 values (index 0 = midnight, 23 = 11PM) | | `day` | Breaks data down by day of the week | `number[]` with 7 values (index 0 = Sunday, 6 = Saturday) | | `month` | Breaks data down by month of the year | `number[]` with 12 values (index 0 = January, 11 = December) | --- > **Note:** `peak_entry_time and peak_exit_time` does not support intervals — it always returns a single `number | null` value regardless of the `interval` filter. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `filter` | string[] | yes | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. See [Filtering](/api/conventions#filtering). | ## Responses ### 200 — Returns analytics data based on the provided filters. ```json [ null ] ``` --- # Get sessions CSV file `GET https://api.parklio.com/v2/sessions/export` This endpoint allows the user to get the sessions CSV file **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `lot_id`, `lot_ids`, `zone_id`, `zone_ids`, `created_at`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Create a tariff `POST https://api.parklio.com/v2/tariffs` Creates a tariff: when it applies (dates, season, weekly `schedules`), who it applies to (`group_ids`; none means everyone), and how a stay is priced. Only `name` is required. Assign it to zones with `PUT /v2/zones/{id}/tariffs/{tariffId}`. When an LPR camera reads a plate that isn't whitelisted, a matching tariff lets it in (reason `tariff`). In `schedules`, `weekday` 0 is **Monday**, and times are in the lot's time zone. Prices are calculated by payment machines, not by the API. See the [Sessions and tariffs](/guides/sessions-and-tariffs#3-set-up-tariffs) guide. **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `group_ids` | number[] | no | IDs of the groups this tariff applies to | | `name` | string | yes | Name of the tariff | | `description` | string | no | Description of the tariff | | `start_time` | string | no | Date and time from which the tariff becomes active. The tariff is not applied before this timestamp. | | `end_time` | string | no | Date and time until which the tariff remains active. The tariff is not applied after this timestamp. | | `season_start` | string | no | Seasonal start date of the tariff. Only the day and month are evaluated; the year is ignored. The tariff is applied only within this day–month range each year. | | `season_end` | string | no | Seasonal end date of the tariff. Only the day and month are evaluated; the year is ignored. The tariff is applied only within this day–month range each year. | | `interval` | number | no | Charging interval in minutes (minimum 15) | | `price` | number | no | Base price for the interval | | `exit_free_time_interval` | number | no | Exit free time in minutes | | `entry_free_time_interval` | number | no | Entry free time in minutes | | `maxima` | number | no | Maximum price allowed for the tariff | | `max_time_of_stay` | number | no | Maximum time of stay in minutes | | `gratis_time` | number | no | Gratis time in minutes before charging starts | | `currency_id` | number | no | Currency ID (e.g., 1 = EUR) | | `price_progression_type` | string (`flat`, `linear`, `exponential`, `incremental`) | no | How the price increases (flat, linear, etc.) | | `max_affected_members_per_group` | number | no | Maximum amount of members what will be allowed by that tariff per group | | `schedules` | object[] | yes | | ```json { "group_ids": [ 2, 9 ], "name": "Weekend Special", "description": "Special pricing for weekends only", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2025-01-01", "season_end": "2025-03-01", "interval": 15, "price": 2.5, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 20, "max_time_of_stay": 240, "gratis_time": 30, "currency_id": 1, "price_progression_type": "flat", "max_affected_members_per_group": 3, "schedules": [ { "weekday": 0, "start_time": "08:00:00", "end_time": "17:00:00" } ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all tariffs `GET https://api.parklio.com/v2/tariffs` This endpoint allows the user to list all tariffs **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `name`, `created_at`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get tariffs `GET https://api.parklio.com/v2/tariffs/{id}` This endpoint allows the user to get tariffs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Replace tariffs `PUT https://api.parklio.com/v2/tariffs/{id}` This endpoint allows the user to replace tariffs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `group_ids` | number[] | yes | IDs of the groups this tariff applies to | | `name` | string | yes | Name of the tariff | | `description` | string | yes | Description of the tariff | | `start_time` | string | yes | Date and time from which the tariff becomes active. The tariff is not applied before this timestamp. | | `end_time` | string | yes | Date and time until which the tariff remains active. The tariff is not applied after this timestamp. | | `season_start` | string | no | Seasonal start date of the tariff. Only the day and month are evaluated; the year is ignored. The tariff is applied only within this day–month range each year. | | `season_end` | string | no | Seasonal end date of the tariff. Only the day and month are evaluated; the year is ignored. The tariff is applied only within this day–month range each year. | | `interval` | number | yes | Charging interval in minutes (minimum 15) | | `price` | number | yes | Base price for the interval | | `exit_free_time_interval` | number | yes | Exit free time in minutes | | `entry_free_time_interval` | number | yes | Entry free time in minutes | | `maxima` | number | yes | Maximum price allowed for the tariff | | `max_time_of_stay` | number | yes | Maximum time of stay in minutes | | `gratis_time` | number | yes | Gratis time in minutes before charging starts | | `currency_id` | number | yes | Currency ID (e.g., 1 = EUR) | | `price_progression_type` | string (`flat`, `linear`, `exponential`, `incremental`) | yes | How the price increases (flat, linear, etc.) | | `max_affected_members_per_group` | number | yes | Maximum amount of users that will be affected by this tariff | | `schedules` | object[] | yes | | ```json { "group_ids": [ 2, 9 ], "name": "Weekend Special", "description": "Special pricing for weekends only", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2026-01-15T09:30:00.000Z", "season_end": "2026-01-15T09:30:00.000Z", "interval": 15, "price": 2.5, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 20, "max_time_of_stay": 240, "gratis_time": 30, "currency_id": 1, "price_progression_type": "flat", "max_affected_members_per_group": 20, "schedules": [ { "weekday": 0, "start_time": "08:00:00", "end_time": "17:00:00" } ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Update tariffs `PATCH https://api.parklio.com/v2/tariffs/{id}` This endpoint allows the user to update tariffs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `description` | string | no | | | `start_time` | string | no | | | `end_time` | string | no | | | `interval` | number | no | | | `price` | number | no | | | `exit_free_time_interval` | number | no | | | `entry_free_time_interval` | number | no | | | `maxima` | number | no | | | `max_time_of_stay` | number | no | | | `gratis_time` | number | no | | | `currency` | number | no | | | `price_progression_type` | string (`flat`, `linear`, `exponential`, `incremental`) | no | | ```json { "name": "Updated Tariff Name", "description": "Updated description for this tariff.", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "interval": 30, "price": 250, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 15, "currency": 1, "price_progression_type": "flat" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete tariffs `DELETE https://api.parklio.com/v2/tariffs/{id}` This endpoint allows the user to delete tariffs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Assign tariff groups `PUT https://api.parklio.com/v2/tariffs/{id}/groups/{groupId}` This endpoint allows the user to assign tariff groups by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | | `groupId` | number | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Unassign tariff groups `DELETE https://api.parklio.com/v2/tariffs/{id}/groups/{groupId}` This endpoint allows the user to unassign tariff groups by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `groupId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk assign tariff groups `POST https://api.parklio.com/v2/tariffs/{id}/groups` This endpoint allows the user to bulk assign tariff groups by using their ids **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | IDs of the groups to assign to the tariff | ```json { "ids": [ 3, 8, 12 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk unassign tariff groups `DELETE https://api.parklio.com/v2/tariffs/{id}/groups` This endpoint allows the user to bulk unassign tariff groups by using their ids **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | IDs of the groups to assign to the tariff | ```json { "ids": [ 3, 8, 12 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Replace tariff groups `PUT https://api.parklio.com/v2/tariffs/{id}/groups` This endpoint allows the user to replace tariff groups by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | List of group IDs that will replace all existing groups assigned to the tariff | ```json { "ids": [ 1, 4, 7 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Standard Day Tariff", "description": "Applies during regular business hours", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "season_start": "2020-01-01", "season_end": "2020-12-01", "interval": 60, "price": 200, "exit_free_time_interval": 10, "entry_free_time_interval": 5, "maxima": 2000, "max_time_of_stay": 480, "gratis_time": 20, "currency": "EUR", "price_progression_type": "linear", "account_id": 1, "schedules": [ { "id": 1, "weekday": 1, "start_time": "08:00", "end_time": "18:00" } ], "max_affected_members_per_group": 20, "zones": [ { "id": 1, "name": "Premium Parking Zone" } ], "groups": [ { "id": 5, "name": "Employees" } ] } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Create terminals `POST https://api.parklio.com/v2/terminals` This endpoint allows the user to create terminals **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `device_id` | string | yes | | | `mac` | string | yes | | | `public_key` | string | yes | | | `type` | string (`entry`, `exit`, `pof`, `lp_pof`, `lp_exit`) | yes | | | `is_master` | boolean | yes | | ```json { "device_id": "92338123", "mac": "45:d6:4a:fc:f7:2f", "public_key": "Z3ljaTBlem55OG5jZHh0b3FwbmpqejFxdDdxdmk0aDh3bW40NHBmMHBxaWplZTBnOW50eDVpeXhtN21pdTFueTd0c292eW05dHY4cXZjM3M0anMzcTdyYjJjZWI0bW5iN29ubTl3bWhqeXFjbWF4bTY2NGVhMHNqcW9vMGVuemY=", "type": "entry", "is_master": true } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "zone_id": 1, "name": "Terminal", "status": "online", "device_id": "273827664", "ip_address": "192.168.2.223", "version": "1.0.1", "mac": "AA:BB:CC:11:22:33", "is_master": true, "type": "exit", "pos_serial_number": "23432411233", "electronic_device_id": "12331365465", "system_currency": "EUR", "last_seen": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "created_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Get all terminals `GET https://api.parklio.com/v2/terminals` This endpoint allows the user to list all terminals **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `lot_id`, `zone_id`, `device_id`, `name`, `is_master`, `pos_serial_number`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`, `created_at`, `updated_at`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "zone_id": 1, "name": "Terminal", "status": "online", "device_id": "273827664", "ip_address": "192.168.2.223", "version": "1.0.1", "mac": "AA:BB:CC:11:22:33", "is_master": true, "type": "exit", "pos_serial_number": "23432411233", "electronic_device_id": "12331365465", "system_currency": "EUR", "last_seen": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "created_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Get terminals `GET https://api.parklio.com/v2/terminals/{id}` This endpoint allows the user to get terminals by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "zone_id": 1, "name": "Terminal", "status": "online", "device_id": "273827664", "ip_address": "192.168.2.223", "version": "1.0.1", "mac": "AA:BB:CC:11:22:33", "is_master": true, "type": "exit", "pos_serial_number": "23432411233", "electronic_device_id": "12331365465", "system_currency": "EUR", "last_seen": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "created_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Update terminals `PATCH https://api.parklio.com/v2/terminals/{id}` This endpoint allows the user to update terminals by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `zone_id` | number | no | | | `pos_serial_number` | string | no | | | `electronic_device_id` | string | no | | ```json { "name": "Terminal", "zone_id": 1, "pos_serial_number": "23432411233", "electronic_device_id": "12331365465" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "zone_id": 1, "name": "Terminal", "status": "online", "device_id": "273827664", "ip_address": "192.168.2.223", "version": "1.0.1", "mac": "AA:BB:CC:11:22:33", "is_master": true, "type": "exit", "pos_serial_number": "23432411233", "electronic_device_id": "12331365465", "system_currency": "EUR", "last_seen": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "created_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Delete terminals `DELETE https://api.parklio.com/v2/terminals/{id}` This endpoint allows the user to delete terminals by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Terminal successfuly unassigned. ```json { "success": true, "message": "Terminal successfuly unassigned." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Get terminal sessions details `GET https://api.parklio.com/v2/terminals/{id}/sessions/{session_id}/details` This endpoint allows the user to get terminal sessions details by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `session_id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "session_id": "854813550000003054", "started_at": "2026-01-15T09:30:00.000Z", "ended_at": "2026-01-15T09:30:00.000Z", "duration": 10800, "discount_time": 15, "entry_type": "exit", "entry_terminal": "Terminal.85481355", "amount_paid": 20, "amount_due": 1.3, "payments": [ { "origin": "85481355", "payment_id": 1, "session_id": "854813550000003054", "paid_at": {}, "currency": "EUR", "amount_paid": 20, "amount_due": 20, "payment_method": "CARD", "success": true, "error": null, "provider_transaction_id": "521911099292", "provider_payment_id": "854813550000003054-9a8a-00006894898d" } ], "possibly_exited": false, "exit_allowed": true, "license_plate": "DT-PLATE2" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Get all sessions from terminal `GET https://api.parklio.com/v2/terminals/{id}/sessions` This endpoint allows the user to list all sessions from terminal **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `session_id`, `started_at`, `ended_at`, `entry_type`, `duration`, `payments.paid_at`, `license_plate`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `session_id`, `duration`, `ended_at`, `started_at`, `license_plate`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "zone_id": 1, "name": "Terminal", "status": "online", "device_id": "273827664", "ip_address": "192.168.2.223", "version": "1.0.1", "mac": "AA:BB:CC:11:22:33", "is_master": true, "type": "exit", "pos_serial_number": "23432411233", "electronic_device_id": "12331365465", "system_currency": "EUR", "last_seen": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "created_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Get all lpr cameras by terminal ids `GET https://api.parklio.com/v2/terminals/{id}/lpr-cameras` This endpoint allows the user to list all lpr cameras by terminal ids **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `direction`, `name`, `sn`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": "L0123456789", "created_at": "2026-01-15T09:30:00.000Z", "modified_at": "2026-01-15T09:30:00.000Z", "direction": "in", "cert": "", "ap_id": "", "mac": "00-B0-D0-63-C2-26", "model": "axios", "sn": "L0123456789" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Get lpr cameras live feed by terminal and lpr cameras ids `GET https://api.parklio.com/v2/terminals/{id}/lpr-cameras/{lprCameraId}/live/{size}` This endpoint allows the user to get lpr cameras live feed by terminal and lpr cameras ids by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `lprCameraId` | string | yes | | | `size` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": "L0123456789", "created_at": "2026-01-15T09:30:00.000Z", "modified_at": "2026-01-15T09:30:00.000Z", "direction": "in", "cert": "", "ap_id": "", "mac": "00-B0-D0-63-C2-26", "model": "axios", "sn": "L0123456789" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Get all plater readings by terminal id `GET https://api.parklio.com/v2/terminals/{id}/plate-readings` This endpoint allows the user to list all plater readings by terminal id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `direction`, `plate`, `session_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `created_at`, `id`, `plate`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "session_id": "854813550000003054", "started_at": "2026-01-15T09:30:00.000Z", "ended_at": "2026-01-15T09:30:00.000Z", "duration": 10800, "discount_time": 15, "entry_type": "exit", "entry_terminal": "Terminal.85481355", "amount_paid": 20, "amount_due": 1.3, "payments": [ { "origin": "85481355", "payment_id": 1, "session_id": "854813550000003054", "paid_at": {}, "currency": "EUR", "amount_paid": 20, "amount_due": 20, "payment_method": "CARD", "success": true, "error": null, "provider_transaction_id": "521911099292", "provider_payment_id": "854813550000003054-9a8a-00006894898d" } ], "possibly_exited": false, "exit_allowed": true, "license_plate": "DT-PLATE2" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Get terminal camera reading pictures `GET https://api.parklio.com/v2/terminals/{id}/plate-readings/{plateid}/{size}` Endpoint allows the user to get all the sessions associated with that terminal **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `plateid` | string | yes | | | `size` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "session_id": "854813550000003054", "started_at": "2026-01-15T09:30:00.000Z", "ended_at": "2026-01-15T09:30:00.000Z", "duration": 10800, "discount_time": 15, "entry_type": "exit", "entry_terminal": "Terminal.85481355", "amount_paid": 20, "amount_due": 1.3, "payments": [ { "origin": "85481355", "payment_id": 1, "session_id": "854813550000003054", "paid_at": {}, "currency": "EUR", "amount_paid": 20, "amount_due": 20, "payment_method": "CARD", "success": true, "error": null, "provider_transaction_id": "521911099292", "provider_payment_id": "854813550000003054-9a8a-00006894898d" } ], "possibly_exited": false, "exit_allowed": true, "license_plate": "DT-PLATE2" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Create terminal sessions `POST https://api.parklio.com/v2/terminals/{id}/session` This endpoint allows the user to create terminal sessions **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `plate` | string | yes | | | `user_id` | number | no | | | `user_email` | string | no | | ```json { "plate": "DT-PLATE2", "user_id": 1, "user_email": "john.doe@example.com" } ``` ## Responses ### 200 ```json { "success": true, "data": { "session_id": "854813550000003054", "started_at": "2026-01-15T09:30:00.000Z", "ended_at": "2026-01-15T09:30:00.000Z", "duration": 10800, "discount_time": 15, "entry_type": "exit", "entry_terminal": "Terminal.85481355", "amount_paid": 20, "amount_due": 1.3, "payments": [ { "origin": "85481355", "payment_id": 1, "session_id": "854813550000003054", "paid_at": {}, "currency": "EUR", "amount_paid": 20, "amount_due": 20, "payment_method": "CARD", "success": true, "error": null, "provider_transaction_id": "521911099292", "provider_payment_id": "854813550000003054-9a8a-00006894898d" } ], "possibly_exited": false, "exit_allowed": true, "license_plate": "DT-PLATE2" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Create session payments `POST https://api.parklio.com/v2/terminals/{terminal_id}/sessions/{session_id}/payments` This endpoint allows the user to create session payments **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `terminal_id` | string | yes | | | `session_id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `currency` | string | yes | | | `amount_paid` | number | yes | The paid amount is in EUR | | `payment_method` | string | yes | | | `electronic_device_id` | string | no | | | `user_id` | number | no | | | `user_email` | string | no | | ```json { "currency": "EUR", "amount_paid": 5, "payment_method": "CARD", "electronic_device_id": "67c83c69c1g512e6b4ti9671", "user_id": 1, "user_email": "john.doe@example.com" } ``` ## Responses ### 200 ```json { "success": true, "data": { "origin": "85481355", "payment_id": 1, "session_id": "854813550000003054", "paid_at": "2026-01-15T09:30:00.000Z", "currency": "EUR", "amount_paid": 20, "amount_due": 20, "payment_method": "CARD", "success": true, "error": null, "provider_transaction_id": "521911099292", "provider_payment_id": "854813550000003054-9a8a-00006894898d", "invoice_link": "https://api.spaceinvoices.com/v1/Documents/public/id/pdf" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Update terminal sessions `PATCH https://api.parklio.com/v2/terminals/{id}/sessions/{sessionId}` This endpoint allows the user to update terminal sessions by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `sessionId` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `license_plate` | string | no | | | `discount_time` | number | no | | | `end` | boolean | no | | | `user_id` | number | no | | | `user_email` | string | no | | ```json { "license_plate": "DT-PLATE2", "discount_time": -1, "end": true, "user_id": 1, "user_email": "john.doe@example.com" } ``` ## Responses ### 200 ```json { "success": true, "data": { "session_id": "854813550000003054", "started_at": "2026-01-15T09:30:00.000Z", "ended_at": "2026-01-15T09:30:00.000Z", "duration": 10800, "discount_time": 15, "entry_type": "exit", "entry_terminal": "Terminal.85481355", "amount_paid": 20, "amount_due": 1.3, "payments": [ { "origin": "85481355", "payment_id": 1, "session_id": "854813550000003054", "paid_at": {}, "currency": "EUR", "amount_paid": 20, "amount_due": 20, "payment_method": "CARD", "success": true, "error": null, "provider_transaction_id": "521911099292", "provider_payment_id": "854813550000003054-9a8a-00006894898d" } ], "possibly_exited": false, "exit_allowed": true, "license_plate": "DT-PLATE2" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` ### 503 — the device might not be connected or an internal error happened ```json { "success": false, "errors": [ { "code": 40, "message": "the message to the remote device timed out" } ] } ``` --- # Bulk create parking places `POST https://api.parklio.com/v2/parking-places` This endpoint allows the user to bulk create parking places **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | | | `external_id` | string | no | | | `place_count` | number | no | | | `zone_id` | number | yes | | ```json { "name": "Parking place", "external_id": 1, "place_count": 5, "zone_id": 1 } ``` ## Responses ### 200 ```json { "success": true, "data": { "lot_id": 1, "name": "Parking place", "occupancy_status": 1, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "external_id": 1, "zone_id": 1, "uuid": "10ba038e-48da-487b-96e8-8d3b99b6d18a", "id": 1 } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Requested zone resource does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 303, "message": "Zone not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # List parking places `GET https://api.parklio.com/v2/parking-places` Lists the parking places of lots with parking detection, with each place's `occupancy_status`: `free`, `occupied` or `unknown`. Without the `PARKING_DETECTION` feature, every place reads `unknown`. See the [Live occupancy](/guides/live-occupancy) guide. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `lot_id`, `lot_ids`, `name`, `occupancy_status`, `uuid`, `zone_id`, `zone_ids`, `external_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "data": [ { "id": 301, "lot_id": 7, "zone_id": 9, "name": "Spot 12", "occupancy_status": "occupied", "external_id": null, "uuid": "10ba038e-48da-487b-96e8-8d3b99b6d18a", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-03-02T08:12:40.000Z" } ], "meta": { "total": 1, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=1", "last_page_url": "/?page=1", "next_page_url": "/?page=2", "previous_page_url": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get parking places `GET https://api.parklio.com/v2/parking-places/{id}` This endpoint allows the user to get parking places by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "lot_id": 1, "name": "Parking place", "occupancy_status": 1, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "external_id": 1, "zone_id": 1, "uuid": "10ba038e-48da-487b-96e8-8d3b99b6d18a", "id": 1 } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update parking places `PATCH https://api.parklio.com/v2/parking-places/{id}` This endpoint allows the user to update parking places by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | ```json { "name": "Parking place" } ``` ## Responses ### 200 ```json { "success": true, "data": { "lot_id": 1, "name": "Parking place", "occupancy_status": 1, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "external_id": 1, "zone_id": 1, "uuid": "10ba038e-48da-487b-96e8-8d3b99b6d18a", "id": 1 } } ``` ### 400 — Cannot be moved between different lots. ```json { "success": false, "errors": [ { "code": 1000, "message": "Cannot move to different lot." } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete parking places `DELETE https://api.parklio.com/v2/parking-places/{id}` This endpoint allows the user to delete parking places by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # List parking place changes `GET https://api.parklio.com/v2/parking-place-changes` The history of every parking place's status changes, with how long each state lasted (`duration`, in seconds). Needs the `PARKING_DETECTION` feature. See the [Live occupancy](/guides/live-occupancy) guide. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `created_at`, `duration`, `end_time`, `lot_id`, `lot_ids`, `parking_place_name`, `occupancy_status`, `parking_place_id`, `parking_place_ids`, `zone_id`, `zone_ids`. See [Filtering](/api/conventions#filtering). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "lot_id": 1, "parking_place_id": 1, "zone_id": 1, "duration": 1, "end_time": null, "occupancy_status": "occupied", "parking_place": {}, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get parking place changes CSV file `GET https://api.parklio.com/v2/parking-place-changes/export` This endpoint allows the user to get the parking place changes CSV file **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `duration`, `lot_id`, `lot_ids`, `parking_place_id`, `parking_place_ids`, `occupancy_status`, `zone_id`, `zone_ids`, `created_at`. See [Filtering](/api/conventions#filtering). | ## Responses ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get parking place changes `GET https://api.parklio.com/v2/parking-place-changes/{id}` This endpoint allows the user to get parking place changes by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "lot_id": 1, "parking_place_id": 1, "zone_id": 1, "duration": 1, "end_time": null, "occupancy_status": "occupied", "parking_place": {}, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get parking analytics `GET https://api.parklio.com/v2/parking-analytics` Returns parking analytics data based on the provided filters. --- ### Metrics (`metric` filter) | Metric | Description | |--------|-------------| | `average_duration` | The average time (in minutes) a vehicle spends parked | | `average_duration_percentage` | The average duration as a percentage of the maximum allowed parking time | | `total_duration` | The total cumulative time (in minutes) all vehicles spent parked | | `turnover_rate` | The number of different vehicles that occupied a spot over a given period | | `peak_occupancy_hour` | The hour of the day (0–23) with the highest occupancy | --- ### Scopes (`scope` filter) | Scope | Description | ID filter | |-------|-------------|-----------| | `lot` | Aggregates data per parking lot | `lot_ids` | | `zone` | Aggregates data per zone within a lot | `zone_ids` | | `place` | Aggregates data per individual parking place | `parking_place_ids` | | *(none)* | No scope provided — returns a single aggregated result across all matched entities | — | --- ### Intervals (`interval` filter) | Interval | Description | Response shape | |----------|-------------|----------------| | *(none)* | No interval — returns a single cumulative value | `number` | | `hour` | Breaks data down by hour of the day | `number[]` with 24 values (index 0 = midnight, 23 = 11PM) | | `day` | Breaks data down by day of the week | `number[]` with 7 values (index 0 = Sunday, 6 = Saturday) | | `month` | Breaks data down by month of the year | `number[]` with 12 values (index 0 = January, 11 = December) | --- > **Note:** `peak_occupancy_hour` does not support intervals — it always returns a single `number | null` value regardless of the `interval` filter. **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `filter` | string[] | yes | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. See [Filtering](/api/conventions#filtering). | ## Responses ### 200 — Returns analytics data based on the provided filters. ```json [ null ] ``` ### 403 — User is trying to filter where end time is before start time. ```json { "success": false, "errors": [ { "code": 306, "message": "End time is before start time." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk create proactive alerts `POST https://api.parklio.com/v2/proactive-alerts/{id}/receivers` This endpoint allows the user to bulk create proactive alerts **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `receivers` | number[] | yes | | ```json { "receivers": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk put proactive alerts `PUT https://api.parklio.com/v2/proactive-alerts/{id}/receivers` This endpoint allows the user to bulk put proactive alerts **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `receivers` | number[] | yes | | ```json { "receivers": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Bulk unassign proactive alert receivers `DELETE https://api.parklio.com/v2/proactive-alerts/{id}/receivers` This endpoint allows the user to bulk unassign proactive alert receivers by using their ids **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `receiver_ids` | number[] | yes | IDs of the account users to unassign as receivers from this license plate alert. | ```json { "receiver_ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Put proactive alerts receivers `PUT https://api.parklio.com/v2/proactive-alerts/{alertId}/receivers/{receiverId}` This endpoint allows the user to put proactive alerts receivers by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `alertId` | string | yes | | | `receiverId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Put proactive alerts `PUT https://api.parklio.com/v2/proactive-alerts/{id}` This endpoint allows the user to put proactive alerts by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `zone_id` | number | yes | | | `receivers` | number[] | yes | | | `name` | string | yes | | | `occupancy_threshold` | number | yes | | | `min_minutes_between_emails` | number | yes | Minimum time interval (in minutes) required between sending alert emails. Must be between 1 and 3600 minutes. Default value is 30. | ```json { "zone_id": 1, "receivers": [ 1, 2, 3 ], "name": "Alert name", "occupancy_threshold": 90, "min_minutes_between_emails": 30 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get proactive alerts `GET https://api.parklio.com/v2/proactive-alerts/{id}` This endpoint allows the user to get proactive alerts by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update proactive alerts `PATCH https://api.parklio.com/v2/proactive-alerts/{id}` This endpoint allows the user to update proactive alerts by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | Descriptive name of the alert. Must be between 3 and 256 characters. | | `occupancy_threshold` | number | no | Occupancy percentage threshold in % that triggers an email notification when the parking lot reaches or exceeds this value. | | `min_minutes_between_emails` | number | no | Minimum time interval (in minutes) required between sending alert emails. Must be between 1 and 3600 minutes. | ```json { "name": "New Alert name", "occupancy_threshold": 90, "min_minutes_between_emails": 60 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete proactive alerts `DELETE https://api.parklio.com/v2/proactive-alerts/{id}` This endpoint allows the user to delete proactive alerts by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create proactive alerts `POST https://api.parklio.com/v2/proactive-alerts` This endpoint allows the user to create proactive alerts **Auth:** `Authorization: Bearer ` ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `zone_id` | number | yes | Unique identifier of the zone associated with the alert | | `receivers` | number[] | yes | List of account user IDs that will receive the email alert. | | `name` | string | yes | Descriptive name of the alert. Must be between 3 and 256 characters. | | `occupancy_threshold` | number | no | Occupancy percentage threshold in % that triggers an email notification when the parking lot reaches or exceeds this value. | | `min_minutes_between_emails` | number | no | Minimum time interval (in minutes) required between sending alert emails. Must be between 1 and 3600 minutes. Default value is 30. | ```json { "zone_id": 1, "receivers": [ 1, 2, 3 ], "name": "Alert name", "occupancy_threshold": 90, "min_minutes_between_emails": 30 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all proactive alerts `GET https://api.parklio.com/v2/proactive-alerts` This endpoint allows the user to list all proactive alerts **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `created_at`, `lot_id`, `zone_id`. See [Filtering](/api/conventions#filtering). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Unassign proactive alert receivers `DELETE https://api.parklio.com/v2/proactive-alerts/{id}/receivers/{reciverId}` This endpoint allows the user to unassign proactive alert receivers by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `reciverId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Defualt Zone Alert", "occupancy_threshold": 90, "min_minutes_between_emails": 30, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "receivers": [ { "id": 1, "name": "John Doe", "receiver_email": "john.doe@example.com" }, { "id": 2, "name": "Jane Smith", "receiver_email": "jane.smith@example.com" } ], "zone": { "id": 1, "name": "Defualt Zone" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all rgb displays `GET https://api.parklio.com/v2/rgb-displays` This endpoint allows the user to list all rgb displays **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `name`, `lot_id`, `zone_id`, `serial_number`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Add a source zones to an rgb display `POST https://api.parklio.com/v2/rgb-displays/{id}/source-zones` This endpoint allows the user to add source zones to an existing rgb display. **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | The list of zone ids to assign to this RGB display as source zones. | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Unassign unassign source zones from rgb displays `DELETE https://api.parklio.com/v2/rgb-displays/{id}/source-zones` This endpoint allows the user to unassign unassign source zones from rgb displays by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | The list of zone ids to unassign from this RGB display's source zones. | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Create replace source zones on rgb display `PUT https://api.parklio.com/v2/rgb-displays/{id}/source-zones` This endpoint allows the user to create replace source zones on rgb display **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `ids` | number[] | yes | The list of zone ids that will fully replace this RGB display's current source zones. | ```json { "ids": [ 1, 2, 3 ] } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Simulate result for rgb displays `POST https://api.parklio.com/v2/rgb-displays/{id}/simulate` This endpoint allows the user to simulate the rgb display response by available count **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `occupied_spots` | number | yes | The number of occupied spots to simulate. This value is evaluated against the display's rules, and the first matching rule determines the output message. | | `current_time` | string | yes | The point in time to simulate the rule evaluation at. Used to check whether time-windowed rules (`startTime`/`endTime`) would be active at that moment. | ```json { "occupied_spots": 5, "current_time": "2026-01-15T09:30:00.000Z" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get rgb displays `GET https://api.parklio.com/v2/rgb-displays/{id}` This endpoint allows the user to get rgb displays by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update rgb displays `PATCH https://api.parklio.com/v2/rgb-displays/{id}` This endpoint allows the user to update rgb displays by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | The display name of the RGB display. Must be between 3 and 255 characters. | | `default_message` | string | yes | Optional default message shown on the display when no conditional rule matches. Maximum length is 255 characters. Supports placeholders: `{free}` (current free spots) and `{total}` (total zone capacity). | | `zone_id` | number | yes | The ID of the zone this RGB display is associated with. | ```json { "name": "RGB Display name", "default_message": "{free} FREE", "zone_id": 1 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete rgb displays `DELETE https://api.parklio.com/v2/rgb-displays/{id}` This endpoint allows the user to delete rgb displays by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Rgb display successfuly unassigned. ```json { "success": true, "message": "Rgb display successfuly unassigned." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Replace an RGB display `PUT https://api.parklio.com/v2/rgb-displays/{id}` Fully replaces an RGB display's configuration, including its name, default message, and source zones. The provided sourceZoneIds list completely replaces the existing source zones **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | The ID of the RGB display to replace | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | The display name of the RGB display. Must be between 3 and 255 characters. | | `source_zone_ids` | number[] | yes | The list of zone ids that will fully replace this RGB display's current source zones. | | `default_message` | string | yes | The default message shown on the display when no conditional rule matches. Supports placeholders: `{free}` (current free spots) and `{total}` (total zone capacity). | ```json { "name": "RGB Display name", "source_zone_ids": [ 1, 2, 3 ], "default_message": "{free} FREE" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` --- # Unassign unassign source zones by source zone id from rgb displays `DELETE https://api.parklio.com/v2/rgb-displays/{id}/source-zones/{sourceZoneId}` This endpoint allows the user to unassign unassign source zones by source zone id from rgb displays by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `sourceZoneId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Create assign source zones to rgb displays `PUT https://api.parklio.com/v2/rgb-displays/{id}/source-zones/{sourceZoneId}` This endpoint allows the user to create assign source zones to rgb displays **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | | `sourceZoneId` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "RGB Display name", "status": "online", "last_seen": "2026-01-15T09:30:00.000Z", "total_occupied_count": 12, "total_spot_count": 20, "serial_number": "RGB Display serial number", "default_message": "{free} FREE", "zone_id": 1, "lot_id": 1, "rules": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "source_zones": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ], "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Get all rgb display rules `GET https://api.parklio.com/v2/rgb-display-rules` This endpoint allows the user to list all rgb display rules **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `name`, `lot_id`, `zone_id`, `rgb_display_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `order`, `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get rgb display rules `GET https://api.parklio.com/v2/rgb-display-rules/{id}` This endpoint allows the user to get rgb display rules by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update rgb display rules `PATCH https://api.parklio.com/v2/rgb-display-rules/{id}` This endpoint allows the user to update rgb display rules by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | The name of the rule used to identify it in the list. | | `operator` | string (`eq`, `lt`, `gt`, `between`) | no | The comparison operator used to evaluate the rule against the available spot count. Supported values: `eq` (equal), `lt` (less than), `gt` (greater than), `between` (inclusive range). | | `occupied_value` | number | no | The value to compare the occupied spot count against. When operator is `between`, this represents the lower bound of the range. | | `occupied_value_to` | number | no | The upper bound occupied value used only when operator is `between`. Required when operator is `between`, ignored for all other operators. | | `display_text` | string | no | The text to display when this rule matches. Supports placeholders: `{free}` (current free spots) and `{total}` (total zone capacity). | | `start_time` | string | no | The time from which this rule is active. If null the rule is active at all times. Must be used together with `endTime`. | | `end_time` | string | no | The time until which this rule is active. If null the rule is active at all times. Must be used together with `startTime`. | | `order` | number | no | The position of this rule in the evaluation order. Rules are evaluated top-to-bottom and the first match wins. If omitted, the existing order is preserved. | ```json { "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "order": 0 } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Low availability", "operator": "gt", "occupied_value": 5, "occupied_value_to": 10, "display_text": "{free} FREE", "order": 0, "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete rgb display rules `DELETE https://api.parklio.com/v2/rgb-display-rules/{id}` This endpoint allows the user to delete rgb display rules by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get all parking detection cameras `GET https://api.parklio.com/v2/parking-detection-cameras` This endpoint allows the user to list all parking detection cameras **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `lot_id`, `account_id`, `uuid`, `zone_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "name": "Parking Detection Camera 1", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4", "external_id": "0101010101010101", "parking_detection_gateway_id": 1, "zone_id": 1, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "view_schema": "string" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get parking detection cameras `GET https://api.parklio.com/v2/parking-detection-cameras/{id}` This endpoint allows the user to get parking detection cameras by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Parking Detection Camera 1", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4", "external_id": "0101010101010101", "parking_detection_gateway_id": 1, "zone_id": 1, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "view_schema": "string" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update parking detection cameras `PATCH https://api.parklio.com/v2/parking-detection-cameras/{id}` This endpoint allows the user to update parking detection cameras by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `external_id` | string | no | | | `uuid` | string | no | | ```json { "name": "Parking Detection Camera 1", "external_id": "0101010101010101", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Parking Detection Camera 1", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4", "external_id": "0101010101010101", "parking_detection_gateway_id": 1, "zone_id": 1, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "view_schema": "string" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete parking detection cameras `DELETE https://api.parklio.com/v2/parking-detection-cameras/{id}` This endpoint allows the user to delete parking detection cameras by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get camera frame `GET https://api.parklio.com/v2/parking-detection-cameras/{id}/frame` Get parking detection camera frame by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Parking Detection Camera 1", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4", "external_id": "0101010101010101", "parking_detection_gateway_id": 1, "zone_id": 1, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "view_schema": "string" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Upload view schema by id `POST https://api.parklio.com/v2/parking-detection-cameras/{id}/upload-view-schema` Upload view schema to detection camera frame by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Parking detection camera ID | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "name": "Parking Detection Camera 1", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4", "external_id": "0101010101010101", "parking_detection_gateway_id": 1, "zone_id": 1, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "view_schema": "string" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — The file uploaded is not valid or corrupted ```json { "success": false, "errors": [ { "code": 27, "message": "The file you uploaded is malformed" } ] } ``` --- # Get all parking detection gateways `GET https://api.parklio.com/v2/parking-detection-gateways` This endpoint allows the user to list all parking detection gateways **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `lot_id`, `uuid`, `status`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "name": "Parking Detction Gateway 1", "uuid": "string", "lot_id": 0, "status": "online", "external_id": "string", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get parking detection gateways `GET https://api.parklio.com/v2/parking-detection-gateways/{id}` This endpoint allows the user to get parking detection gateways by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "name": "Parking Detction Gateway 1", "uuid": "string", "lot_id": 0, "status": "online", "external_id": "string", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update parking detection gateways `PATCH https://api.parklio.com/v2/parking-detection-gateways/{id}` This endpoint allows the user to update parking detection gateways by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | | `external_id` | string | no | | | `uuid` | string | no | | ```json { "name": "Parking Detction Gateway 1", "external_id": "0101010101010101", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4" } ``` ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "name": "Parking Detction Gateway 1", "uuid": "string", "lot_id": 0, "status": "online", "external_id": "string", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete parking detection gateways `DELETE https://api.parklio.com/v2/parking-detection-gateways/{id}` This endpoint allows the user to delete parking detection gateways by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | number | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Create violation zones `POST https://api.parklio.com/v2/violation-zones` This endpoint allows the user to create violation zones **Auth:** none (public) ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | yes | | | `parking_detection_camera_id` | number | yes | | ```json { "name": "Violation zone", "parking_detection_camera_id": 1 } ``` ## Responses ### 201 --- # Get all violation zones `GET https://api.parklio.com/v2/violation-zones` This endpoint allows the user to list all violation zones **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `lot_id`, `name`, `uuid`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "parking_detection_camera_id": 1, "name": "Violation Zone", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4", "id": 1 } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get violation zones `GET https://api.parklio.com/v2/violation-zones/{id}` This endpoint allows the user to get violation zones by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "parking_detection_camera_id": 1, "name": "Violation Zone", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4", "id": 1 } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Update violation zones `PATCH https://api.parklio.com/v2/violation-zones/{id}` This endpoint allows the user to update violation zones by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | string | no | | ```json { "name": "Violation zone" } ``` ## Responses ### 200 ```json { "success": true, "data": { "parking_detection_camera_id": 1, "name": "Violation Zone", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4", "id": 1 } } ``` ### 400 — Cannot be moved between different lots. ```json { "success": false, "errors": [ { "code": 1000, "message": "Cannot move to different lot." } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` ### 422 — Some of the provided request parameters are missing or are invalid. User should add/change them according to response message. ```json { "success": false, "errors": [ { "code": 23, "message": "Validation error", "field": "field" } ] } ``` --- # Delete violation zones `DELETE https://api.parklio.com/v2/violation-zones/{id}` This endpoint allows the user to delete violation zones by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 — Successfully deleted. ```json { "success": true, "message": "Successfully deleted." } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get all violation zone events `GET https://api.parklio.com/v2/violation-zone-events` This endpoint allows the user to list all violation zone events **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `color`, `license_plate`, `lot_id`, `model`, `parking_detection_camera_id`, `start_time`, `end_time`, `stationary_time_above`, `stationary_time_bellow`, `violation_zone_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "license_plate": "DT-PLATE2", "color": "red", "model": 1, "stationary_time": 60, "image_url": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "violation_zone": { "id": 1, "parking_detection_camera_id": 1, "name": "Violation Zone", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4" } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get violation zone events `GET https://api.parklio.com/v2/violation-zone-events/{id}` This endpoint allows the user to get violation zone events by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "license_plate": "DT-PLATE2", "color": "red", "model": 1, "stationary_time": 60, "image_url": "string", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "violation_zone": { "id": 1, "parking_detection_camera_id": 1, "name": "Violation Zone", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "uuid": "d3a17704-98e4-4c8b-bb2e-5c2ec4e1c1f4" } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get all lpr remote controls `GET https://api.parklio.com/v2/logs/lpr-remote-controls` This endpoint allows the user to list all lpr remote controls **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `lot_id`, `zone_id`, `lpr_camera_device_id`, `lpr_camera_name`, `account_user_id`, `account_user_name`, `operation`, `date_from`, `date_to`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "lpr_camera_id": 0, "account_id": 0, "account_user_id": 0, "lot_id": 0, "operation": "open", "created_at": "2026-01-15T09:30:00.000Z", "lpr_camera": { "id": 0, "device_id": "L0123456789", "device_serial": "L0123456789", "name": "John Doe", "account_id": 0, "zone_id": 0, "status": "online", "lpr_postion": "in", "connected_device_type": "gate", "mac": "00:1A:2B:3C:4D:5E", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null }, "account_user": { "id": 0, "account_user_role_id": 1, "account_id": 0, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "language_tag": "fr-BE", "country_code": "BE", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account": { "id": 1, "account_role_id": 1, "auto_extend": {}, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 666 6666", "billing_title": "string", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "created_at": {}, "updated_at": {}, "infinite_shares": 0, "account_role": { "id": 2, "name": "PMS", "created_at": {}, "updated_at": {} }, "authorities": [] }, "account_user_role": { "id": 1, "name": "Super Admin", "created_at": {}, "updated_at": {} }, "lots": { "id": 1, "name": "Default Lot", "address": "Put Stinica 12" } } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get lpr remote controls `GET https://api.parklio.com/v2/logs/lpr-remote-controls/{id}` This endpoint allows the user to get lpr remote controls by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "lpr_camera_id": 0, "account_id": 0, "account_user_id": 0, "lot_id": 0, "operation": "open", "created_at": "2026-01-15T09:30:00.000Z", "lpr_camera": { "id": 0, "device_id": "L0123456789", "device_serial": "L0123456789", "name": "John Doe", "account_id": 0, "zone_id": 0, "status": "online", "lpr_postion": "in", "connected_device_type": "gate", "mac": "00:1A:2B:3C:4D:5E", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "deleted_at": null }, "account_user": { "id": 0, "account_user_role_id": 1, "account_id": 0, "email": "john.doe@example.com", "name": "John Doe", "phone": "+385 555 5555", "language_tag": "fr-BE", "country_code": "BE", "email_confirmed": 1, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "account": { "id": 1, "account_role_id": 1, "auto_extend": {}, "name": "John Doe", "contact_name": "John Doe", "contact_email": "john.doe@example.com", "contact_phone": "+385 666 6666", "billing_title": "string", "vat": "25%", "address": "Put Stinica 12", "city": "Split", "zip_code": "21000", "created_at": {}, "updated_at": {}, "infinite_shares": 0, "account_role": { "id": 2, "name": "PMS", "created_at": {}, "updated_at": {} }, "authorities": [] }, "account_user_role": { "id": 1, "name": "Super Admin", "created_at": {}, "updated_at": {} }, "lots": { "id": 1, "name": "Default Lot", "address": "Put Stinica 12" } } } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get all logs `GET https://api.parklio.com/v2/logs/lot-whitelists` This endpoint allows the user to list all logs **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `lot_id`, `account_user_id`, `account_user_name`, `license_plate`, `operation`, `start_time`, `end_time`, `is_unlimited`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "account_id": 0, "account_user_id": 0, "account_user": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" }, "lot_id": 0, "license_plate": "DT-PLATE2", "operation": "added", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "is_unlimited": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get logs `GET https://api.parklio.com/v2/logs/lot-whitelists/{id}` This endpoint allows the user to get logs by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 0, "account_id": 0, "account_user_id": 0, "account_user": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" }, "lot_id": 0, "license_plate": "DT-PLATE2", "operation": "added", "start_time": "2026-01-15T09:30:00.000Z", "end_time": "2026-01-15T09:30:00.000Z", "is_unlimited": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get logs/product/operation `GET https://api.parklio.com/v2/logs/product/operation/{id}` This endpoint allows the user to get logs/product/operation by id **Auth:** `Authorization: Bearer ` ## Path parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | | ## Responses ### 200 ```json { "success": true, "data": { "id": 1, "account_user_id": 1, "zone_id": 1, "account_user": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" }, "product_id": 1, "product": { "id": 1, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 1, "zone_id": 1, "product_type_id": 1, "status": "online" }, "operation_type": "open", "type_of_access": "app", "created_at": "2026-01-15T09:30:00.000Z" } } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` ### 403 — User does not have permission to access requested resource. ```json { "success": false, "errors": [ { "code": 4, "message": "No permission." } ] } ``` ### 404 — Resource that has been requested does not exist. User should not try to repeat this request. ```json { "success": false, "errors": [ { "code": 6, "message": "Resource not found." } ] } ``` --- # Get all logs/product/operation `GET https://api.parklio.com/v2/logs/product/operation` This endpoint allows the user to list all logs/product/operation **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_id`, `account_user_email`, `account_user_id`, `account_user_name`, `account_user_role_id`, `created_at`, `device_id`, `lot_id`, `operation_type`, `product_id`, `type_of_access`, `zone_id`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `name`, `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 1, "account_user_id": 1, "zone_id": 1, "account_user": { "id": 1, "email": "john.doe@example.com", "name": "John Doe" }, "product_id": 1, "product": { "id": 1, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 1, "zone_id": 1, "product_type_id": 1, "status": "online" }, "operation_type": "open", "type_of_access": "app", "created_at": "2026-01-15T09:30:00.000Z" } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ``` --- # Get all remote control logs `GET https://api.parklio.com/v2/statistics/remote-control` This endpoint allows the user to list all remote control logs **Auth:** `Authorization: Bearer ` ## Query parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `page` | number | no | Page number, starting at 1. See [Paging](/api/conventions#paging). | | `size` | number | no | Items per page: 10 by default, at most 300. See [Paging](/api/conventions#paging). | | `filter` | string[] | no | Filter as `field:rule:value`, for example `lot_id:eq:7`. Repeat `filter` to combine filters; all must match. Fields: `account_user_id`, `product_id`, `operation`. See [Filtering](/api/conventions#filtering). | | `sort` | string[] | no | Sort as `field:asc` or `field:desc`. Send it once. Fields: `id`. See [Sorting](/api/conventions#sorting). | ## Responses ### 200 ```json { "success": true, "meta": { "total": 100, "per_page": 10, "current_page": 1, "last_page": 1, "first_page": 1, "first_page_url": "/?page=2", "last_page_url": "/?page=10", "next_page_url": "/?page=3", "previous_page_url": "/?page=1" }, "data": [ { "id": 0, "product_id": 0, "account_user_id": 0, "operation": "open", "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "product": { "product_error": { "id": 0, "jammed": 0, "sensor_dirty": 0, "pin_broken": 0, "rtcc_invalid": 0, "battery_empty": 0, "master_key_not_set": 0, "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "error_code": "string" }, "id": 0, "device_id": "B123456", "name": "B123456", "power_type": "battery", "lot_id": 0, "zone_id": 0, "product_type_id": 2, "hardware_version": "1.0.0", "firmware_version": "1.0.0", "latitude": 43.52652359, "longitude": 16.450813293, "status": "online", "advertisement_data": "string", "battery_status": null, "product_error_id": null, "rssi": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z", "barrier": { "id": 0, "product_id": 0, "state": "opened", "is_autoclose": 0, "is_car_on_top": 0, "autoclose_time": 0, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "gate": { "id": 0, "product_id": 0, "state": "closed", "permanent_position": "disabled", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "chain": { "id": 0, "product_id": 0, "state": "closed", "permanent_position": "disabled", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "bollard": { "id": 0, "product_id": 0, "state": "closed", "permanent_position": "disabled", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" }, "brain": { "id": 0, "product_id": 0, "state": "closed", "permanent_position": "disabled", "deleted_at": null, "created_at": "2026-01-15T09:30:00.000Z", "updated_at": "2026-01-15T09:30:00.000Z" } } } ] } ``` ### 401 — Account user is not logged in and they should log in again. ```json { "success": false, "errors": [ { "code": 1, "message": "Unauthorized access." } ] } ```