NavKids API

The NavKids API serves the driver app, the school dashboard and the public landing page. This page is the reference for developers building against it.

Basics

  • Base URL: https://api.investika.co.ke/api/v1 (staging)
  • Format: JSON in and out. Send Accept: application/json.
  • Time: every timestamp is ISO 8601 with an offset, in the school's time zone (Africa/Nairobi). Devices send the time they recorded something; the server adds the time it received it.
  • Envelope: a success is {"data": ...}; lists add meta (page, per_page, total). A failure is {"error": {"code", "message", "details?"}}.

Authentication

Sign in with POST /auth/login, sending identifier (an email, or a driver's Driver ID), password and device_name. The reply holds a bearer token:

Authorization: Bearer <token>

Each device gets its own token, which does not expire; it ends when the user signs out, a device is revoked, or the password changes. A driver may also send assistant_id, their co-driver's ID.

Tenancy and identity always come from the token, never from the request body: a school_id in a body is ignored, and another school's records answer 404.

Errors

Status Code Meaning
401 unauthenticated No token, or an invalid one
401 invalid_credentials Sign-in details are wrong
403 insufficient_permission Signed in, but without the permission this needs
403 account_inactive The account or school is suspended
404 not_found Does not exist, or belongs to another school
409 trip_already_active, students_still_aboard, no_assignment, resource_in_use, assignment_conflict, primary_guardian_required The request conflicts with the current state; details explains
422 validation_failed, import_validation_failed details lists each field's or row's problem
429 too_many_attempts Too many requests; slow down and retry
500 server_error Something failed on our side

Rate limits

Kind Limit
Sign-in and password reset 6 a minute, per address and per identifier
Reads 120 a minute
Writes 60 a minute
Location batches 120 a minute (generous: a phone catching up after no signal sends a burst)
Launch registrations 5 an hour, per address
Emergencies Never limited

Retries and offline devices

Every write from a device carries a client_event_id (a UUID the device makes). Sending the same write again returns the original result and creates nothing new, so a phone can retry freely after losing signal. Location batches count duplicates instead of failing.

Driver endpoints

Method Path Notes
GET /driver/assignment Today's runs (morning, afternoon, evening) with stops and the children expected at each
POST /driver/trips Start a trip; works for trips started offline
GET /driver/trips/{trip} The trip's state, for an app restarted mid-trip
POST /driver/trips/{trip}/locations Up to 500 GPS points per batch
POST /driver/trips/{trip}/stops/{stop}/arrive Arrival at a stop; out-of-order arrivals are flagged, not refused
POST /driver/trips/{trip}/attendance Boarded, alighted or absent, in batches; exceptions are flagged, not refused
POST /driver/trips/{trip}/end Refused with 409 while children are recorded on board, unless confirmed
POST /driver/emergencies Raise an emergency; never refused, never rate-limited
GET, PATCH /driver/profile The driver's profile; contact details are editable
GET /driver/alerts Messages from the school
POST /driver/alerts/{alert}/acknowledge Mark a message read

Account endpoints

Method Path Notes
POST /auth/login Sign in
POST /auth/logout End this device's session
GET /auth/sessions This user's devices
DELETE /auth/sessions/{id} Sign a device out
POST /auth/password/forgot Texts a 6-digit code; the same reply whether or not the account exists
POST /auth/password/reset Set a new password with that code
PUT /auth/password Change the password while signed in

School administration endpoints

For school staff. Lists accept page and per_page (up to 200).

Method Path Notes
GET, POST, PATCH, DELETE /school/vehicles Buses; expiring insurance or inspection is flagged
GET, POST, PATCH, DELETE /school/drivers New drivers are texted their Driver ID and an activation code
POST /school/drivers/{id}/invite Send a fresh activation code
GET, POST, PATCH, DELETE /school/attendants Co-drivers
GET, POST, PATCH /school/routes Routes, morning or home-bound
GET, POST, PUT /school/routes/{id}/stops Changing stops creates a new route version; past trips keep theirs
GET, POST, PATCH, DELETE /school/students Filter by name, class, route and status
POST /school/students/import Check a CSV; .../import/{id}/confirm saves it, all or nothing
GET, POST, PATCH, DELETE /school/students/{id}/guardians Parents and guardians, with one primary contact
GET, POST, PATCH /school/assignments Driver, bus, route and run; warns about double-booking and capacity
GET /school/trips, /school/trips/active Trip history and the live board, problems first
GET /school/trips/{id}, .../locations One trip in full, and its GPS trail
GET /school/attendance Each child on each trip, including who received them at drop-off
GET, POST /school/emergencies, .../acknowledge, .../resolve Emergencies, acknowledged and resolved by named staff
GET /school/reports/{type} attendance, trips or emergencies, as JSON or CSV
GET /school/audit Every change, and every view of a child's data

Real-time

The API publishes live events over a Pusher-compatible WebSocket (Laravel Reverb). Subscribe to a private channel through POST /broadcasting/auth with your bearer token; every subscription is authorised on its own.

Channel Events
private-trip.{id} trip.started, location.updated, stop.arrived, attendance.recorded, trip.ended
private-school.{id} trip.started, trip.ended, emergency.raised, emergency.updated, trip.dark, trip.tracking_restored, trip.students_aboard

On shared hosting the WebSocket server is off; the dashboard polls instead.

Public endpoints

No account needed.

Method Path Notes
GET /status System status, uptime and recent incidents; 503 while down. Also shown at /
POST /registrations Launch registration from the landing page
GET /up (no /api/v1 prefix) A plain health check for monitors