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.
- 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.
What happens on a read
- The camera reads a plate and Parklio records a plate reading
(
plate_reading_createin realtime). - 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
debounceand nothing else happens. - 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.
- On the whitelist:
- the decision is
triggered, reasonwhitelist; - 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, out ends it.
- the decision is
- Not on the whitelist: tariffs decide.
- The decision is recorded on the reading (
plate_reading_updatein 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 adds up to 20 plates to
a lot for a time window. Always send lot_id.
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-CDbecomesST555CD. - Send either
end_timeor"is_unlimited": true, not both. end_timemust be in the future, and afterstart_time.
The response has one item per plate. Keep each license_id: you need it to bind
the plate to products.
{
"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};
GET /v2/lot-whitelists/sample/export
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}.
GET /v2/lot-whitelists 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:
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
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.
:::
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}.
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. |
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). Over REST, filter
GET /v2/plate-readings:
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
- Drive a whitelisted car past the camera, going in.
plate_reading_createarrives, thenplate_reading_updatewithaccess_decision: "triggered"and reasonwhitelist.- A
lot_changeshows the bound productopened, and asession_createarrives. GET /v2/products/{id}/bound-licenseslists 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: what happens to cars that aren't whitelisted.
- Events