This endpoint requires a valid JWT Bearer token. Accessible via the API gateway at /v1/voice-ai/*.
Voice AI API
Voice AI assistant (Hey Maximus) for voice queries, commands, proactive alerts, and real-time speech processing.
Overview
| Attribute | Value |
|---|---|
| Base Path | /api/v1/voice-ai |
| Authentication | Bearer Token |
| Required Roles | pos_staff, server, restaurant_staff, kitchen, host, bartender, cashier, chef, manager, drive_thru_staff, restaurant_manager, tenant_admin, platform_admin, system_admin, super_admin |
| Real-time | WebSocket at /api/voice-ai/stream/{session_id} (Python service only; not proxied through Go gateway) |
Health & Status
Health Check
Check Voice AI service health and agent availability.
GET /api/v1/voice-ai/health
Response
{
"status": "healthy",
"service": "voice-ai-manager",
"agent_available": true,
"timestamp": "2026-02-19T14:30:00Z"
}
The status field returns "healthy" when the LangGraph voice agent is loaded, or "degraded" when the agent is unavailable.
Agent Status
Get Voice AI agent capabilities and supported intents.
GET /api/v1/voice-ai/status
Response
{
"available": true,
"capabilities": [
"query_sales",
"query_labor",
"query_inventory",
"query_performance",
"query_forecast",
"command_pricing",
"command_special",
"command_message"
],
"supported_intents": [
"query_sales",
"query_labor",
"query_inventory",
"query_performance",
"query_forecast",
"command_pricing",
"command_special",
"command_message",
"general"
],
"wake_words": ["hey maximus", "maximus", "hey max"],
"message": "Voice AI agent ready"
}
When the agent is not installed, available is false, capabilities and supported_intents are empty arrays, and message returns "Agent not installed".
Voice Queries
Process Voice Query
Send a text transcript for AI processing through the Voice AI Manager LangGraph agent. The Go gateway extracts tenant_id and user_id from the JWT token and forwards them to the Python service.
POST /api/v1/voice-ai/query
Request Body
{
"transcript": "Hey Maximus, what were my sales yesterday?",
"location_id": "loc-456",
"session_id": "sess-789",
"include_ssml": true
}
| Field | Type | Required | Description |
|---|---|---|---|
transcript | string | Yes | Voice transcript text |
location_id | string | Yes | Location identifier |
session_id | string | No | Session identifier (auto-generated if omitted) |
include_ssml | boolean | No | Include SSML for TTS synthesis (default: true) |
Response
{
"response_text": "Yesterday's sales were $11,200 across 260 orders. That's up 8% from the day before.",
"response_ssml": "<speak>Yesterday's sales were <say-as interpret-as='currency'>$11,200</say-as>...</speak>",
"intent": "query_sales",
"confidence": 0.95,
"analytics_result": {
"sales_total": 11200.00,
"order_count": 260,
"comparison_percent": 8
},
"requires_confirmation": false,
"session_id": "sess-789"
}
| Field | Type | Description |
|---|---|---|
response_text | string | Natural language response |
response_ssml | string | SSML for TTS synthesis (null if include_ssml was false) |
intent | string | Classified intent (e.g., query_sales, query_labor, general) |
confidence | float | Intent confidence score (0.0 to 1.0) |
analytics_result | object | Analytics data returned by the agent (null if not applicable) |
requires_confirmation | boolean | Whether a follow-up command needs confirmation |
session_id | string | Session identifier |
Voice Commands
Execute Command
Execute a voice command through the Voice AI Manager agent. Commands support permission checking based on user roles.
POST /api/v1/voice-ai/command
Request Body
{
"command": "Turn on Happy Hour pricing",
"location_id": "loc-456",
"session_id": "sess-789",
"confirmed": true
}
| Field | Type | Required | Description |
|---|---|---|---|
command | string | Yes | Voice command text |
location_id | string | Yes | Location identifier |
session_id | string | No | Session identifier (auto-generated if omitted) |
confirmed | boolean | No | Whether the command is pre-confirmed (default: false) |
Response
{
"success": true,
"response_text": "Done. Happy Hour pricing is now active.",
"response_ssml": "<speak>Done. Happy Hour pricing is now active.</speak>",
"command_type": "command_pricing",
"result": {
"success": true,
"pricing_mode": "happy_hour",
"activated_at": "2026-02-19T16:00:00Z"
}
}
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the command executed successfully |
response_text | string | Natural language response |
response_ssml | string | SSML for TTS synthesis (may be null) |
command_type | string | Classified command type |
result | object | Command execution result (may be null) |
Supported Command Types
| Type | Examples |
|---|---|
enable_pricing | "Turn on Happy Hour pricing" |
add_special | "Add a daily special: Grilled Salmon $24.99" |
send_message | "Send the team a message about the VIP party tonight" |
86_item | "86 the salmon", "Bring back the salmon" |
Alerts & Notifications
Get Proactive Alerts
Retrieve pending proactive alerts from Maximus for a location.
GET /api/v1/voice-ai/alerts
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Location identifier |
The tenant_id is extracted from the JWT token by the Go gateway.
Response
{
"alerts": [
{
"id": "ot-1234567890",
"type": "overtime",
"severity": "high",
"title": "Overtime Alert",
"message": "Server Maria is approaching overtime at 38.5 hours",
"data": {
"employee_name": "Maria",
"current_hours": 38.5,
"threshold": 40
},
"created_at": "2026-02-19T14:25:00Z",
"acknowledged": false
}
],
"count": 1
}
Alert Types
| Type | Description |
|---|---|
overtime | Employees approaching overtime |
inventory | Items needing to be 86'd |
sales_target | Behind or ahead of daily target |
pending_orders | Long-pending orders |
Acknowledge Alert
Mark a proactive alert as acknowledged.
POST /api/v1/voice-ai/alerts/acknowledge
Request Body
The Go gateway sends only alert_id and extracts user_id from the JWT. The Python endpoint accepts an extended body:
{
"alert_id": "ot-1234567890",
"user_id": "manager-123",
"tenant_id": "demo-restaurant",
"location_id": "loc-456",
"session_id": "sess-789",
"alert_type": "overtime"
}
| Field | Type | Required | Description |
|---|---|---|---|
alert_id | string | Yes | ID of the alert to acknowledge |
user_id | string | Yes | User acknowledging the alert |
tenant_id | string | Yes | Tenant identifier |
location_id | string | Yes | Location identifier |
session_id | string | No | Voice session ID (if from voice interaction) |
alert_type | string | No | Type of alert being acknowledged |
Response
{
"success": true,
"alert_id": "ot-1234567890",
"acknowledged_by": "manager-123",
"acknowledged_at": "2026-02-19T14:35:00Z"
}
Session State
Get Session State
Get the current state of a voice session, including pending alerts and last interaction.
GET /api/v1/voice-ai/session/{session_id}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
session_id | string | Voice session identifier |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
location_id | string | Yes | Location identifier |
The tenant_id is extracted from the JWT token by the Go gateway.
Response
{
"session_id": "sess-789",
"tenant_id": "demo-restaurant",
"location_id": "loc-456",
"is_active": true,
"last_activity": "2026-02-19T14:30:00Z",
"pending_alerts": 2,
"last_query": "What were my sales yesterday?",
"last_response": "Yesterday's sales were $11,200 across 260 orders."
}
If the session does not exist or cannot be retrieved, the endpoint returns a default inactive state with is_active: false and pending_alerts: 0.
Command Queue
The command queue endpoints exist on the Python service but are not proxied through the Go API gateway. They are only accessible by calling the Python Analytics service directly (internal service-to-service communication). The Go gateway only exposes the core Voice AI endpoints listed above (health, status, query, command, alerts, alerts/acknowledge, session/{session_id}).
The command queue enables offline-first voice command execution. When a device is offline or connectivity is limited, commands are queued locally and synced to the server when connectivity is restored. Commands are prioritized and processed in order.
Queue Command
Queue a voice command for offline or deferred execution.
POST /api/voice-ai/queue/command
Request Body
{
"tenant_id": "demo-restaurant",
"user_id": "manager-123",
"location_id": "loc-456",
"session_id": "sess-789",
"command_text": "Turn on Happy Hour pricing",
"command_type": "enable_pricing",
"command_data": {
"pricing_mode": "happy_hour"
},
"confirmed": false,
"priority": "high",
"device_id": "device-abc-123"
}
| Field | Type | Required | Description |
|---|---|---|---|
tenant_id | string | Yes | Tenant identifier |
user_id | string | Yes | User identifier |
location_id | string | Yes | Location identifier |
session_id | string | Yes | Session identifier |
command_text | string | Yes | Voice command text |
command_type | string | Yes | Command type (e.g., enable_pricing, add_special, send_message, 86_item) |
command_data | object | No | Additional command parameters |
confirmed | boolean | No | Whether the command is pre-confirmed (default: false) |
priority | string | No | Priority level: critical, high, normal, low (default: normal) |
device_id | string | No | Device ID for sync tracking |
Response
{
"command_id": "cmd-uuid-001",
"status": "pending",
"queued_at": "2026-02-19T14:30:00Z",
"priority": "high",
"estimated_execution": null
}
Get Queued Command Status
Get the current status of a specific queued command.
GET /api/voice-ai/queue/command/{command_id}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
command_id | string | Queued command identifier |
Response
{
"id": "cmd-uuid-001",
"tenant_id": "demo-restaurant",
"user_id": "manager-123",
"location_id": "loc-456",
"command_text": "Turn on Happy Hour pricing",
"command_type": "enable_pricing",
"status": "pending",
"priority": "high",
"created_at": "2026-02-19T14:30:00Z",
"updated_at": "2026-02-19T14:30:00Z",
"attempt_count": 0,
"last_error": null,
"result": null
}
| Status Value | Description |
|---|---|
pending | Queued and waiting for execution |
executing | Currently being processed |
completed | Successfully executed |
failed | Failed after all retry attempts |
cancelled | Cancelled by a user |
Returns 404 if the command ID is not found.
Get Pending Commands
Get all pending commands in the queue for a tenant.
GET /api/voice-ai/queue/pending
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tenant_id | string | Yes | Tenant identifier |
limit | integer | No | Maximum results (1-100, default: 50) |
Response
[
{
"id": "cmd-uuid-001",
"tenant_id": "demo-restaurant",
"user_id": "manager-123",
"location_id": "loc-456",
"command_text": "Turn on Happy Hour pricing",
"command_type": "enable_pricing",
"status": "pending",
"priority": "high",
"created_at": "2026-02-19T14:30:00Z",
"updated_at": "2026-02-19T14:30:00Z",
"attempt_count": 0,
"last_error": null,
"result": null
}
]
Cancel Queued Command
Cancel a pending queued command. Only commands in pending status can be cancelled.
DELETE /api/voice-ai/queue/command/{command_id}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
command_id | string | Queued command identifier |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | string | Yes | User requesting the cancellation |
Response
{
"success": true,
"command_id": "cmd-uuid-001",
"status": "cancelled"
}
If the command cannot be cancelled (e.g., already executing or completed), success is false and status reflects the current state.
Get Queue Statistics
Get statistics about the offline command queue.
GET /api/voice-ai/queue/stats
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tenant_id | string | No | Filter statistics by tenant (omit for global stats) |
Response
{
"pending_count": 5,
"executing_count": 1,
"completed_count": 142,
"failed_count": 3,
"total_processed": 145,
"total_retries": 8,
"average_execution_time_ms": 320.5,
"oldest_pending_age_seconds": 45.2
}
Sync Commands from Device
Sync queued commands from an offline device. When a device reconnects after being offline, it sends all locally queued commands to the server for execution.
POST /api/voice-ai/queue/sync
Request Body
{
"tenant_id": "demo-restaurant",
"device_id": "device-abc-123",
"commands": [
{
"device_id": "local-cmd-1",
"user_id": "manager-123",
"location_id": "loc-456",
"command_text": "Turn on Happy Hour pricing",
"command_type": "enable_pricing"
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
tenant_id | string | Yes | Tenant identifier |
device_id | string | Yes | Device identifier |
commands | array | Yes | Array of command objects to sync |
Response
{
"synced_count": 1,
"id_mapping": {
"local-cmd-1": "cmd-uuid-001"
}
}
The id_mapping maps the device-local command IDs to the server-assigned IDs. If a command fails to sync, its value in the mapping will be prefixed with error:.
Trigger Queue Processing
Manually trigger processing of pending commands. This is typically called when connectivity is restored after a period of being offline.
POST /api/voice-ai/queue/process
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tenant_id | string | Yes | Tenant identifier |
Response
{
"success": true,
"processed_count": 5,
"timestamp": "2026-02-19T14:35:00Z"
}
Dead Letter Queue
Dead letter queue endpoints are part of the internal Python service and are not proxied through the Go API gateway.
Commands that fail after all retry attempts are moved to the dead letter queue (DLQ). These commands can be inspected and manually retried.
Get Dead Letter Queue
Get commands that have permanently failed.
GET /api/voice-ai/queue/dlq
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
tenant_id | string | Yes | Tenant identifier |
limit | integer | No | Maximum results (1-100, default: 50) |
Response
[
{
"id": "cmd-uuid-003",
"tenant_id": "demo-restaurant",
"user_id": "manager-123",
"location_id": "loc-456",
"command_text": "Send the team a message about the VIP party",
"command_type": "send_message",
"status": "failed",
"priority": "normal",
"created_at": "2026-02-19T12:00:00Z",
"updated_at": "2026-02-19T12:05:00Z",
"attempt_count": 3,
"last_error": "Notification service unavailable",
"result": null
}
]
Retry Command from DLQ
Re-queue a failed command from the dead letter queue for another execution attempt. The command must be in failed status.
POST /api/voice-ai/queue/dlq/{command_id}/retry
Path Parameters
| Parameter | Type | Description |
|---|---|---|
command_id | string | Failed command identifier |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | string | Yes | User requesting the retry |
Response
{
"success": true,
"command_id": "cmd-uuid-003",
"status": "pending"
}
Returns 404 if the command is not found. Returns 400 if the command is not in failed status.
WebSocket Streaming
The WebSocket endpoint exists on the Python service but is not proxied through the Go API gateway. It is only accessible by connecting directly to the Python Analytics service.
WebSocket Connection
Connect for real-time voice interaction with Maximus.
ws://{python-service}/api/voice-ai/stream/{session_id}?tenant_id={tenant_id}&user_id={user_id}&location_id={location_id}
Client Messages
| Type | Description |
|---|---|
text_query | Send a text query for processing |
command | Execute a voice command |
acknowledge_alert | Acknowledge a proactive alert |
ping | Keep-alive ping |
Text Query
{
"type": "text_query",
"text": "What are today's sales?"
}
Command
{
"type": "command",
"command": "Turn on Happy Hour pricing",
"confirmed": true
}
Acknowledge Alert
{
"type": "acknowledge_alert",
"alert_id": "ot-1234567890",
"alert_type": "overtime"
}
Server Messages
| Type | Description |
|---|---|
state | Session state update (sent on connection) |
response | Query response with analytics data |
command_response | Command execution result |
alert | Proactive alert pushed to client |
alert_acknowledged | Confirmation of alert acknowledgment |
pong | Keep-alive response |
error | Error message |
State (on connection)
{
"type": "state",
"status": "connected",
"session_id": "sess-789",
"timestamp": "2026-02-19T14:30:00Z"
}
Response
{
"type": "response",
"response_text": "Today's sales are $12,450...",
"response_ssml": "<speak>...</speak>",
"intent": "query_sales",
"confidence": 0.95,
"analytics_result": {...},
"timestamp": "2026-02-19T14:30:05Z"
}
Alert (pushed periodically)
{
"type": "alert",
"alert": {
"id": "ot-1234567890",
"type": "overtime",
"severity": "high",
"title": "Overtime Alert",
"message": "Server Maria is approaching overtime at 38.5 hours",
"data": {...},
"created_at": "2026-02-19T14:25:00Z"
}
}
Proactive alerts are checked every 30 seconds and pushed to the connected client.
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | validation_error | Invalid request body or missing required fields |
| 401 | tenant_required | JWT token missing or tenant context not available |
| 403 | permission_denied | User role does not have permission for the command |
| 404 | not_found | Session or command not found |
| 500 | service_error | Internal processing error |
| 503 | service_unavailable | Voice AI agent not loaded or Python service unreachable |
Related Documentation
- Voice AI Manager Guide - Setup guide
- Voice AI Sessions - Voice sessions API
- Drive-Thru Voice - Drive-thru voice