🚀 OmniWatch API Documentation

Version 1.0.0 | Last Updated: February 2026

🔐 Authentication

The OmniWatch API uses JWT (JSON Web Token) for authentication. After a successful login, you will receive a token that must be included in the header of subsequent requests.

Getting Started

To authenticate with the OmniWatch API:

  1. Call POST /api/login with phone and password
  2. Receive the JWT token in the response
  3. Include the token in the Authorization header for protected routes
# Authorization Header Format
Authorization: Bearer

👤 Auth Endpoints

POST /api/register PUBLIC

Register a new user account

Request Body

ParameterTypeRequiredDescription
firstNamestringYesUser's first name
lastNamestringYesUser's last name
phonestringYesPhone number (e.g., +254712345678)
passwordstringYesAccount password (min 6 characters)
rolestringNoUser role (default: "guard")
companyCodestringNoCompany/invite code

Example Request

curl -X POST http://localhost:5000/api/register \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "John",
    "lastName": "Doe",
    "phone": "+254712345678",
    "password": "securepassword123",
    "role": "guard"
  }'

Example Response

{
  "message": "User registered successfully",
  "user": {
    "id": "abc123",
    "name": "John Doe",
    "phone": "+254712345678",
    "role": "guard"
  }
}
Status Codes: 201 Created 400 Bad Request 409 Conflict 500 Internal Server Error
POST /api/login PUBLIC

Authenticate and receive JWT token

Request Body

ParameterTypeRequiredDescription
phonestringYesRegistered phone number
passwordstringYesAccount password

Example Request

curl -X POST http://localhost:5000/api/login \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+254712345678",
    "password": "securepassword123"
  }'

Example Response

