API Docs
Dashboard

Mobile app quickstart

Build a native iOS or Android app where operators and drivers log in as themselves, switch between businesses, and run trips, dispatch and quotes from their phone.

Two ways to authenticate

The API accepts two kinds of Bearer token. A business API key is long-lived and bound to one business — great for server-to-server integrations, but never safe to embed in an app a user installs. A user access token is obtained by logging a person in, is short-lived, and can act on every business that person can access. Mobile apps use user tokens.

Every endpoint in the reference accepts a user token except the handful marked as API-key specific.

1. Log in

Exchange the user's email and password for an access token (a 15-minute JWT) and a refresh token (valid 30 days). The response also lists every business the user can act on — use it to render a business switcher. Pass an optional device_name so the user can recognise the session later.

Request

curl -X POST https://app.destination.dev/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "•••••••••",
    "device_name": "Pixel 9"
  }'

Response

{
  "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" },
      { "business_id": 9, "business_name": "City Chauffeurs" }
    ]
  }
}

2. Choose the acting business

Send the X-Business-Id header on every request. If the user has access to exactly one business you can omit it; with several available and none selected, the API returns 422 business_required listing the choices. Call GET /api/v1/me/businesses any time to refresh the switcher, and GET /api/v1/me for the signed-in user's profile.

Request

curl https://app.destination.dev/api/v1/dispatch/trips \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1Qi..." \
  -H "X-Business-Id: 7"

3. Keep the session alive

When a request returns 401 because the access token expired, exchange the refresh token for a fresh pair. Refresh tokens rotate: each call invalidates the token you sent and returns a new one, so always store the newest. Presenting an already-used refresh token is treated as theft and revokes the whole session — the user must log in again.

Store tokens in the secure store. Use the iOS Keychain or Android EncryptedSharedPreferences / Keystore — never plain preferences or disk.

Request

curl -X POST https://app.destination.dev/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{ "refresh_token": "rt_9f2c4e..." }'

Log out (revokes the session)

curl -X POST https://app.destination.dev/api/v1/auth/logout \
  -H "Content-Type: application/json" \
  -d '{ "refresh_token": "rt_9f2c4e..." }'

4. Sign in with Google

For native Google Sign-In, obtain a Google ID token on the device and exchange it for API tokens. The response matches /auth/login, with an extra needs_business_setup flag that is true on a brand-new account (prompt the user to create their transport business before they can dispatch).

Request

curl -X POST https://app.destination.dev/api/v1/auth/google \
  -H "Content-Type: application/json" \
  -d '{ "id_token": "eyJhbGciOi...", "device_name": "iPhone 16" }'

Build the operator app

With a user token and a selected business you can drive the whole operation:

  • Dispatch boardGET /api/v1/dispatch/trips for the day's jobs; assign with POST /api/v1/trips/{id}/drivers and watch GET /api/v1/drivers/locations live.
  • Trips — full CRUD: GET/POST /api/v1/trips, GET/PATCH/DELETE /api/v1/trips/{id}, plus PATCH /api/v1/trips/{id}/driver-status to move a job along.
  • Fleet & drivers — full CRUD over /api/v1/cars and /api/v1/drivers.
  • QuotesGET/POST /api/v1/quotes, price, send, and POST /api/v1/quotes/{id}/accept / convert to turn a quote into booked trips.
  • Bookings & customers — list and manage product bookings and your customer directory.
  • Invoices & payments — read invoices with line items and payments under /api/v1/invoices.
  • Uploads — attach a driver photo, car image or document with POST /api/v1/uploads, then reference the returned URL.
  • Settings — read and update the business profile with GET/PATCH /api/v1/settings/business.
  • Platform admin — for super-admins, see the Admin API.

Push notifications

Register the device's push token after login so the app can receive alerts, and remove it on logout. These are user-scoped (not business-scoped): a device follows the person across every business they can act on.

  • POST /api/v1/me/devices — register (idempotent upsert), body { platform: ios|android|web, device_token, device_name? }.
  • GET /api/v1/me/devices — list the signed-in user's devices.
  • DELETE /api/v1/me/devices — unregister, body { device_token }.

Register a device

curl -X POST https://app.destination.dev/api/v1/me/devices \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1Qi..." \
  -H "Content-Type: application/json" \
  -d '{ "platform": "ios", "device_token": "fMEp...token", "device_name": "iPhone 16" }'

Passenger invoice payment

For a hosted pay page or the passenger app, the unauthenticated consumer-pay routes take an invoice's payment_hash as the capability — no token needed. GET /api/v1/public/invoices/{hash} returns a sanitised invoice, and POST /api/v1/public/invoices/{hash}/pay creates a Stripe PaymentIntent and returns a client_secret to confirm the card client-side (the invoice is marked paid by the Stripe webhook, so re-GET it to observe the new status).

Start a payment

curl -X POST https://app.destination.dev/api/v1/public/invoices/HASH/pay \
  -H "Content-Type: application/json"

Build the driver app

Drivers get a lean, purpose-built surface. After they log in and select the business, point the app at the driver endpoints:

  • My next jobGET /api/v1/driver/trip returns the driver's current assigned trip.
  • Share locationPOST /api/v1/driver/location streams the driver's position (surfaced to dispatch and the passenger's tracking link).
  • One-tap statusPOST /api/v1/driver/status for en route / arrived / on board / completed.

The passenger-facing side reads GET /api/v1/trips/{id}/location for a "track your ride" view.

Browse every endpoint in the API reference, and read Errors for the shared error envelope.