Version 1.0.0 | Last Updated: February 2026
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.
To authenticate with the OmniWatch API:
Authorization header for protected routes# Authorization Header Format
Authorization: Bearer
Register a new user account
| Parameter | Type | Required | Description |
|---|---|---|---|
| firstName | string | Yes | User's first name |
| lastName | string | Yes | User's last name |
| phone | string | Yes | Phone number (e.g., +254712345678) |
| password | string | Yes | Account password (min 6 characters) |
| role | string | No | User role (default: "guard") |
| companyCode | string | No | Company/invite code |
curl -X POST http://localhost:5000/api/register \
-H "Content-Type: application/json" \
-d '{
"firstName": "John",
"lastName": "Doe",
"phone": "+254712345678",
"password": "securepassword123",
"role": "guard"
}'
{
"message": "User registered successfully",
"user": {
"id": "abc123",
"name": "John Doe",
"phone": "+254712345678",
"role": "guard"
}
}
Authenticate and receive JWT token
| Parameter | Type | Required | Description |
|---|---|---|---|
| phone | string | Yes | Registered phone number |
| password | string | Yes | Account password |
curl -X POST http://localhost:5000/api/login \
-H "Content-Type: application/json" \
-d '{
"phone": "+254712345678",
"password": "securepassword123"
}'
{
"message": "Login successful",
"user": {
"id": "abc123",
"name": "John Doe",
"phone": "+254712345678",
"role": "guard",
"company_code": "COMP001"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
End user session and logout
curl -X POST http://localhost:5000/api/logout \
-H "Content-Type: application/json" \
-c cookies.txt
{ "message": "Logout successful" }
Get current authenticated user information
| Header | Value |
|---|---|
| Authorization | Bearer |
curl -X GET http://localhost:5000/api/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
{
"authenticated": true,
"user": {
"id": "abc123",
"name": "John Doe",
"phone": "+254712345678",
"role": "guard"
}
}
Verify if a JWT token is valid
| Parameter | Type | Required | Description |
|---|---|---|---|
| token | string | Yes | JWT token to verify |
curl -X POST http://localhost:5000/api/verify-token \
-H "Content-Type: application/json" \
-d '{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }'
{
"valid": true,
"user": {
"id": "abc123",
"name": "John Doe",
"role": "guard"
}
}
The following routes require authentication. Include the JWT token in the Authorization header.
Admin dashboard data (requires any authenticated user)
curl -X GET http://localhost:5000/api/admin/dashboard \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
{
"message": "Welcome to the admin dashboard",
"user": { "id": "abc123", "name": "John Doe" },
"data": {
"totalGuards": 45,
"activeShifts": 12,
"pendingReports": 5
}
}
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.
List all guards in the admin organization, enriched with latest assignment and patrol-derived online/last-seen data.
| Field | Type | Description |
|---|---|---|
| id | string | Guard user ID |
| first_name | string | Guard first name |
| last_name | string | Guard last name |
| phone | string | Guard phone number |
| location | string | Resolved location name from latest assignment |
| location_id | string | Location ID from latest assignment |
| assigned_areas | string | Comma-separated assigned areas |
| operating_hours_start | string | Assignment start time |
| operating_hours_end | string | Assignment end time |
| is_online | boolean | true when latest patrol is active and not ended |
| last_seen | string|null | Latest patrol end_time or fallback access time |
| last_seen_display | string | "Online (Currently on patrol)" when active, otherwise timestamp or "Never" |
curl -X GET http://localhost:5000/api/admin/guards \
-H "Authorization: Bearer <token>"
{
"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)"
}
]
}
Remove a guard from the organization and cascade-delete related records by user_id from assignments, logs, and patrols.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Guard user ID |
curl -X DELETE http://localhost:5000/api/admin/guards/f3a9... \
-H "Authorization: Bearer <token>"
{
"message": "Guard removed successfully",
"deleted": {
"assignments": 2,
"logs": 8,
"patrols": 5
}
}
Create a new assignment for a guard in the same organization.
| Parameter | Type | Required | Description |
|---|---|---|---|
| user_id | string | Yes | Guard user ID |
| location | string | Yes | Location ID |
| assigned_areas | string | Yes | Comma-separated areas/checkpoints |
| start_time | string | Yes | Assignment start time (e.g., 09:00) |
| end_time | string | Yes | Assignment end time (e.g., 17:00) |
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"
}'
{
"message": "Assignment created successfully",
"assignment": {
"id": "a7bc...",
"user_id": "f3a9...",
"location": "8b2a..."
}
}
List all locations in the authenticated admin organization.
{
"locations": [
{
"id": "8b2a...",
"name": "Main Gate",
"assigned_areas": "Gate A, Gate B"
}
]
}
Create a location for the admin organization.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Location name |
| assigned_areas | string | No | Comma-separated areas |
{
"message": "Location created successfully",
"location": {
"id": "8b2a...",
"name": "Main Gate"
}
}
Update location fields (name and/or assigned_areas) within the admin organization.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | No | Updated location name (cannot be empty) |
| assigned_areas | string | No | Updated areas list |
{
"message": "Location updated successfully",
"location": {
"id": "8b2a...",
"name": "Rear Gate"
}
}
Delete a location in the admin organization.
{
"message": "Location deleted successfully",
"id": "8b2a..."
}
Endpoints used by the guard page for assignments, patrol lifecycle, location tracking, and logs.
Fetch assignments for the authenticated guard.
{
"assignments": [
{ "id": "asg1", "user_id": "u1", "location": "loc1", "assigned_areas": "Gate A, Lobby", "start_time": "09:00", "end_time": "17:00" }
]
}Update the authenticated guard's assignment details.
| Parameter | Type | Required | Description |
|---|---|---|---|
| location | string | Yes | Location ID |
| assigned_areas | string | Yes | Comma-separated areas |
| start_time | string | Yes | Shift start time |
| end_time | string | Yes | Shift end time |
{ "message": "Assignment updated successfully" }Fetch all locations in the authenticated user's organization.
{
"locations": [
{ "id": "loc1", "name": "Main Gate", "assigned_areas": "Gate A, Gate B" }
]
}Fetch guard patrol history.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Max records (default: 10) |
| sort | string | No | Sort order (default: -start_time) |
{ "patrols": [ { "id": "p1", "status": "active" } ] }Start a patrol session.
| Parameter | Type | Required | Description |
|---|---|---|---|
| start_time | string | Yes | Patrol start timestamp |
| user_id | string | Yes | Guard user ID |
| organization_id | string | No | Organization ID |
| duration | number | No | Initial duration in minutes |
| end_time | string | No | End time if already completed |
| map | string | No | Serialized path data |
| location_data | array | No | Location points (alternative to map) |
{ "message": "Patrol started successfully", "data": { "id": "p1", "status": "active" } }Update patrol details, including ending a patrol by setting end_time and optionally status.
| Parameter | Type | Required | Description |
|---|---|---|---|
| duration | number | No | Patrol duration in minutes |
| end_time | string | No | Patrol end timestamp |
| map | string | No | Serialized route data |
| location_data | array | No | Route points (mapped to map) |
| status | string | No | Patrol state (e.g., active/completed) |
{ "message": "Patrol updated successfully", "data": { "id": "p1" } }Append incremental GPS points to an active patrol route.
| Parameter | Type | Required | Description |
|---|---|---|---|
| location_data | array | Yes | Array of points: { latitude, longitude, timestamp } |
{ "message": "Location updated successfully", "points_count": 120 }Fetch logs created by the authenticated guard.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Max records (default: 50) |
| sort | string | No | Sort order (default: -timestamp) |
{ "logs": [ { "id": "l1", "title": "Checkpoint" } ] }Create a new guard log entry with optional images and patrol linkage.
| Parameter | Type | Required | Description |
|---|---|---|---|
| title | string | Yes | Log title |
| description | string | Yes | Log details |
| category | string | Yes | One of: activity, unusual, incident, checkpoint, other |
| images | string|array | No | Image data/URIs (commonly JSON string) |
| patrol_id | string | No | Related patrol ID |
{ "message": "Log created successfully", "data": { "id": "l1" } }Fetch assignment records from backend storage.
{ "assignments": [ { "id": "asg1" } ] }Return invite codes for registered organizations.
{ "inviteCodes": ["ORG001", "ORG002"] }Validate whether an organization invite code exists.
| Parameter | Type | Required | Description |
|---|---|---|---|
| inviteCode | string | Yes | Organization invite code |
{ "valid": true, "message": "Invite code is valid" }Admin patrol feed across all guards in organization, enriched with guard name and checkpoints.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Max records (default: 50) |
| sort | string | No | Sort by timestamp (default: -start_time) |
{ "patrols": [ { "id": "p1", "guard_name": "Jane Doe" } ] }Admin logs feed across all guards in organization with location resolution.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Max records (default: 50) |
| sort | string | No | Sort by timestamp (default: -timestamp) |
{ "logs": [ { "id": "l1", "location": "Main Gate" } ] }All error responses follow a consistent format:
{
"error": "Error Type",
"message": "Human readable error message"
}
| Status | Error | Description |
|---|---|---|
| 400 | Validation Error | Missing required fields or invalid data |
| 401 | Unauthorized | Invalid credentials or no token provided |
| 403 | Forbidden | Access denied for this resource |
| 409 | Conflict | User already exists (duplicate entry) |
| 500 | Internal Server Error | Server-side error |
// 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();
};
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