Overview
With the Parking Manager API, an external system can perform the day-to-day access tasks your team now does by hand — most often temporarily adding a license plate for a visitor.
The recommended way to grant temporary access is a visitor booking: a license plate that is only valid between a start and end time, and then expires automatically. You can also add a plate permanently, read live occupancy, and check available timeslots before booking.
Base URL
All paths below are relative to your instance, e.g. https://your-instance.revtek.nl/chatbot/api/v1. You receive the exact hostname for your location together with your API key.
Conventions
- All requests and responses use
application/json. - Every response includes a
"status"field:"success","partial", or"error". - License plates are automatically normalized to uppercase and trimmed.
- Every authenticated request must name a tenant — see Tenants & scope.
Authentication
Every endpoint requires an API key, except the public endpoints /health and /webhooks/event-types.
Pass your key in either header with every request:
Request headers
X-API-Key: your_api_key_here
# — or —
Authorization: Bearer your_api_key_here
Handling keys
Keys are stored hashed and can be given an expiry date. Treat your key like a password — only send it over HTTPS and never put it in client-side code. Contact us to rotate or revoke a key.
Example — a full request with curl
curl https://your-instance.revtek.nl/chatbot/api/v1/occupancy?tenant_name=Silverflow \
-H "X-API-Key: your_api_key_here"
Tenants & scope
A tenant is your organization at a shared parking location. Every request acts on behalf of a single tenant, and your key only touches your own tenant.
Include tenant_name in the request body (for POST/PUT) or as a query parameter (for GET/DELETE). Name matching is lenient — exact, then case-insensitive, then partial — so small variations still match. Your key is bound to your tenant and cannot read or modify data belonging to other companies at the same location.
Note
Because your key is tenant-bound, you still pass tenant_name for clarity, but you can only act on your own tenant.
Dates & times
All timestamps are ISO 8601 strings.
- Send times in UTC with a trailing
Z— e.g.2026-07-10T14:00:00Z— or with an explicit offset. - A timestamp without a time zone is interpreted as Central European Time (Amsterdam).
- All timestamps in responses are returned in UTC.
Errors
Errors return a non-2xx status code and a JSON body with a human-readable message.
| Code | Meaning | Typical cause |
|---|---|---|
| 200 / 201 | Success | Request completed. |
| 400 | Bad request | Missing or invalid field (e.g. no plate_number). |
| 401 | Unauthorized | Missing, invalid, or expired API key. |
| 403 | Forbidden | Visitor parking not enabled, or plate belongs to a different tenant. |
| 404 | Not found | Tenant or resource does not exist. |
| 409 | Conflict | Plate already exists, or no visitor slots available. |
| 500 | Server error | Unexpected error on our side. |
Error body
{
"status": "error",
"message": "plate_number is required"
}
Visitor bookings
The primary way to grant temporary access. A booking makes a plate valid only between start_time and end_time, and counts toward your visitor capacity.
Create a visitor booking. Optionally recurring — see Recurrence.
| Field | Type | Description | |
|---|---|---|---|
| tenant_name | string | required | Your tenant. |
| visitor_name | string | required | Name of the visitor. |
| start_time | ISO 8601 | required | When access begins. |
| end_time | ISO 8601 | optional | When access ends. Defaults to start_time + 1 hour. |
| plate_number | string | optional | The visitor's license plate. Omitted? Then the slot is reserved and the plate can be filled in later. |
| recurrence_rule | string | optional | weekly, biweekly, or monthly. Omit for a one-off. |
| recurrence_weekdays | int[] | optional | Mon=0 … Sun=6. Only for weekly/biweekly; each day becomes its own series. |
| recurrence_end_date | ISO 8601 | optional | Stop recurring after this date. |
Request
POST /chatbot/api/v1/visitors
{
"tenant_name": "Silverflow",
"visitor_name": "Jan Jansen",
"plate_number": "12ABC3",
"start_time": "2026-07-10T09:00:00Z",
"end_time": "2026-07-10T17:00:00Z"
}
Response · 201
{
"status": "success",
"message": "Visitor booking created",
"booking": {
"id": 142,
"plate_number": "12ABC3",
"visitor_name": "Jan Jansen",
"start_time": "2026-07-10T09:00:00+00:00",
"end_time": "2026-07-10T17:00:00+00:00",
"status": "active"
}
}
When the location is full
If no visitor slots are available for that window, the response is 409 with an alternative_slots array proposing up to three nearby free timeslots. For a multi-day recurring request, days that don't fit go into a skipped array while the rest are still created ("status": "partial").
Retrieve visitor bookings for your tenant.
| Query param | Type | Description |
|---|---|---|
| tenant_name | string | Your tenant. required |
| status | string | Filter: active, arrived, completed, cancelled. |
| from_date | ISO 8601 | Start of the window. Default: now. |
| to_date | ISO 8601 | End of the window. Default: 7 days from now. |
| recurring_only | bool | true — only the parent bookings of recurrences. |
| include_occurrences | bool | true — include automatically generated copies of recurring bookings. |
Response · 200
{
"status": "success",
"count": 1,
"bookings": [{
"id": 142,
"plate_number": "12ABC3",
"visitor_name": "Jan Jansen",
"start_time": "2026-07-10T09:00:00+00:00",
"end_time": "2026-07-10T17:00:00+00:00",
"status": "active",
"recurrence_rule": null,
"is_recurring_copy": false
}]
}
Update a booking. All body fields are optional; send only what you want to change.
| Field | Type | Description |
|---|---|---|
| plate_number | string | New license plate for the visitor. |
| visitor_name | string | New name of the visitor. |
| start_time | ISO 8601 | New start time. |
| end_time | ISO 8601 | New end time. |
| recurrence_end_date | ISO 8601 | Change when a recurring series stops (parent booking only). |
Request
PUT /chatbot/api/v1/visitors/142
{
"tenant_name": "Silverflow",
"plate_number": "99XYZ9"
}
Cancel a booking.
| Query param | Type | Description |
|---|---|---|
| tenant_name | string | Your tenant. required |
| cancel_series | bool | true — cancel all future recurrences in a series, whether you pass the parent booking or a copy. Past recurrences are left untouched. |
Request
DELETE /chatbot/api/v1/visitors/142?tenant_name=Silverflow
Create a booking and receive a self-registration link. Send the link to the visitor; they open it to register their own license plate. The invitation expires at end_time. Accepts the same fields as POST /visitors (including recurrence — one link per weekday series).
Response · 201 (shape)
{
"status": "success",
"booking": { "id": 143, /* … */ },
"invitation_url": "https://your-instance.revtek.nl/invite/<token>"
}
Recurrence
Bookings can repeat. Add the recurrence fields to POST /visitors and the system creates future recurrences automatically.
weekly— every 7 days.biweekly— every 14 days.monthly— the same date every month.
For weekly/biweekly, each weekday in recurrence_weekdays becomes its own independent series. New recurrences are generated automatically as earlier ones near their end, up to recurrence_end_date (or indefinitely if it's absent). Use GET /visitors?recurring_only=true to see the parent bookings and include_occurrences=true for the generated copies.
Request — weekdays Mon/Wed/Fri, until March
POST /chatbot/api/v1/visitors
{
"tenant_name": "Silverflow",
"visitor_name": "Contractor",
"start_time": "2026-07-13T08:00:00Z",
"end_time": "2026-07-13T18:00:00Z",
"recurrence_rule": "weekly",
"recurrence_weekdays": [0, 2, 4],
"recurrence_end_date": "2026-03-01T00:00:00Z"
}
License plates
For plates with permanent access rather than a time-bound booking. For temporary visitor access, prefer visitor bookings.
Add a permanent license plate to your tenant.
| Field | Type | Description | |
|---|---|---|---|
| tenant_name | string | required | Your tenant. |
| plate_number | string | required | The plate to add. |
| person_name | string | optional | Who the plate belongs to. |
Request
POST /chatbot/api/v1/plates
{
"tenant_name": "Silverflow",
"plate_number": "12ABC3",
"person_name": "Rogier de Vries"
}
Response · 201
{
"status": "success",
"message": "License plate added",
"plate": { "id": 57, "plate_number": "12ABC3", "tenant_name": "Silverflow" }
}
Duplicate plates
If the plate already exists anywhere in the system, the response is 409.
Retrieve all license plates for your tenant. Query param: tenant_name.
Response · 200
{
"status": "success",
"count": 1,
"plates": [{
"id": 57,
"plate_number": "12ABC3",
"person_name": "Rogier de Vries",
"is_vip": false,
"expiry_date": null
}]
}
Update the plate or the name. The body accepts plate_number and/or person_name, plus tenant_name.
PUT /chatbot/api/v1/plates/57
{ "tenant_name": "Silverflow", "person_name": "R. de Vries" }
Permanently delete a license plate from your tenant. Query param: tenant_name.
DELETE /chatbot/api/v1/plates/57?tenant_name=Silverflow
Availability & occupancy
Read-only endpoints to check capacity before you book.
Live occupancy for your tenant — regular, VIP, and visitor slots with current counts and limits. Query param: tenant_name.
Response · 200
{
"status": "success",
"tenant": "Silverflow",
"occupancy": [{
"parking_object": "Willem Fenengastraat",
"visitor_occupancy": 2,
"visitor_limit": 5,
"available_visitor_slots": 3
}],
"summary": { "total_occupancy": 2, "available_slots": 3 }
}
Available visitor timeslots for a day (hourly, 08:00–20:00).
| Query param | Type | Description |
|---|---|---|
| tenant_name | string | Your tenant. required |
| date | ISO date | Day to check. Default: today. |
| duration_hours | int | Length of each timeslot. Default: 4. |
Response · 200
{
"status": "success",
"date": "2026-07-10",
"duration_hours": 4,
"available_timeslots": [{
"start_time": "2026-07-10T08:00:00+00:00",
"end_time": "2026-07-10T12:00:00+00:00",
"available_slots": 3, "total_slots": 5
}]
}
List the tenants your API key may act on. With a tenant-bound key, that's only your own tenant — handy for confirming the exact name for other calls.
Webhooks
Optional. Register a URL and we'll send a POST whenever parking events occur — so your systems react in real time instead of polling.
Register a webhook.
| Field | Type | Description | |
|---|---|---|---|
| name | string | required | A label for this webhook. |
| url | string | required | Where we POST the event. |
| event_type | string | required | One of the event types below. |
| secret | string | optional | HMAC-SHA256 signing secret; payloads are signed when this is set. |
| tenant_name | string | optional | Only fire for this tenant. |
Event types
Other webhook endpoints: GET /webhooks (list yours), DELETE /webhooks/{id} (delete one), and GET /webhooks/event-types (list valid types — no auth required).
Public health check. Handy for uptime monitoring.
{ "status": "ok", "service": "Parking Manager Chatbot API", "version": "1.0.0" }