{
  "message": "Login successful",
  "user": {
    "id": "abc123",
    "name": "John Doe",
    "phone": "+254712345678",
    "role": "guard",
    "company_code": "COMP001"
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Status Codes: 200 OK 400 Bad Request 401 Unauthorized 500 Internal Server Error
POST /api/logout PUBLIC

End user session and logout

Example Request

curl -X POST http://localhost:5000/api/logout \
  -H "Content-Type: application/json" \
  -c cookies.txt

Example Response

{ "message": "Logout successful" }
Status Codes: 200 OK 500 Internal Server Error
GET /api/me PROTECTED

Get current authenticated user information

Headers

HeaderValue
AuthorizationBearer

Example Request

curl -X GET http://localhost:5000/api/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response

{
  "authenticated": true,
  "user": {
    "id": "abc123",
    "name": "John Doe",
    "phone": "+254712345678",
    "role": "guard"
  }
}
Status Codes: 200 OK 401 Unauthorized
POST /api/verify-token PUBLIC

Verify if a JWT token is valid

Request Body

ParameterTypeRequiredDescription
tokenstringYesJWT token to verify

Example Request

curl -X POST http://localhost:5000/api/verify-token \
  -H "Content-Type: application/json" \
  -d '{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }'

Example Response

{
  "valid": true,
  "user": {
    "id": "abc123",
    "name": "John Doe",
    "role": "guard"
  }
}
Status Codes: 200 OK 400 Bad Request 401 Unauthorized

🛡️ Protected Routes

The following routes require authentication. Include the JWT token in the Authorization header.

GET /api/admin/dashboard PROTECTED

Admin dashboard data (requires any authenticated user)

Example Request

curl -X GET http://localhost:5000/api/admin/dashboard \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Example Response

{
  "message": "Welcome to the admin dashboard",
  "user": { "id": "abc123", "name": "John Doe" },
  "data": {
    "totalGuards": 45,
    "activeShifts": 12,
    "pendingReports": 5
  }
}

🧭 Admin Management APIs

These endpoints are used by the admin dashboard to manage guards, assignments, and locations. All are scoped to the authenticated admin's organization via invite_code.

GET /api/admin/guards PROTECTED

List all guards in the admin organization, enriched with latest assignment and patrol-derived online/last-seen data.

Response Fields (per guard)

FieldTypeDescription
idstringGuard user ID
first_namestringGuard first name
last_namestringGuard last name
phonestringGuard phone number
locationstringResolved location name from latest assignment
location_idstringLocation ID from latest assignment
assigned_areasstringComma-separated assigned areas
operating_hours_startstringAssignment start time
operating_hours_endstringAssignment end time
is_onlinebooleantrue when latest patrol is active and not ended
last_seenstring|nullLatest patrol end_time or fallback access time
last_seen_displaystring"Online (Currently on patrol)" when active, otherwise timestamp or "Never"

Example Request

curl -X GET http://localhost:5000/api/admin/guards \
  -H "Authorization: Bearer <token>"

Example Response

{
  "guards": [
    {
      "id": "f3a9...",
      "first_name": "Jane",
      "last_name": "Doe",
      "phone": "+2547...",
      "location": "Main Gate",
      "location_id": "8b2a...",
      "is_online": true,
      "last_seen_display": "Online (Currently on patrol)"
    }
  ]
}
DELETE /api/admin/guards/:id PROTECTED

Remove a guard from the organization and cascade-delete related records by user_id from assignments, logs, and patrols.

Path Params

ParameterTypeRequiredDescription
idstringYesGuard user ID

Example Request

curl -X DELETE http://localhost:5000/api/admin/guards/f3a9... \
  -H "Authorization: Bearer <token>"

Example Response

{
  "message": "Guard removed successfully",
  "deleted": {
    "assignments": 2,
    "logs": 8,
    "patrols": 5
  }
}
Status Codes: 200 OK 400 Bad Request 401 Unauthorized 404 Not Found 500 Internal Server Error
POST /api/admin/assignments PROTECTED

Create a new assignment for a guard in the same organization.

Request Body

ParameterTypeRequiredDescription
user_idstringYesGuard user ID
locationstringYesLocation ID
assigned_areasstringYesComma-separated areas/checkpoints
start_timestringYesAssignment start time (e.g., 09:00)
end_timestringYesAssignment end time (e.g., 17:00)

Example Request

curl -X POST http://localhost:5000/api/admin/assignments \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "f3a9...",
    "location": "8b2a...",
    "assigned_areas": "Gate A, Lobby",
    "start_time": "09:00",
    "end_time": "17:00"
  }'

Example Response

{
  "message": "Assignment created successfully",
  "assignment": {
    "id": "a7bc...",
    "user_id": "f3a9...",
    "location": "8b2a..."
  }
}
GET /api/admin/locations PROTECTED

List all locations in the authenticated admin organization.

Example Response

{
  "locations": [
    {
      "id": "8b2a...",
      "name": "Main Gate",
      "assigned_areas": "Gate A, Gate B"
    }
  ]
}
POST /api/admin/locations PROTECTED

Create a location for the admin organization.

Request Body

ParameterTypeRequiredDescription
namestringYesLocation name
assigned_areasstringNoComma-separated areas

Example Response

{
  "message": "Location created successfully",
  "location": {
    "id": "8b2a...",
    "name": "Main Gate"
  }
}
PATCH /api/admin/locations/:id PROTECTED

Update location fields (name and/or assigned_areas) within the admin organization.

Request Body

ParameterTypeRequiredDescription
namestringNoUpdated location name (cannot be empty)
assigned_areasstringNoUpdated areas list

Example Response

{
  "message": "Location updated successfully",
  "location": {
    "id": "8b2a...",
    "name": "Rear Gate"
  }
}
DELETE /api/admin/locations/:id PROTECTED

Delete a location in the admin organization.

Example Response

{
  "message": "Location deleted successfully",
  "id": "8b2a..."
}

🛰️ Guard App APIs

Endpoints used by the guard page for assignments, patrol lifecycle, location tracking, and logs.

GET /api/my-assignments PROTECTED

Fetch assignments for the authenticated guard.

Example Response

{
  "assignments": [
    { "id": "asg1", "user_id": "u1", "location": "loc1", "assigned_areas": "Gate A, Lobby", "start_time": "09:00", "end_time": "17:00" }
  ]
}
PUT /api/my-assignments PROTECTED

Update the authenticated guard's assignment details.

Request Body

ParameterTypeRequiredDescription
locationstringYesLocation ID
assigned_areasstringYesComma-separated areas
start_timestringYesShift start time
end_timestringYesShift end time

Example Response

{ "message": "Assignment updated successfully" }
GET /api/locations PROTECTED

Fetch all locations in the authenticated user's organization.

Example Response

{
  "locations": [
    { "id": "loc1", "name": "Main Gate", "assigned_areas": "Gate A, Gate B" }
  ]
}
GET /api/patrols PROTECTED

Fetch guard patrol history.

Query Params

ParameterTypeRequiredDescription
limitnumberNoMax records (default: 10)
sortstringNoSort order (default: -start_time)

Example Response

{ "patrols": [ { "id": "p1", "status": "active" } ] }
POST /api/patrols PROTECTED

Start a patrol session.

Request Body

ParameterTypeRequiredDescription
start_timestringYesPatrol start timestamp
user_idstringYesGuard user ID
organization_idstringNoOrganization ID
durationnumberNoInitial duration in minutes
end_timestringNoEnd time if already completed
mapstringNoSerialized path data
location_dataarrayNoLocation points (alternative to map)

Example Response

{ "message": "Patrol started successfully", "data": { "id": "p1", "status": "active" } }
PATCH /api/patrols/:id PROTECTED

Update patrol details, including ending a patrol by setting end_time and optionally status.

Request Body

ParameterTypeRequiredDescription
durationnumberNoPatrol duration in minutes
end_timestringNoPatrol end timestamp
mapstringNoSerialized route data
location_dataarrayNoRoute points (mapped to map)
statusstringNoPatrol state (e.g., active/completed)

Example Response

{ "message": "Patrol updated successfully", "data": { "id": "p1" } }
PATCH /api/patrols/:id/location PROTECTED

Append incremental GPS points to an active patrol route.

Request Body

ParameterTypeRequiredDescription
location_dataarrayYesArray of points: { latitude, longitude, timestamp }

Example Response

{ "message": "Location updated successfully", "points_count": 120 }
GET /api/logs PROTECTED

Fetch logs created by the authenticated guard.

Query Params

ParameterTypeRequiredDescription
limitnumberNoMax records (default: 50)
sortstringNoSort order (default: -timestamp)

Example Response

{ "logs": [ { "id": "l1", "title": "Checkpoint" } ] }
POST /api/logs PROTECTED

Create a new guard log entry with optional images and patrol linkage.

Request Body

ParameterTypeRequiredDescription
titlestringYesLog title
descriptionstringYesLog details
categorystringYesOne of: activity, unusual, incident, checkpoint, other
imagesstring|arrayNoImage data/URIs (commonly JSON string)
patrol_idstringNoRelated patrol ID

Example Response

{ "message": "Log created successfully", "data": { "id": "l1" } }

🧩 Additional APIs

GET /api/assignments PROTECTED

Fetch assignment records from backend storage.

Example Response

{ "assignments": [ { "id": "asg1" } ] }
GET /api/organizations/invite-codes PROTECTED

Return invite codes for registered organizations.

Example Response

{ "inviteCodes": ["ORG001", "ORG002"] }
POST /api/organizations/validate-invite-code PUBLIC

Validate whether an organization invite code exists.

Request Body

ParameterTypeRequiredDescription
inviteCodestringYesOrganization invite code

Example Response

{ "valid": true, "message": "Invite code is valid" }
GET /api/admin/patrols PROTECTED

Admin patrol feed across all guards in organization, enriched with guard name and checkpoints.

Query Params

ParameterTypeRequiredDescription
limitnumberNoMax records (default: 50)
sortstringNoSort by timestamp (default: -start_time)

Example Response

{ "patrols": [ { "id": "p1", "guard_name": "Jane Doe" } ] }
GET /api/admin/logs PROTECTED

Admin logs feed across all guards in organization with location resolution.

Query Params

ParameterTypeRequiredDescription
limitnumberNoMax records (default: 50)
sortstringNoSort by timestamp (default: -timestamp)

Example Response

{ "logs": [ { "id": "l1", "location": "Main Gate" } ] }

⚠️ Error Handling

All error responses follow a consistent format:

{
  "error": "Error Type",
  "message": "Human readable error message"
}

Common Error Codes

StatusErrorDescription
400Validation ErrorMissing required fields or invalid data
401UnauthorizedInvalid credentials or no token provided
403ForbiddenAccess denied for this resource
409ConflictUser already exists (duplicate entry)
500Internal Server ErrorServer-side error

📱 Frontend Integration

React Native / Expo Example

// Login Example
const login = async (phone, password) => {
  const response = await fetch('http://localhost:5000/api/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ phone, password }),
  });
  const data = await response.json();
  if (data.token) {
    // Store token securely
    AsyncStorage.setItem('token', data.token);
  }
  return data;
};

// Protected API Call
const getDashboard = async () => {
  const token = await AsyncStorage.getItem('token');
  const response = await fetch('http://localhost:5000/api/admin/dashboard', {
    headers: {
      'Authorization': `Bearer ${token}`,
    },
  });
  return response.json();
};

🔧 Environment Variables

Create a .env file in the backend root with the following variables:

# Server
APIPORT=5000
NODE_ENV=development

# JWT Secret (change this in production!)
JWT_SECRET=your-super-secret-jwt-key-change-in-production

# Directus Configuration
DIRECTUS_URL=http://your-directus-instance.com
DIRECTUS_TOKEN=your-directus-static-token

# PostgreSQL Database
DB_USER=postgres
DB_HOST=localhost
DB_NAME=omniwatch
DB_PASSWORD=your-password
DB_PORT=5432