Fleet, reservations, staff & video support
Your account was created for you, so somebody else knows the password you just used. Choose one only you know.
Apply for a Hannk partner account. Back to sign in
Every application is verified by email and reviewed by the Hannk team before activation.
Zone breaches, impacts and vehicle conditions that need someone to look. Newest first, and every one of them is stored — a warning is opened when it first appears and closed when it stops being true, so the age beside it is how long it has really been going on.
Pick-up and return inspections. Select a booking's pair to compare photos side by side.
Impacts are detected automatically from telemetry, pushed by the box, or logged by staff after a call - each one gets a full reconstruction: speed graph, deceleration, bearing and trail.
Loading…
Geofence breaches, commands, transfers and telemetry alerts.
View and manage all reservations in one place. Search, filter and track every stage of the rental journey.
| Booking ref | Customer | Vehicle | Branch | Collection | Return | Status | Channel |
|---|
Locations, opening times, contacts, video and terms - one branch at a time.
Draw your 3D GeoLayers (or auto-add whole countries), give each a daily driving price, and every vehicle is watched against the zones its reservation permits. Crossing out triggers a spoken in-car warning and a 3D GeoLayer event here.
Loading…
Everything waiting for somebody to look at: customer documents to approve, and the fines, tolls and charges to assign and collect.
| Uploaded | Customer | Booking ref | Document type | Branch | Status |
|---|
| Date | Type | Reference | Location | Amount | Customer | Booking ref | Status |
|---|
Every partner company on the platform. New applications wait here for approval after email verification; active companies can be suspended and reactivated at any time. The company is emailed on every decision.
Manage your Hannk subscription, vehicles, boxes and invoices all in one place.
Every order a rental company has placed - what to send, where, and who signed for it.
Loading…
What Hannk charges rental companies, what each of them is subscribed to, and every payment received.
Everything from your logo to the API, in the order you need it.
From signing in to taking your first unattended rental: your brand, your branches, your driving zones, your fleet, your reservations and the API.
Ten minutes of gathering now saves a second sitting later.
Hannk runs an unattended rental: your customer picks the car up, inspects it, signs the agreement and drives away without anybody at a desk. That only works if the system knows four things about your business - what you look like, where you operate from, where drivers may go, and what you are renting.
Have these to hand:
Your console is at https://hannk.cloud/admin.
My account → Branding
Your customer never sees a Hannk-branded screen if you do this: your logo sits at the top of every booking screen and on the emails they receive, and your splash screen is what they look at while the app opens.
This is not a gate. Nothing is locked until you upload them - a company that never brands simply shows Hannk's own styling to its customers. Most operators would rather that was their own.
| Asset | Exact size | Format | Maximum file |
|---|---|---|---|
| Logo | 480 × 160 px, horizontal | PNG only, transparent background | 300 KB |
| App icon, large | exactly 512 × 512 px | PNG only, opaque | 300 KB |
| App icon, small | exactly 192 × 192 px | PNG only, opaque | 100 KB |
| Splash screen | 1080 × 1920 px (9:16 portrait) | PNG, JPEG or WebP | 800 KB |
The same page takes a primary and an accent colour. These tint the buttons and headings a customer sees. Pick the two from your own brand guide rather than leaving the defaults - it is the cheapest change on this page.
Branches
A branch is a place a customer collects from. Everything else - fleet, reservations, zones, video hours - hangs off one, so this is the section to do properly first.
ALC-01) - it is how the API refers to this branch, so use the
code your own systems already use.A branch will save without these and nothing will complain. Each one switches something off when it is missing:
| Setting | What stops working without it |
|---|---|
Radius (miles) | Defaults to 60 miles. Every driver from this branch is fenced at 60 miles whether that suits your market or not. |
Local driver area | Nobody is ever treated as local. Every customer is asked for a passport rather than a local ID, and pays the standard deposit. Draw the area on the map under 3D GeoLayers on the branch - a plain radius in miles is offered as well, but a drawn area always wins over it. |
Zone chart | Bookings from this branch inherit no driving zones - the map shows the customer nothing and they are bounded only by the plain mileage radius. |
Telephone numbers, pick-up and return instructions | A customer whose day goes wrong has nothing to ring and no instructions to follow. |
Branches → 3D GeoLayers
A zone is an area you draw on a map: an island, a region, a city, a country. Zones do two jobs at once - they tell the driver where they may go, and they let you charge for the places you would rather they told you about first.
The editor opens over the branch record, so closing it puts you back exactly where you were.
Fleet
Fleet → Add a vehicle. What it asks for:
| Field | Why it matters |
|---|---|
| Reg plate | How everybody - you, the customer, the agreement - identifies the car. |
| ACRISS code | Four letters, e.g. CDMR. This is what lets Hannk assign a car automatically when a reservation asks for a class rather than a specific plate. |
| Brand, model, year, seats | Shown to the customer when they collect. |
| Transmission and fuel | Auto or manual; petrol, diesel, hybrid or electric. |
| Branch | Where the car lives. A one-way rental moves it to the drop-off branch automatically on return. |
| Telematics device ID and access token | Optional. Without a device the car still rents - you simply have no live location, no lock and unlock, and no journey history. |
Fleet has a spreadsheet upload for the first load. Prepare a sheet with one row per car and the columns named above, and upload it rather than typing fifty cars in.
Reservations
A reservation is one rental: who, which car, from when, to when, and where from.
Hannk returns a voucher link for that reservation. Send it to the customer - it is what opens their rental on their phone: the car, the inspection, the agreement and the keys. One link per reservation.
Give every reservation your own booking reference. It is how you update or cancel that rental later without having to look up a Hannk id, and it stops a retried request creating the booking twice.
Control centre
The live map: every car you have, where it is, and what it is doing. Colour tells you condition at a glance, and warnings - zone breaches, impacts, vehicle conditions - are on the map with the vehicle rather than on a separate page.
Click a car to open its panel: the rental it is on, the customer, its journeys, and the lock and unlock controls if it has a telematics device.
Documents & fines
Two things in one page, because they are the same job - paperwork attached to a rental or a customer.
Agents
Add the people who work your desks. Each gets their own login - never share one.
| Role | What it can reach |
|---|---|
| Administrator | Everything, including your staff, what you pay Hannk, and your API keys. |
| Agent | The desk: reservations, video calls, the control centre, customers, branches, fleet, documents and fines. Never your staff list, your account or your API keys. |
Within the agent role you can tick what each person may do - taking video calls, assigning vehicles, unlocking cars, editing the fleet, editing branches, inspections, events. Give somebody only what their job needs.
An agent can also be pinned to one branch, several, or left company-wide.
Video calls
A customer standing next to the car with a problem can start a video call from the app. It arrives in the console, and whoever is on the desk answers it, sees the car through their camera and talks them through it.
On each branch: does this branch answer video calls, and when. Either around the clock or set hours per day, read in that branch's own timezone. You can also limit it to the rental window - from a few hours before pick-up to a few hours after return.
API - administrators only
If you already run a reservation system, you do not have to type anything twice. The API pushes your branches, fleet and reservations into Hannk from whatever you use today.
The API page shows your key - it starts hk_. Send it on every request:
Base URL: https://hannk.cloud
Header: Authorization: Bearer hk_your_key_here
Content: application/json
curl -X POST https://hannk.cloud/api/v1/reservations \
-H "Authorization: Bearer hk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"external_ref": "YOUR-BOOKING-12345",
"from": "2026-11-14T10:30",
"to": "2026-11-18T09:00",
"acriss": "CDMR",
"pickup_branch_id": "ALC-01",
"customer_name": "Maria Gomez",
"customer_email": "maria@example.com",
"allowed_zones": ["Mainland"]
}'
The reply carries the voucher link to send your customer, and a
missing_info list naming anything the rental still needs before it can run
unattended. Read that list - it is the difference between a booking that works on the
day and one that stops at the car.
from and to are required, as 2026-11-14T10:30.
Minutes matter, and to must be after from.acriss (Hannk picks a matching free
car), plate, or vehicle_id (you pick it).external_ref. It makes the request safe to
retry - a repeat is refused as a duplicate rather than creating a second booking - and
it is how you update the rental later.problems array, so you fix
them in one pass instead of four round trips.GET /api/v1/zones lists what
you may name. An unrecognised name is refused rather than silently dropped, because a
quietly missing zone is a driver breaching a 3D GeoLayer later.| You want to | Call |
|---|---|
| Create, change or cancel a reservation | POST / PUT / DELETE /api/v1/reservations, by your own reference |
| Load many reservations at once | POST /api/v1/reservations/import |
| Attach a specific car to a booking | POST /api/v1/reservations/{ref}/vehicle |
| Fetch the signed rental agreement | GET /api/v1/reservations/{ref}/agreement |
| See where the car went | GET /api/v1/reservations/{ref}/journeys |
| Push or list your fleet | POST / GET /api/v1/vehicles |
| Lock, unlock or locate a car | POST /api/v1/vehicles/{id}/command, GET .../status |
| Move a car to another branch | POST /api/v1/vehicles/{id}/branch |
| Create or update branches | POST / PUT /api/v1/branches, plus /import |
| List your zones | GET /api/v1/zones |
| Get a branded voucher link | GET /api/v1/voucher-link |
| Poll what has happened | GET /api/v1/events |
The API page carries the full reference, with a worked example for every call, inside the console next to your key.
Run down this list once. It is the difference between a first rental that works and a first rental that becomes a phone call.
| ✓ | Check | What happens if you skip it |
|---|---|---|
| Logo, both icons and splash uploaded | Your customers see Hannk's branding, not yours | |
| Every branch has a map pin on the real pick-up point | Customers navigate to the wrong side of the airport | |
| Every branch has a radius that suits its market | Everybody is fenced at 60 miles | |
| Every branch has a zone chart | The customer's map shows no driving area at all | |
| Every branch has telephone numbers and instructions | Nothing to ring when the day goes wrong | |
| Local radius and local deposit set | Local customers are asked for passports and charged full deposits | |
| Opening hours on every branch | Customers arrive when nobody expects them | |
| Video support answered honestly - on with hours, or off | Calls ring with nobody there | |
| Every car has an ACRISS code | Hannk cannot assign a car by class | |
| Staff added with the right roles | People share a login, and you cannot tell who did what | |
| One test reservation, taken end to end on a real phone | You find out with a real customer instead |
Stuck on something? Write to sales@hannk.com and say which branch or reservation you are looking at - it is the fastest way to a useful answer.
This guide describes the Hannk console as it stands today. Screens improve; if something is where this guide does not expect it, the guide is the thing that is out of date, and we would like to know.
Use this from your own rental/management system. Keep it secret - anyone holding it can control your fleet.
-
What partner systems are NOT sending on reservations - your shopping list for the API conversation. The create-reservation response also returns missing_info so their developers see it immediately.
Loading…
Each vehicle has its own device token, supplied with its Hannk device kit. Send it with the vehicle - the spreadsheet has a device_token column, the API takes a device_token field, and the vehicle Edit form has a token box. Stored securely server-side, never shown again.
Fleet-wide fallback (optional): if your account uses one shared token instead, paste it below and it covers any vehicle without its own.
1. Spreadsheet: Fleet tab → "Upload your fleet from a spreadsheet" - download the template, fill a row per vehicle, upload. Easiest for getting started.
2. One by one: Fleet tab → "+ Add vehicle".
3. Automatically from your own system (below): call our API whenever a vehicle is added or changed in your rental system - Hannk stays in sync with no manual work.
Base URL: https://hannk.cloud · every request needs the header: Authorization: Bearer YOUR_API_KEY
Vehicle fields: plate (required unless telematics_id given), telematics_id (the device ID from your Hannk device kit - links the vehicle to live data), device_token (that vehicle's own access token, from the same kit), brand, model, year, acriss (used for automatic reservation assignment), day_rate, branch (name - created automatically if new).
Worked example - add a vehicle from your system:
curl -X POST https://hannk.cloud/api/v1/vehicles \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"plate":"AB12 CDE","telematics_id":"device-0001",
"brand":"Toyota","model":"Corolla","year":2024,
"device_token":"eyJhbGciOi.this-vehicles-token",
"acriss":"CDMR","day_rate":40,"branch":"Blackburn Hub"}'
PUT /api/v1/branches/{your-code} creates and updates - send only the fields you want to change. No coordinates needed: Hannk geocodes the address for you. Minimal example:
curl -X PUT https://hannk.cloud/api/v1/branches/HER01 \
-H "Authorization: Bearer hk_yourkey" -H "Content-Type: application/json" \
-d '{"name":"Right Cars - Heraklion Airport",
"address":"Heraklion Airport, Crete, Greece"}'
A branch is not finished until these are set. They are not required by the API - it will accept a branch without them - but each one turns a feature off when it is missing:
| Field | What stops working without it |
|---|---|
| local_radius_miles | Nobody is ever treated as local. Every customer is asked for a passport rather than a local ID, and pays the standard deposit. The local-customer pricing on a reservation is never applied. local_deposit is still accepted on a branch, but it is now a per-vehicle figure and the branch value is only used for cars that have none of their own. |
| zone_chart (or zone_ids) |
Bookings from this branch inherit no driving zones, so the map shows nothing and the driver is bounded only by the plain mileage radius below. |
| geofence_miles | Defaults to 60 miles. Leave it wrong and every driver from this branch is fenced at 60 miles whether that suits the market or not. |
| address → lat/lng | The branch has no position, so neither the local-customer check nor the mileage geofence can work at all. Send the address and Hannk geocodes it - send lat/lng only to correct a bad geocode. |
| pickup_instructions return_instructions |
The customer arrives at the branch with no idea where to go or what to do. This is the single most complained-about gap. |
| phones[] | Nothing for the customer to call when something goes wrong on the day. |
| opening_hours | The app cannot tell a customer whether the branch is open, and out-of-hours handling has nothing to work from. |
| driver_age_rules | There is no minimum or maximum driver age and no young- or senior-driver surcharge. Anyone of any age books at the standard rate, and the branch discovers it at the desk. |
| addl_driver_cost | Additional drivers are charged a built-in €35 fallback instead of your price. Still accepted on a branch, but this is now a per-vehicle figure: the branch value applies only to cars that have none of their own. |
| zone_breach_cost | Leaving a permitted zone costs the driver nothing. The breach is recorded and shown, but no charge is raised, so the geofence has no teeth. |
| agreement_template_id | The branch falls back to the company default template. If there is no default, there is nothing for the customer to sign and the handover stops there. |
| video-support hours | Video support advertises itself as open 24/7. Customers call at 3am, nobody answers, and the branch wears it. |
| postcode country |
The geocode is less certain and can place the branch in the wrong town - which then silently breaks the local-customer check and the mileage geofence above. |
Nothing on this list is optional. The API accepts a branch without them so an integration can be built in stages - but a branch is not live until every row above is filled in.
The whole branch endpoint, in one place:
GET /api/v1/branches every branch you have
GET /api/v1/branches/{code} one of them
PUT /api/v1/branches/{code} create it, or change only the fields you send
DELETE /api/v1/branches/{code} remove it
POST /api/v1/branches/import the whole network at once, JSON or XML
Changing one thing. A PUT to a branch that already exists only writes what is in the body - everything else keeps the value it has, and the address is not re-geocoded unless you send a new one:
curl -X PUT https://hannk.cloud/api/v1/branches/HER01 \
-H "Authorization: Bearer hk_yourkey" -H "Content-Type: application/json" \
-d '{"phones":["+30 2810 000000"],"opening_hours":{"sun":"closed"}}'
Sending a field as "" is blanking it,
not omitting it - name and address are refused that way. DELETE is
refused while the branch has current or future reservations; move or complete them first.
GET /api/v1/features what this account is switched on for GET /api/v1/telematics/devices the device kits issued to you, and what each is fitted to
Call features first: it is how an integration finds out what to build rather than discovering it from a 403.
POST /api/v1/vehicles add one (or update by plate / telematics_id)
GET /api/v1/vehicles your fleet
GET /api/v1/vehicles/{reg} one car, in full
PUT /api/v1/vehicles/{reg} change only the fields you send
DELETE /api/v1/vehicles/{reg}?mode=... retire (keeps everything) or erase
GET /api/v1/vehicles/{reg}/everything one call: where it is, who has it, what it is complaining about
GET /api/v1/vehicles/{reg}/warnings ?state=open|cleared|dismissed|all
GET /api/v1/vehicles/{reg}/reservations ?from= &to= &status= &page=
GET /api/v1/vehicles/{reg}/events what has been done to it, and by what
GET /api/v1/vehicles/{reg}/status live doors, fuel, position
GET /api/v1/vehicles/{reg}/history every telematics sample, paged
GET /api/v1/vehicles/{reg}/journeys the same data as trips
POST /api/v1/vehicles/{reg}/command lock, unlock, immobilise
POST /api/v1/vehicles/{reg}/branch move it between branches
{reg} is the registration or the Hannk id - either works, everywhere.
One call for a fleet board.
/everything returns the record, the last position, the reservation it is out on,
the next one booked, every open warning and the totals - so a board does not have to stitch
five calls together and invent its own idea of what "on rental" means:
curl https://hannk.cloud/api/v1/vehicles/AB12CDE/everything \ -H "Authorization: Bearer hk_yourkey"
Amending. A PUT writes only what is in
the body. plate cannot be blanked and cannot be moved onto another car in your
fleet. branch goes through a real branch transfer, not a column write, because it
starts and stops zones and local pricing. telematics_id is read-only here - fit a
box in the console, or POST with device_id.
Removing. Say which you mean:
mode=retire takes it out of the fleet and keeps every record, and can be undone;
mode=erase also needs confirm_plate and is final - reservations keep
the registration and the journey history moves to the archive under it. Both are refused while
the car is out on rental, and while a box is still fitted: that box stays on subscription and
belongs to the next car.
Everything Hannk has collected from the telematics unit, served back to the company that owns the car. Three shapes of the same data, because three different questions get asked.
GET /api/v1/reservations/{ref}/journeys what this customer did with the car
GET /api/v1/reservations/{ref}/telematics the same window, every sample
GET /api/v1/vehicles/{reg}/journeys trips, with routes to draw on a map
GET /api/v1/vehicles/{reg}/history every sample, paged
GET /api/v1/vehicles/{reg}/history.csv the same as a file
?from=2026-08-14&to=2026-08-21 a bare To date means the whole of that day
?event=ignition filter on part of the event name
?points=false journeys without the routes - much smaller
?page=1&page_size=500 history paging, 5000 max
Point them at the reservation call first. It is the one that answers the question a rental company actually asks, and its window runs from collection to return rather than from the booked times - so a car picked up two hours late and dropped early gives the real window, not the planned one. No date arithmetic for them to get wrong.
| Worth telling them | Why |
|---|---|
| Registrations, not ids | AB12 CDE, ab12cde, AB12CDE all work. They do not need to hold our numeric ids. |
| distance_source | odometer means the vehicle's own count; gps means we computed it from positions, which cuts corners and under-reads by 5-15%. A mileage charge should only be raised on an odometer figure. |
| ended_by | Says how a trip was bounded - ignition off is the unit telling us; stopped means we inferred it from a gap. Not every unit reports ignition. |
| An empty result is not an error | Every empty reply carries a note saying which it is: no telematics unit on the vehicle, or that period not synced yet. |
| Nothing here calls the telematics platform | These read our own store. A partner running a report can never trigger an outbound request that hangs, costs money, or rate-limits our sync. |
This is personal data. It is a record of where a named person drove. We scope every query to the company that owns the vehicle - but once it reaches them they are the controller, and they need a lawful basis, a privacy notice that mentions it, and a way to answer a subject-access request. Worth raising before they build a report that emails it around.
Full guide: TELEMATICS-API.md in the welcome pack.
PUT /api/v1/branches/:code create/update a branch (see green box)
GET /api/v1/branches list your branches
DEL /api/v1/branches/:code retire a branch
POST /api/v1/vehicles push/update a vehicle
{"plate":"AB12 CDE","telematics_id":"device-id",
"brand":"Toyota","model":"Corolla","year":2024,
"branch":"Blackburn - Hannk Hub","day_rate":40}
GET /api/v1/vehicles list your fleet
GET /api/v1/vehicles/:id/status live telematics
GET /api/v1/vehicles/:id/telemetry history (?limit=100)
GET /api/v1/events?since=ISO-DATE all vehicle events
POST /api/v1/vehicles/:id/command {"cmd":"lock|unlock|immobilize|release"}
POST /api/v1/reservations create a reservation (see blue box)
POST /api/v1/reservations/import many at once, JSON or XML (max 500)
GET /api/v1/reservations list (?include_cancelled=true)
GET /api/v1/reservations/:ref fetch one (includes charges[])
GET /api/v1/reservations/:ref/agreement
what the rental agreement is still missing
GET /api/v1/availability is there a car? (read-only, holds nothing)
PUT /api/v1/reservations/:ref create-or-update - send only what changed
{"vehicle":"auto"} re-allocates for new dates
POST /api/v1/reservations/:ref/vehicle {"plate":"AB12 CDE"} reassign
DELETE /api/v1/reservations/:ref cancel (?reason=... , ?purge=true)
GET /api/v1/zones the 3D GeoLayers you may name
GET /api/v1/voucher-link?ref=&broker= branded claim link for a reservation
The smallest booking that will be accepted. Everything else is optional - but anything left out comes back in missing_info, which is the agenda for the partner conversation.
curl -X POST https://hannk.cloud/api/v1/reservations \
-H "Authorization: Bearer hk_yourkey" -H "Content-Type: application/json" \
-d '{"external_ref":"RC000123",
"from":"2026-08-20T10:30", "to":"2026-08-27T09:00",
"plate":"AB12 CDE",
"branch_code":"LHR",
"customer":{"name":"Jane Smith","email":"jane@example.com"}}'
REQUIRED
from, to "2026-08-20T10:30" - minutes matter, to must be after from
customer.name, .email the driver. No account needed - one is created and they
are invited to claim the booking
external_ref optional, but SEND ONE. It is how a retry is recognised
instead of double-booking, and how PUT/DELETE address it
NOTHING ABOUT THE CAR IS REQUIRED. Name one or let Hannk choose.
THE VEHICLE - YOURS TO NAME, OR OURS TO ALLOCATE
registration the car you want. plate, registration_number, reg, vrm,
licence_plate and license_plate all mean the same thing,
so send whichever your own system calls it
vehicle_id our numeric id, if you have stored it
acriss narrow the allocation to a class - OPTIONAL
send none of them Hannk allocates any car free for those dates at that
branch
SPACING AND CASE ARE IGNORED. "AB11 CDE", "ab11cde" and "AB11-CDE" are one car,
so a difference in formatting is no longer read as a missing vehicle.
THE REPLY CONFIRMS WHICH CAR IT IS:
registration top level, on every create, amend and fetch
vehicle.registration the same value, beside vehicle.plate
assignment.mode "requested" if you named it, "allocated" if we chose
assignment.matched_on registration | vehicle_id | acriss | any_available
WHEN THERE IS NO CAR, the 409 says WHICH none, with a code you can branch on:
REGISTRATION_NOT_FOUND that registration is not in your fleet (404)
VEHICLE_ALREADY_BOOKED the car you named is out; clashes_with names the booking
NO_ACTIVE_VEHICLES your fleet has no active vehicles at all
NO_SUCH_ACRISS you do not operate that class; acriss_available lists
the ones you do
NONE_AT_BRANCH the cars exist, but none is standing at that branch
ALL_BUSY they exist and are all out; busy[] names what each is
on, and free_after is when the first one returns
Every vehicle belongs to a branch, and the customer collects from where the car
actually is. Sending a car that sits at another branch is refused, 409:
VEHICLE_NOT_AT_BRANCH - the reply names where it is and where you asked for
VEHICLE_HAS_NO_BRANCH - that car has no branch set at all
ASKING BEFORE YOU COMMIT
GET /api/v1/availability?from=...&to=...&branch_code=LHR&acriss=CDMR
Read-only. Answers available true/false, would_allocate.registration, and the
same code/reason as above when there is nothing. NOTHING IS HELD - another
booking can take the car between this call and your POST, so the create
allocates again and is the only answer that counts.
BRANCHES
branch_code | branch | pickup_branch_id by YOUR code, by name, or our id
dropoff_branch_code | dropoff_branch | dropoff_branch_id
A different drop-off makes it a ONE-WAY: the car changes home branch on the spot,
and the reply lists in one_way.affected_reservations every later booking that
still expects to collect it from the old branch. Those need updating.
MONEY AND COVER all optional, all full-rental figures
total, deposit_amount, excess_amount
included ["CDW","Unlimited mileage"] (array or one string)
deposit_insurance_daily per-day price if the customer's card refuses the deposit
and they take damage insurance instead
THE PRICED LINES charges: [] - up to 60, in the order they print
These ARE the items table of the rental agreement the customer signs. Nothing
is derived from the total: send none and the contract prints an empty table
and says so, which is honest but is not what you want a customer to sign.
[{"code":"RENT","description":"Vehicle rental, 7 days",
"qty":7,"unit_price":27,"amount":189,"payment_by":"A","remark":""},
{"code":"CDW","description":"Collision damage waiver",
"qty":7,"amount":0,"payment_by":"A","remark":"I"}]
description is required on every line; the rest are optional.
payment_by A = the agency has already taken it, C = the renter pays at the desk
remark I = included in the price, F = free of charge
On PUT, sending charges REPLACES the whole set. Leaving the key out changes
nothing - so an update that only moves a date cannot wipe the items table.
ADD-ON PRODUCTS products: [] - up to 40, what the desk will try to sell
Charges are what the customer IS paying for. Products are what they have been
OFFERED - a child seat, an extra driver, a full tank - and only reach the
agreement when somebody sells one. Until then they cost nothing and print
nothing, which is the point: a seat nobody took has no business on a contract.
[{"code":"SEAT","description":"Child seat",
"sales_description":"Group 1 seat, 9-18kg, fitted by the branch.",
"qty":1,"unit_price":25.00,"sold":false}]
description is required; the rest are optional.
sales_description what the desk says about it. Never printed on the agreement.
qty / unit_price priced for the WHOLE rental: unit_price x qty. For something
charged per day, send the number of days as the quantity.
The line total is worked out here and not read from you.
sold false (default) = offered. true = sold, and it appears in
the charges table above, priced, marked payment_by C.
On PUT, sending products REPLACES the whole set, and leaving the key out
changes nothing - so an update cannot wipe what a desk agent sold this
morning. Editing charges never disturbs a sold product's line, and selling a
product never disturbs a line you sent in charges.
LOCAL CUSTOMERS used INSTEAD of the above when the driver's
licence address is inside the branch's local radius
local_deposit_amount, local_excess_amount, local_included
Each falls through on its own - send a local deposit and leave the rest standard.
FUEL fuel_policy: like_for_like | full_to_full |
full_to_empty | empty_to_empty
Written any way you like - "Full/Full", "F-E",
"like for like", "prepaid" - it is normalised.
DRIVERS additional_drivers: 4 AT MOST, and that is four BESIDES the
main driver - the customer on the reservation is not one of
them. A fifth is refused, on create AND on update.
[{"name":"Jane Roe","dob":"1990-04-12","email":"jane@x.com",
"phone":"+4477...","licence_no":"ROE9004121JR9AB"}]
name is required; dob must be YYYY-MM-DD.
WHERE THEY MAY DRIVE
allowed_zones ["UK Mainland"] by zone name, chart name, or id.
Everywhere the customer has PAID to drive, INCLUDING where a
one-way ends: a cross-border drop-off is a zone like any
other and has to be in this list.
A name we do not recognise is REFUSED, not ignored -
a silently dropped zone is a 3D GeoLayer breach at the border,
charged to a customer who bought the right to be there.
GET /api/v1/zones lists yours by id, name and chart.
cross_border countries the driver may enter, and the daily price:
{"agreed":true,
"countries":[{"country":"France","daily_price":12},{"country":"BE","daily_price":15}],
"currency":"EUR","paid":true,"reference":"XB-77"}
Every country must have a 3D GeoLayer. If one does not exist, Hannk draws it from
the country's real boundary and prices it at the daily rate you send. Country
names or ISO codes both work. A daily price is required when agreed is true.
THE VOUCHER
broker your subdomain, e.g. "rightcars". The claim link is then
issued on rightcars.hannk.cloud so YOUR logo shows.
agreement_pdf_url a pre-completed agreement, fetched and filed
agreement_pdf_b64 ...or sent inline
WHAT THE AGREEMENT STILL NEEDS
GET /api/v1/reservations/:ref/agreement
Reads the operator's OWN published contract layout and resolves every field on
it against this reservation, so it is never a guess about what some contract
might want. It returns what would print blank and whose job each one is:
{"layout":"Classic one page", "version_no":2, "signed":false,
"charges":0, "ok":false,
"blank":[{"key":"customer.licence_no","label":"Driving licence number",
"where":"the customer",
"who":"their account and uploaded documents"}],
"lists":[{"key":"charges","label":"Charge lines","where":"the reservation"}]}
layout is null when the operator has published no layout at all.
THE REPLY 201 Created
{"ok":true, "action":"created", "reservation_id":41, "status":"upcoming",
"vehicle":{"id":28,"plate":"AB12 CDE","brand":"Kia","model":"Niro"},
"voucher_url":"https://rightcars.hannk.cloud/?claim=...",
"missing_info":["deposit_insurance_daily"],
"one_way":{...}}
WHEN IT IS REFUSED
400 something is wrong with the payload - problems[] lists EVERY fault at once
404 no vehicle with that plate / no branch with that code
409 duplicate external_ref, the car is already booked for those dates,
or the car is not at the collection branch
CHANGING ONE LATER PUT /api/v1/reservations/:ref - only the fields you send
Moving the dates or swapping the vehicle is checked for clashes BEFORE anything
is written; a clash returns 409 VEHICLE_NOT_AVAILABLE naming the reservation in
the way. Once the customer has collected the car, dates and vehicle are frozen -
money, cover and fuel policy can still be corrected.
CANCELLING DELETE /api/v1/reservations/:ref?reason=...
Cancels rather than deletes, and frees the vehicle immediately. Cancelling twice
is safe. Once the car has been collected it can no longer be cancelled by API
(409 ALREADY_COLLECTED) - the branch closes the rental.
MANY AT ONCE POST /api/v1/reservations/import
{"reservations":[ {...}, {...} ]} or XML with Content-Type: application/xml
Matched on external_ref, so sending the same file twice updates rather than
duplicates. Each row reports its own outcome; a partial failure is normal.
How this company runs - the agreement customers sign, your branding, email, and where the money goes. The partner API has its own page.
Who works here, what they can see, and when they are at a desk.
| Name | Role | Reports to | Branches | Work hours | Calls | Status |
|---|
The charts a branch can be assigned to.