ParkedIn Fleet API — developer guide
How your rental system integrates with ParkedIn: send licence plates to manage parking operators' whitelists (Feature 1), and receive licence plate scan events from those operators (Feature 2).
Overview
Access is set up by ParkedIn after a contract is signed — there is no self-service sign-up. At onboarding we create your company account, link it to your parking operators, give you your API key and, if you use scan events, register your webhook URL and give you its signing secret.
| Direction | What | How |
|---|---|---|
| You → ParkedIn | Add / remove licence plates (Feature 1) | POST /vehicles, DELETE /vehicles, GET /vehicles |
| ParkedIn → you | Licence plate scan events (Feature 2) | Signed POST to your webhook URL |
Base URL & authentication
All requests go to https://api.parkedin.net and must carry your API key in the Authorization header using the Bearer scheme:
Authorization: Bearer fpk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
- Your key starts with
fpk_live_. It is shown to you once at onboarding; ParkedIn stores only a hash and cannot retrieve it again. If it is lost or exposed, contact us for a new one. - Keep the key on your server. Never put it in a browser, mobile app or public repository.
- Every request only ever sees and changes your own company's vehicles.
Licence plate format
- 2 to 15 characters: letters, digits, hyphens and spaces (
A–Z a–z 0–9 - space). - ParkedIn normalises every plate: trimmed, upper case, spaces turned into
-."zh 123 ab"is stored and returned as"ZH-123-AB". - Responses and scan events always use the normalised form — match against it in your system.
Add vehicles
Adds one or more licence plates to your active fleet. Each new or re-activated plate is passed on to every parking operator linked to your account.
Request
curl -X POST https://api.parkedin.net/vehicles \
-H "Authorization: Bearer $PARKEDIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"vehicles": [
{ "plate": "ZH-123-AB" },
{ "plate": "zh 456 cd" },
{ "plate": "BE-789-EF" }
]
}'
| Field | Type | Notes |
|---|---|---|
vehicles | array, 1–1000 items | Required |
vehicles[].plate | string | Required. See plate format |
Response 200
{
"added": ["ZH-123-AB", "ZH-456-CD"],
"reactivated": ["BE-789-EF"],
"already_active": [],
"summary": { "requested": 3, "newly_active": 3, "unchanged": 0 }
}
added— new plates.reactivated— plates you removed earlier and are adding again.already_active— no change.- Adding a plate that is already active is harmless, so a request can safely be repeated.
Remove vehicles
Removes one or more licence plates from your active fleet. The removal is passed on to every linked parking operator. Note the body is a plain list of plate strings.
Request
curl -X DELETE https://api.parkedin.net/vehicles \
-H "Authorization: Bearer $PARKEDIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "vehicles": ["ZH-123-AB", "ZH-999-ZZ"] }'
| Field | Type | Notes |
|---|---|---|
vehicles | array of strings, 1–1000 items | Required. Plates to remove |
Response 200
{
"removed": ["ZH-123-AB"],
"not_found": ["ZH-999-ZZ"],
"already_inactive": [],
"summary": { "requested": 2, "newly_inactive": 1, "unchanged": 1 }
}
- A removed plate is marked
inactive, not deleted. Add it again withPOST /vehiclesto re-activate it. - Plates you never added come back in
not_found; the request still succeeds.
List vehicles
Returns your own vehicle list — useful to reconcile ParkedIn against your fleet system.
curl "https://api.parkedin.net/vehicles?status=active&limit=500&offset=0" \
-H "Authorization: Bearer $PARKEDIN_API_KEY"
| Query parameter | Values | Default |
|---|---|---|
status | active, inactive, all | active |
limit | 1–500 | 500 |
offset | 0 or more | 0 |
{
"vehicles": [
{ "plate": "ZH-456-CD", "status": "active",
"added_at": "2026-11-14T09:12:03.000Z", "updated_at": "2026-11-14T09:12:03.000Z" }
],
"count": 1, "limit": 500, "offset": 0
}
Results are newest first. To page through a larger fleet, increase offset by limit until count is less than limit.
Batches & call volume
- Batch updates are supported.
POSTandDELETE /vehicleseach accept up to 1000 plates per request. For an initial load of a larger fleet, send several requests of up to 1000 plates. - All or nothing. Each request is applied in one transaction: if it fails, none of its plates are changed, and it is safe to send again.
- Response time. Allow 1–2 seconds per request; larger batches take longer. Use a client timeout of at least 30 seconds.
- Send in sequence. Send requests one after another rather than in parallel, and prefer one batch over many single-plate calls.
Errors
Errors return JSON with an error code and a readable message. Validation errors also list each problem in details:
{
"error": "bad_request",
"message": "Validation failed",
"details": [ { "path": "vehicles.0.plate", "message": "Plate number contains invalid characters" } ]
}
| Status | error | Meaning | What to do |
|---|---|---|---|
200 | — | Success | — |
400 | bad_request | Invalid JSON, invalid plate, empty list, or more than 1000 items | Fix the request; do not retry unchanged |
401 | unauthorized | Missing, malformed or wrong API key | Check the Authorization header |
403 | unauthorized | Your account is not active | Contact ParkedIn |
500 | internal_server_error | Problem on our side; we are alerted automatically | Retry later with backoff — repeating a request is safe |
Scan event webhook
When a linked parking operator's camera reads one of your active plates and the operator sends that scan to ParkedIn, we send it to the HTTPS webhook URL registered for your company. Your webhook URL and signing secret are set up at onboarding; contact us to change them.
Payload
POST <your webhook url>
Content-Type: application/json
X-Event-Type: vehicle.scanned
X-Event-Id: 7f3d1e6a-8b2c-4d9e-a1f0-2c3b4a5d6e7f
X-Signature: 9c1e… # HMAC-SHA256 of the raw body, hex
{
"event_id": "7f3d1e6a-8b2c-4d9e-a1f0-2c3b4a5d6e7f",
"event_type": "vehicle.scanned",
"plate_number": "ZH-123-AB",
"location_id": "zn-01",
"location_name": "Zurich Nord",
"scanned_at": "2026-07-04T14:32:07.000Z",
"scan_type": "entry",
"operator_id": "…",
"operator_name": "Zurich Parking Nord"
}
| Field | Meaning |
|---|---|
event_id | Unique per scan, and the same on every retry. Use it to ignore repeats |
plate_number | Normalised plate, as on your vehicle list |
scanned_at | When the camera read the plate (ISO 8601, UTC). Use it for vehicle location and as the return time |
scan_type | entry, exit or unknown |
location_id, location_name | The operator's facility |
operator_id, operator_name | The parking operator that sent the scan |
Events can arrive out of order — order them by scanned_at.
Verify the signature
Compute HMAC-SHA256 of the raw request body (the exact bytes received, not re-serialised JSON) with your webhook secret, hex-encode it, and compare it to X-Signature in constant time. Reject the request if they differ.
// Node.js
import crypto from "node:crypto";
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const ok = expected.length === signature.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
# Python
import hmac, hashlib
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, signature_header)
Retries & duplicates
Answer with any 2xx as soon as you have stored the event, and do the processing afterwards. We wait up to 8 seconds.
| Your response | What ParkedIn does |
|---|---|
any 2xx | Delivered; this event_id is not sent again |
408, 429, any 5xx, timeout, connection error | Retried automatically — up to 5 attempts over about 12 minutes, then held and our on-call team is alerted |
any other 4xx, or a redirect (3xx) | Treated as rejected; not retried, our on-call team is alerted |
- Delivery is at least once: the same
event_idcan arrive more than once. Store processedevent_ids, ignore repeats, and still answer2xx. - Redirects are not followed — register the final HTTPS URL.
- A scan we could not deliver is kept and can be re-sent once the cause is fixed.
What happens after a call
| Operator connection | When your plate changes reach the operator |
|---|---|
| REST API | Usually within about a minute of your call |
| SFTP file | In the next nightly file (full current list, delivered between 00:30 and 07:00 Zurich time) |
Which operators you are linked to, and how each is connected, is agreed at onboarding. Questions? hello@parkedin.io