Section 3 — API Reference

Tenant API Manual

REST endpoints, authentication, and real-time channels for building custom integrations on top of a tenant's data.

Overview

The Tenant Portal API is a JSON REST API served by the central platform at https://api.your-domain.com/api/portal/. It gives authorised tenant portal applications machine-level access to devices, incidents, beats, and assignees — all automatically scoped to the tenant.

This API is consumed internally by the server-tenant container but can also be used by custom dashboards, mobile apps, or backend integrations built for a specific tenant.

Authentication

Every request requires two headers:

Authorization: Bearer {APP_TENANT_API_KEY}
X-Tenant-Id:   {APP_TENANT_ID}

Both headers are validated on every request. The tenant context is set from the API key — all queries are automatically scoped to that tenant's data. There is no cross-tenant data access possible.

Optional: user attribution

To attribute actions to a specific user (for audit trails), include the user's Passport access token:

X-User-Token: {user_passport_access_token}

Base URL

https://api.your-domain.com/api/portal/

Endpoints

Devices

GET
/devices

List all devices for the tenant. Supports ?search= and ?status= filters. Paginated (50 per page).

GET
/devices/{id}

Get a single device with device type, current assignment, and beat.

GET
/devices/{id}/signals

Get the latest signal snapshot (lat, lon, speed, battery, timestamp).

Incidents

GET
/incidents

List incidents. Supports ?status= and ?device_id= filters. Paginated (50 per page).

GET
/incidents/{id}

Get a single incident with device, assignee, and beat details.

POST
/incidents

Create a new incident manually.

PATCH
/incidents/{id}

Update incident status (open, acknowledged, escalated, resolved) and resolution notes.

Beats

GET
/beats

List all top-level beats (inclusion and exclusion zones) with children and supervisor.

GET
/beats/{id}

Get a single beat with assigned devices.

Assignees

GET
/assignees

List all assignees with type and current device assignment.

GET
/assignees/{id}

Get a single assignee with beat assignments.

Real-time (WebSocket auth)

POST
/broadcasting/auth

Sign a Pusher/Soketi private channel auth response. Required for real-time device location updates.

Response Format

All endpoints return JSON. Lists return paginated objects:

{
  "data":         [...],
  "total":        142,
  "current_page": 1,
  "last_page":    3,
  "per_page":     50
}

Single resources return the object directly. Errors follow Laravel's standard JSON error format with message and optional errors keys.

Real-time — Soketi WebSocket

Device positions stream in real time via Soketi (Pusher-compatible WebSocket). Subscribe to the tenant's private channel:

// Channel name
private-tenant.{TENANT_ID}.locations

// Event fired on position batch
App\Events\LocationsBatchEvent

// Payload
{
  "positions": [
    {
      "device_id":   42,
      "lat":         31.5204,
      "lon":         74.3587,
      "battery":     87,
      "recorded_at": "2026-06-04T08:12:33Z"
    }
  ]
}

Channel auth is handled by the POST /broadcasting/auth endpoint using the machine API key — no user session required.

Rate Limits

Responses include standard rate-limit headers (X-RateLimit-Limit, X-RateLimit-Remaining).

Example — List Devices

curl -X GET \
  https://api.your-domain.com/api/portal/devices \
  -H "Authorization: Bearer tpk_xxxxxxxxxxxxxxxx" \
  -H "X-Tenant-Id: 42" \
  -H "Accept: application/json"

Example — Resolve an Incident

curl -X PATCH \
  https://api.your-domain.com/api/portal/incidents/17 \
  -H "Authorization: Bearer tpk_xxxxxxxxxxxxxxxx" \
  -H "X-Tenant-Id: 42" \
  -H "Content-Type: application/json" \
  -d '{"status":"resolved","resolution_notes":"Operator confirmed safe return"}'
API keys are generated in the admin panel under Organisations → {your org} → Portal API Keys. Each key is shown once and stored hashed — treat it like a password.