Skip to main content

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​

  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, out ends it.
  5. Not on the whitelist: 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 adds up to 20 plates to a lot for a time window. Always send lot_id.

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.

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}; 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:

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 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:

DecisionReasonWhen
triggeredwhitelistActive whitelist entry on the lot.
denieddebounceSame plate, same zone, within 20 seconds.
triggeredtariff, session_exitLet in by a tariff, or out with an open session. See Sessions and tariffs.
deniedunknown_direction, direction_mismatch, no_tariff, max_capacity, no_active_sessionNot 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:

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 lists the plate.

Common errors​

SymptomCauseFix
Reason no_tariff or no_active_session for a plate you whitelistedNo 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 openNot 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 debounceThe camera read the same plate within 20 seconds.Expected.
A plate item with "success": falseThat plate couldn't be added.Check its length and the window.
403 code 402Neither end_time nor is_unlimited.Send one of them.
403 code 403end_time is in the past.Use a future end_time.

Next​