Developer Documentation
Introduction
The Ngoma API gives you programmatic access to all platform data and control surfaces. Build custom dashboards, integrate with your existing home management systems, trigger automations from external sources, or extract historical analytics.
The API exposes three integration channels:
| Channel | Best for | Format |
|---|---|---|
| REST API | Read/write operations, data retrieval, one-off commands | JSON over HTTPS |
| WebSocket feed | Real-time state updates, live dashboards | JSON over WSS |
| Webhooks | Event-driven integrations, push notifications to your systems | JSON over HTTPS POST |
| MQTT | Low-latency IoT integration, perception data on local network | JSON over TCP/TLS |
Quickstart
Make your first API call in under five minutes.
-
Get your API key
Log in to your Ngoma dashboard and go to Settings โ Integrations โ API Keys โ Create Key. Give the key a descriptive name and select the required permission scopes.
-
Make your first request
Retrieve the current perception snapshot for your deployment:
curl -X GET https://api.workforceanalytics.co.za/v2/perception/snapshot \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "X-Deployment-ID: YOUR_DEPLOYMENT_ID" -
Review the response
{ "deployment_id": "dep_abc123", "timestamp": "2026-03-18T09:41:00Z", "perception": { "people_count": 7, "activity": "working", "sound_class": "conversation", "processing_load": 0.34 }, "sensors": { "depth_camera": "active", "lidar": "active", "microphone": "active" } } -
Subscribe to real-time updates
Connect to the WebSocket feed to receive perception updates as they happen โ no polling required. See WebSocket Feed for details.
Authentication
All API requests require a Bearer token in the Authorization header, plus your deployment ID in X-Deployment-ID.
Authorization: Bearer nga_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-Deployment-ID: dep_xxxxxxxx
API Key scopes
Each API key is issued with one or more permission scopes. Request only the scopes your integration requires.
| Scope | Access granted |
|---|---|
perception:read | Read real-time and historical perception data (occupancy, activity, sound) |
security:read | Read security events, alerts, and facial recognition results |
security:write | Trigger sentry mode, send navigation commands |
wellness:read | Read personal wellness data from KnightGuard devices |
environment:read | Read device states, thermostat, and sensor values |
environment:write | Control devices, activate scenes, adjust thermostat |
events:read | Read alert feed and event log |
webhooks:manage | Create, update, and delete webhook endpoints |
security:write or environment:write scopes can affect physical systems in your home. Store these keys in environment variables or a secrets manager โ never commit them to version control.Base URLs
| Environment | Base URL |
|---|---|
| Production | https://api.workforceanalytics.co.za/v2 |
| Sandbox | https://sandbox-api.workforceanalytics.co.za/v2 |
| WebSocket (production) | wss://rt.workforceanalytics.co.za/v2 |
| Local MQTT | mqtt://<jetson-ip>:1883 (TLS: port 8883) |
The sandbox environment uses simulated data and does not connect to real hardware. Use it for development and testing. All endpoints, request formats, and response schemas are identical between environments.
Error Handling
The API returns standard HTTP status codes. Error responses include a machine-readable code and a human-readable message.
{
"error": {
"code": "deployment_not_found",
"message": "No deployment found for ID: dep_xyz",
"status": 404
}
}
| HTTP Status | Meaning |
|---|---|
200 OK | Request succeeded |
400 Bad Request | Malformed request body or invalid parameters |
401 Unauthorized | Missing or invalid API key |
403 Forbidden | API key lacks the required scope |
404 Not Found | Resource does not exist |
429 Too Many Requests | Rate limit exceeded โ see Retry-After header |
503 Service Unavailable | Deployment offline or Jetson unreachable |
Rate Limits
| Plan | Requests / minute | WebSocket connections |
|---|---|---|
| Standard | 120 | 2 |
| Professional | 600 | 10 |
| Enterprise | Unlimited | Unlimited |
Rate limit headers are included in every response:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
X-RateLimit-Reset: 1742292180
Perception API
The Perception API provides access to real-time and historical AI perception data: people counting, activity classification, and ambient sound detection.
Endpoints
// Response
{
"deployment_id": "dep_abc123",
"timestamp": "2026-03-18T09:41:00Z",
"perception": {
"people_count": 7,
"activity": "working", // working | relaxing | meeting | moving
"sound_class": "conversation", // music | conversation | silence | alert
"processing_load": 0.34,
"auto_ducking_active": true
},
"sensors": {
"depth_camera": "active", // active | inactive
"lidar": "active",
"microphone": "active"
},
"mode": "production" // production | simulation
}
// Query parameters
?start=2026-03-01T00:00:00Z
&end=2026-03-18T23:59:59Z
&granularity=hour // minute | hour | day
// Response
{
"data": [
{
"timestamp": "2026-03-18T09:00:00Z",
"avg_people_count": 5.2,
"peak_people_count": 9,
"dominant_activity": "working",
"dominant_sound": "conversation"
},
...
]
}
Security API
// Query parameters
?severity=critical,high // critical | high | medium | low
&limit=50
&cursor=alert_cursor_token
// Response
{
"alerts": [
{
"id": "alrt_xyz",
"timestamp": "2026-03-18T08:55:00Z",
"type": "person_detected", // person_detected | sound_event | motion | unknown_face
"severity": "high",
"description": "Unknown person detected in Server Room zone",
"zone": "server_room",
"resolved": false
}
],
"next_cursor": "alert_cursor_token_next"
}
// Request body (optional โ omit to use configured default route)
{
"waypoints": ["entrance", "floor_1", "server_room"]
}
// Response
{
"patrol_id": "patrol_abc",
"status": "en_route",
"current_waypoint": "entrance",
"total_waypoints": 3
}
Wellness API
// Query parameter
?employee_id=emp_abc
// Response
{
"employee_id": "emp_abc",
"timestamp": "2026-03-18T09:40:30Z",
"vitals": {
"heart_rate_bpm": 72,
"heart_rate_zone": "normal", // low | normal | elevated | high | critical
"spo2_percent": 98,
"spo2_zone": "normal",
"temperature_celsius": 36.8,
"temperature_zone": "normal"
},
"activity": {
"steps_today": 6240,
"calories_burned": 312,
"distance_metres": 4180,
"speed_ms": 0.0,
"motion_intensity": 0.12,
"balance_stability": 0.04
}
}
Environment API
// Response
{
"devices": [
{
"entity_id": "light.reception_main",
"friendly_name": "Reception Main Lights",
"domain": "light",
"state": "on",
"attributes": {
"brightness": 180,
"color_temp": 4000
},
"last_changed": "2026-03-18T08:00:00Z"
},
...
]
}
// Request body
{
"service": "turn_on", // turn_on | turn_off | toggle | set_value
"data": {
"brightness": 200, // optional โ device-specific attributes
"color_temp": 3500
}
}
// Response
{
"entity_id": "light.reception_main",
"state": "on",
"success": true
}
Events & Alerts API
// Query parameters
?source=perception,security // perception | security | wellness | environment
&since=2026-03-18T08:00:00Z
&limit=100
// Response
{
"events": [
{
"id": "evt_xyz",
"timestamp": "2026-03-18T09:30:00Z",
"source": "security",
"type": "unknown_face",
"severity": "high",
"payload": { "zone": "entrance", "confidence": 0.94 }
}
]
}
Webhooks
Register an HTTPS endpoint to receive real-time event notifications via POST requests. Webhooks are ideal for pushing Ngoma events into your existing systems (Slack, PagerDuty, HRMS, SIEM, etc.).
Registering a webhook
// Request body
{
"url": "https://your-system.example.com/ngoma-events",
"events": [
"security.unknown_face",
"security.patrol_complete",
"wellness.vitals_critical",
"perception.occupancy_change"
],
"secret": "your_webhook_signing_secret"
}
// Response
{
"webhook_id": "wh_abc123",
"url": "https://your-system.example.com/ngoma-events",
"status": "active",
"created_at": "2026-03-18T09:00:00Z"
}
Webhook payload
POST https://your-system.example.com/ngoma-events
Content-Type: application/json
X-Ngoma-Signature: sha256=abc123...
X-Ngoma-Delivery: wh-delivery-id
{
"webhook_id": "wh_abc123",
"delivery_id": "wh-delivery-xyz",
"event": "security.unknown_face",
"timestamp": "2026-03-18T09:41:00Z",
"deployment_id": "dep_abc123",
"data": {
"zone": "entrance",
"confidence": 0.96,
"image_available": true
}
}
Verifying webhook signatures
Each delivery includes an X-Ngoma-Signature header containing an HMAC-SHA256 hash of the raw request body, signed with your webhook secret. Always verify this before processing the payload.
import hmac, hashlib
def verify_signature(payload_bytes, secret, signature_header):
expected = 'sha256=' + hmac.new(
secret.encode(),
payload_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)
MQTT Topics
For low-latency, local-network integrations, subscribe directly to the Mosquitto MQTT broker deployed with your Ngoma unit. All perception and security events are published here in real time with sub-100ms latency.
Perception topics
{"count": 7, "ts": 1742292100}{"activity": "working", "confidence": 0.91}{"sound_class": "conversation", "confidence": 0.88}Security topics
{"person_id": "emp_abc", "name": "Jane Smith", "confidence": 0.96, "known": true}. known: false for unrecognised individuals.{"type": "unknown_face", "severity": "high", "zone": "entrance", "ts": 1742292100}{"status": "en_route", "waypoint": 2, "total": 5, "destination": "floor_1"}{"action": "go_to", "waypoint": "server_room"}WebSocket Feed
Connect to the WebSocket feed for real-time state updates without polling. Ideal for live dashboards.
// Connect
const ws = new WebSocket(
'wss://rt.workforceanalytics.co.za/v2/stream' +
'?token=YOUR_API_KEY&deployment=dep_abc123'
);
// Subscribe to event types on connect
ws.onopen = () => {
ws.send(JSON.stringify({
action: 'subscribe',
channels: ['perception', 'security.alerts', 'wellness.vitals']
}));
};
// Receive events
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log(msg.channel, msg.data);
};
// Keepalive โ send ping every 30s
setInterval(() => ws.send(JSON.stringify({ action: 'ping' })), 30000);
WebSocket message format
{
"channel": "perception",
"event": "snapshot_update",
"timestamp": "2026-03-18T09:41:00Z",
"data": {
"people_count": 8,
"activity": "meeting",
"sound_class": "conversation"
}
}
Python SDK
Installation
pip install ngoma-analytics
Basic usage
from ngoma import NgomClient
client = NgomClient(
api_key="nga_live_xxxxxxxxxxxxxxxxxxxx",
deployment_id="dep_abc123"
)
# Get current perception snapshot
snapshot = client.perception.get_snapshot()
print(f"People in office: {snapshot.people_count}")
print(f"Current activity: {snapshot.activity}")
# Get last 7 days of hourly occupancy
history = client.perception.get_history(days=7, granularity="hour")
for hour in history:
print(f"{hour.timestamp}: avg {hour.avg_people_count:.1f} people")
# Control a device
client.environment.control(
entity_id="light.boardroom_main",
service="turn_on",
brightness=220
)
# Activate a scene
client.environment.activate_scene("morning_routine")
Real-time streaming (Python)
from ngoma import NgomClient
client = NgomClient(api_key="nga_live_xxxx", deployment_id="dep_abc123")
@client.on("perception")
def on_perception(data):
print(f"Occupancy: {data['people_count']} | Activity: {data['activity']}")
@client.on("security.unknown_face")
def on_unknown_face(data):
print(f"โ Unknown person in {data['zone']}")
# trigger your alert pipeline here
client.stream.connect() # blocks until disconnected
Node.js SDK
Installation
npm install @ngoma/analytics-sdk
Basic usage
import { NgomClient } from '@ngoma/analytics-sdk';
const client = new NgomClient({
apiKey: process.env.NGOMA_API_KEY,
deploymentId: process.env.NGOMA_DEPLOYMENT_ID
});
// Get current snapshot
const snapshot = await client.perception.getSnapshot();
console.log(`Office occupancy: ${snapshot.peopleCount}`);
// Stream real-time events
client.stream.on('perception', (data) => {
console.log('Activity changed:', data.activity);
});
client.stream.on('security.alert', (alert) => {
if (alert.severity === 'critical') {
notifySecurityTeam(alert);
}
});
await client.stream.connect();
Code Examples
Post security alerts to Slack
import requests
from ngoma import NgomClient
client = NgomClient(api_key="nga_live_xxxx", deployment_id="dep_abc123")
SLACK_WEBHOOK = "https://hooks.slack.com/services/xxx/yyy/zzz"
@client.on("security.alert")
def forward_to_slack(alert):
if alert["severity"] in ("high", "critical"):
requests.post(SLACK_WEBHOOK, json={
"text": f"๐จ Ngoma Security Alert ({alert['severity'].upper()}): "
f"{alert['description']} in {alert['zone']}"
})
client.stream.connect()
Log hourly occupancy to a database
import psycopg2
from ngoma import NgomClient
client = NgomClient(api_key="nga_live_xxxx", deployment_id="dep_abc123")
conn = psycopg2.connect("dbname=workforce host=localhost")
history = client.perception.get_history(days=30, granularity="hour")
with conn.cursor() as cur:
for row in history:
cur.execute(
"INSERT INTO occupancy_log (ts, avg_people, activity) VALUES (%s, %s, %s)",
(row.timestamp, row.avg_people_count, row.dominant_activity)
)
conn.commit()
Activate "Presentation Mode" scene when a meeting is detected
from ngoma import NgomClient
client = NgomClient(api_key="nga_live_xxxx", deployment_id="dep_abc123")
last_activity = None
@client.on("perception")
def handle_activity(data):
global last_activity
if data["activity"] != last_activity:
last_activity = data["activity"]
if data["activity"] == "meeting":
client.environment.activate_scene("presentation_mode")
print("Presentation Mode activated")
elif data["activity"] == "working":
client.environment.activate_scene("focus_mode")
print("Focus Mode activated")
client.stream.connect()
Data Models
PerceptionSnapshot
| Field | Type | Description |
|---|---|---|
deployment_id | string | Your deployment identifier |
timestamp | ISO 8601 | UTC timestamp of the snapshot |
people_count | integer | Number of people detected |
activity | enum | working | relaxing | meeting | moving |
sound_class | enum | music | conversation | silence | alert |
processing_load | float (0โ1) | Jetson AI pipeline CPU/GPU utilisation |
mode | enum | production | simulation |
SecurityAlert
| Field | Type | Description |
|---|---|---|
id | string | Unique alert identifier |
timestamp | ISO 8601 | UTC time of the alert |
type | enum | person_detected | unknown_face | sound_event | motion |
severity | enum | critical | high | medium | low |
zone | string | Named zone where event occurred |
resolved | boolean | Whether the alert has been acknowledged |
WellnessReading
| Field | Type | Description |
|---|---|---|
heart_rate_bpm | integer | Beats per minute |
heart_rate_zone | enum | low | normal | elevated | high | critical |
spo2_percent | float | Blood oxygen saturation (%) |
spo2_zone | enum | normal | low | critical |
temperature_celsius | float | Body temperature (ยฐC) |
steps_today | integer | Total steps since midnight |
API Changelog
v0.0.3 โ 2026-03-18
- New:
/perception/zonesendpoint for per-zone occupancy breakdown - New: WebSocket feed supports
wellness.vitalschannel - New: Webhook payload includes
deployment_idfor multi-deployment integrations - Change:
/security/patrolnow returns apatrol_idfor status tracking - Fix:
/eventscursor pagination now correctly handles timezone offsets
v1.4.0 โ 2025-11-05
- New:
/wellness/historyendpoint for historical biometric summaries - New: MQTT topic
ngoma/navigation/commandfor remote navigation control - Change: Rate limit headers added to all responses
v1.3.0 โ 2025-08-12
- New: Webhook signature verification via HMAC-SHA256
- New:
security:readscope separates from general read scope - Fix:
GET /environment/devicesnow returns correctlast_changedtimestamps