Authentication
Every request authenticates with a Bearer token — either a business API key or a user access token.
Two token types
- Business API key — a long-lived key bound to one business, generated in the dashboard. Use it for server-to-server integrations (your dispatch system, a website, a trip booking widget on your own server).
-
User access token — a short-lived token obtained by logging in
with
POST /api/v1/auth/login. It acts as a person and can reach every business that person can access. This is what the Destination Tools mobile apps (operator and driver) use.
Both are sent the same way, and every endpoint accepts either unless noted otherwise
(the /api/v1/auth/* and /api/v1/me* endpoints are user-token only).
Sending your token
Send the token in the Authorization header on every request.
Requests without a valid token receive a 401 response.
Header
Authorization: Bearer YOUR_API_KEY
401 Response
{
"error": {
"type": "unauthorized",
"message": "Missing API key."
},
"meta": { "request_id": "req_1a2b3c4d5e6f7a8b" }
}
Generating and revoking keys
Manage keys in the dashboard under Settings → Integration. You can create multiple keys and give each a label so you can tell your integrations apart. Each key shows when it was created and last used.
- Create a key per integration or environment (e.g. staging vs production).
- Revoke a key by deleting it — the change takes effect immediately.
User login & refresh tokens
POST /api/v1/auth/login exchanges an email and password for an
access token (a 15-minute JWT) and a refresh token
(valid 30 days). When the access token expires, exchange the refresh token at
POST /api/v1/auth/refresh for a new pair.
Refresh tokens rotate: each refresh invalidates the token you
presented and returns a new one. Reusing an already-rotated refresh token is treated
as theft and revokes the whole session — the user must log in again.
POST /api/v1/auth/logout revokes the session immediately.
Native sign-in is supported for both platforms:
POST /api/v1/auth/google with a Google ID token, and
POST /api/v1/auth/apple with an Apple identity token. Both return the
same payload as /auth/login.
Neither creates an account. They link to an existing user by
verified email; an unrecognised identity returns 403 no_account rather
than signing someone in with access to nothing. An administrator creates the
account first.
Two platform details worth knowing. A native Google client has its own OAuth client
id, and its ID token's audience is that id — not the web one — so every native
client id must be listed in the server's google_mobile_client_ids or
verification fails. And Apple sends email and name only on the
first authorization: the token's subject is stored on link and matched from
then on, so send full_name on that first call because there is no
second chance. An Apple "Hide My Email" relay address cannot be matched to an
operator record and is refused with no_account.
Authenticating with no reachable business returns 403 no_business.
Previously this succeeded and then failed on every scoped call.
Driver accounts
account_type D is a driver: an app-only account with no
back-office access. Driver principals are default-denied across the
API — they reach only /api/v1/me/driver/* and their own profile, and
receive 403 everywhere else, including trips, invoices, customers and
dispatch.
Driving is a capability, not a role: it comes from a drivers
record linked to the user, so an administrator who also drives keeps every operator
right and gains driver access as well. GET /api/v1/me reports
is_driver and driver_id so a client can decide what to
show without a second call. Driver responses deliberately omit pricing.
Login
POST /api/v1/auth/login
{ "email": "[email protected]", "password": "•••", "device_name": "Pixel 9" }
{
"data": {
"token_type": "Bearer",
"access_token": "eyJ0eXAiOiJKV1Qi...",
"expires_in": 900,
"refresh_token": "rt_9f2c4e...",
"user": { "id": 42, "email": "[email protected]", "account_type": "A" },
"businesses": [ { "business_id": 7, "business_name": "Executive Transfers" } ]
}
}
Choosing the business
A user may have access to several transport businesses. Select the one to act on with
the X-Business-Id header (any id from GET /api/v1/me/businesses).
With access to exactly one business the header can be omitted. Without it, and with
several businesses available, requests receive a 422 business_required
error listing the choices.
Request
GET /api/v1/trips
Authorization: Bearer eyJ0eXAiOiJKV1Qi...
X-Business-Id: 7
Scope
An API key is bound to the business that created it; a user token is bound to the
businesses the user can access. Every endpoint is automatically scoped to the acting
business — you can only read and write your own trips, cars, drivers and customers.
Requests for another business's resources return 404, and selecting a
business the user cannot access returns 403.
Transport security
All requests must use HTTPS. Requests over plain HTTP are not supported. Never embed a business API key in client-side code (a browser app or mobile binary) where end users could extract it — call the API from your server. Mobile and desktop apps should use the user login flow instead: tokens are per-person, short-lived and revocable.