ExceptionOps DEMO

API docs

How to test ExceptionOps from a terminal: send signed machine events, see how the engine classifies and dispatches them, then work the task as a field operator and watch the proof get verified.

All data here is mock-up data. The fleet, robots, vehicles, operators, coordinates and keys are synthetic, and nothing is connected to a real machine. The demo resets every 30 minutes, so exception and task ids start again from EX-1 / TASK-1.

Quick start: one exception, start to finish

Base URL https://exceptionops.enthernetservice.com. Send a demo token as Authorization: Bearer <token>.

# 1. The fleet reports an open vehicle door
curl -s -X POST -H "Authorization: Bearer demo-dispatcher" https://exceptionops.enthernetservice.com/api/v1/demo/emit/A
# → {"exception_id":"EX-1","status":"DISPATCHED",…}

# 2. See who it was offered to (matching.ranked[0]) and the task id (current_task)
curl -s -H "Authorization: Bearer demo-viewer" https://exceptionops.enthernetservice.com/api/v1/exceptions/EX-1

# 3. The operator accepts, starts and resolves it with proof
curl -s -X POST -H "Authorization: Bearer demo-op-OP-12" https://exceptionops.enthernetservice.com/api/v1/tasks/TASK-1/accept
curl -s -X POST -H "Authorization: Bearer demo-op-OP-12" https://exceptionops.enthernetservice.com/api/v1/tasks/TASK-1/start
curl -s -X POST -H "Authorization: Bearer demo-op-OP-12" -H "Content-Type: application/json" \
  -d '{"resolution_type":"FIXED_ON_SITE","evidence":[{"type":"PHOTO","content":{"caption":"door closed"}},{"type":"CHECKLIST","content":{"door_closed":true,"cabin_clear":true}}],"telemetry":{"door_state":"CLOSED"},"checklist":{"door_closed":true,"cabin_clear":true}}' \
  https://exceptionops.enthernetservice.com/api/v1/tasks/TASK-1/resolve
# → status CLOSED: proof present, hashes match, telemetry confirms; resume signal issued

# 4. Read the hash-chained audit trail
curl -s -H "Authorization: Bearer demo-auditor" https://exceptionops.enthernetservice.com/api/v1/exceptions/EX-1/audit

If others have run scenarios since the last reset, use the exception id step 1 returns, and the operator and task id from step 2 (matching.ranked[0].operator_id, current_task).

Try it

Response appears here.

Credentials

Static demo tokens for synthetic users (production uses SSO). The integration key and secret sign machine events.

Bearer tokenWho
demo-ownerOwner · everything
demo-dispatcherDispatcher · emit, classify, cancel
demo-auditorAuditor · read + audit trail
demo-viewerViewer · read only
demo-op-OP-11Field operator OP-11
demo-op-OP-12Field operator OP-12
demo-op-OP-14Field operator OP-14
demo-op-OP-16Field operator OP-16
demo-other-ownerOwner of another tenant · sees none of the demo fleet
key-demo-fleet / demo-secret-rotate-meIntegration key and HMAC secret of the synthetic fleet

Endpoints

Intake (machines)

POST/api/v1/exceptionsSigned event from a fleet or robot

Who: integration key (key-demo-fleet)

BODY='{"event_id":"EV-2001","asset_id":"BOT-19","event_type":"PATH_BLOCKED","severity_hint":"MEDIUM",
"metadata":{"safety_state":"SAFE"},"location":[0.015,0.012],"telemetry":{"path_state":"BLOCKED"},
"timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}'
TS=$(date +%s)
SIG=$(printf '%s' "$TS.$BODY" | openssl dgst -sha256 -hmac demo-secret-rotate-me | sed 's/^.* //')
curl -X POST -H "Content-Type: application/json" \
  -H "X-ExceptionOps-Key: key-demo-fleet" -H "X-ExceptionOps-Timestamp: $TS" -H "X-ExceptionOps-Signature: $SIG" \
  -d "$BODY" https://exceptionops.enthernetservice.com/api/v1/exceptions
{"exception_id": "EX-2", "status": "DISPATCHED", "duplicate": false}

Signature: hex(HMAC-SHA256(secret, "<timestamp>." + raw_body)). Rejections come back as 401 with MISSING_HEADERS, UNKNOWN_KEY, BAD_TIMESTAMP, STALE_TIMESTAMP, BAD_SIGNATURE or REPLAY (a signature can only be used once). A repeated event_id returns 200 with "duplicate": true. Body max 64 KB; intake is rate-limited per key.

Fields: event_id, asset_id (AV-104, BOT-19, BOT-42, BOT-77…), event_type, severity_hint, metadata, location [lat, lon], telemetry, timestamp.

POST/api/v1/demo/emit/{A|B|C|D}Have the synthetic fleet sign and send a scenario event

Who: owner, dispatcher

curl -X POST -H "Authorization: Bearer demo-dispatcher" https://exceptionops.enthernetservice.com/api/v1/demo/emit/A
{"exception_id": "EX-1", "status": "DISPATCHED", "duplicate": false}

A = open vehicle door (rule match, dispatch) · B = robot obstruction · C = ambiguous telemetry (no rule; AI suggests, a human confirms) · D = critical mechanical fault.

Dispatchers

GET/api/v1/exceptionsAll exceptions for your tenant

Who: any token

curl -H "Authorization: Bearer demo-viewer" https://exceptionops.enthernetservice.com/api/v1/exceptions
GET/api/v1/exceptions/{id}History, policy decision and operator matching for one exception

Who: any token

curl -H "Authorization: Bearer demo-viewer" https://exceptionops.enthernetservice.com/api/v1/exceptions/EX-1
{
  "id": "EX-1",
  "asset_external_id": "AV-104",
  "event_type": "OPEN_DOOR",
  "type": "OPEN_DOOR",
  "severity": "LOW",
  "status": "DISPATCHED",
  "classification_method": "DETERMINISTIC",
  "ai_used": false,
  "current_task": "TASK-1",
  "history": [
    {
      "state": "VERIFIED",
      "actor_type": "INTEGRATION",
      "explanation": "HMAC signature, timestamp window and replay check passed."
    },
    {
      "state": "DISPATCHED",
      "actor_type": "SYSTEM",
      "explanation": "Offered to OP-12 (score 85.6, 0.8 km, ETA 2 min). Respond within 60s."
    }
  ],
  "matching": {
    "ranked": [
      {
        "operator_id": "OP-12",
        "score": 85.6,
        "distance_km": 0.8
      }
    ],
    "excluded": [
      {
        "operator_id": "OP-14",
        "reasons": [
          "missing skill BASIC_FIELD_ASSISTANCE"
        ]
      }
    ]
  },
  "decision": {
    "policy_id": "POL-AV-OPEN-DOOR",
    "required_skill": "BASIC_FIELD_ASSISTANCE",
    "sla_seconds": 480,
    "proof": [
      "PHOTO",
      "CHECKLIST"
    ],
    "expected_telemetry": {
      "door_state": "CLOSED"
    },
    "checklist": [
      "door_closed",
      "cabin_clear"
    ]
  }
}
GET/api/v1/exceptions/{id}/auditHash-chained audit trail

Who: owner, auditor

curl -H "Authorization: Bearer demo-auditor" https://exceptionops.enthernetservice.com/api/v1/exceptions/EX-1/audit
[
  {
    "seq": 1,
    "actor_type": "INTEGRATION",
    "actor_id": "key-demo-fleet",
    "event_type": "EXCEPTION_RECEIVED",
    "resource_id": "EX-1",
    "prev_hash": "0000\u2026",
    "hash": "41ef8c13\u2026"
  }
]
POST/api/v1/exceptions/{id}/ai-reviewAsk the advisory model for a classification suggestion

Who: owner, dispatcher

curl -X POST -H "Authorization: Bearer demo-dispatcher" https://exceptionops.enthernetservice.com/api/v1/exceptions/EX-3/ai-review

Stored as ai_suggestion (model, suggested_classification, confidence, explanation). It never changes the type or dispatches anyone.

POST/api/v1/exceptions/{id}/confirm-classificationA human sets the type of an unclassified exception

Who: owner, dispatcher

curl -X POST -H "Authorization: Bearer demo-dispatcher" -H "Content-Type: application/json" \
  -d '{"exception_type":"OBSTRUCTION","note":"Matches drive current and the AI suggestion"}' \
  https://exceptionops.enthernetservice.com/api/v1/exceptions/EX-3/confirm-classification

The exception becomes HUMAN_CONFIRMED and goes on to policy and matching.

POST/api/v1/exceptions/{id}/cancelCancel an exception

Who: owner, dispatcher

curl -X POST -H "Authorization: Bearer demo-dispatcher" -H "Content-Type: application/json" \
  -d '{"reason":"Duplicate of EX-1"}' https://exceptionops.enthernetservice.com/api/v1/exceptions/EX-2/cancel

Field operators

POST/api/v1/tasks/{task_id}/acceptAccept an offered task

Who: the operator it was offered to

curl -X POST -H "Authorization: Bearer demo-op-OP-12" https://exceptionops.enthernetservice.com/api/v1/tasks/TASK-1/accept

Use the operator from matching.ranked[0] and the task from current_task. Anyone else gets 403.

POST/api/v1/tasks/{task_id}/rejectTurn down an offer; the next eligible operator is offered it

Who: the operator it was offered to

curl -X POST -H "Authorization: Bearer demo-op-OP-12" https://exceptionops.enthernetservice.com/api/v1/tasks/TASK-1/reject
POST/api/v1/tasks/{task_id}/startMark the task in progress

Who: the assigned operator

curl -X POST -H "Authorization: Bearer demo-op-OP-12" https://exceptionops.enthernetservice.com/api/v1/tasks/TASK-1/start
POST/api/v1/tasks/{task_id}/resolveSubmit the fix with proof; the engine verifies it

Who: the assigned operator

curl -X POST -H "Authorization: Bearer demo-op-OP-12" -H "Content-Type: application/json" \
  -d '{"resolution_type":"FIXED_ON_SITE","notes":"Door closed, cabin clear",
       "evidence":[{"type":"PHOTO","content":{"caption":"rear left door closed"}},
                   {"type":"CHECKLIST","content":{"door_closed":true,"cabin_clear":true}}],
       "telemetry":{"door_state":"CLOSED"},
       "checklist":{"door_closed":true,"cabin_clear":true}}' \
  https://exceptionops.enthernetservice.com/api/v1/tasks/TASK-1/resolve

History then shows RESOLVED → VERIFICATION_PENDING → VERIFIED_COMPLETE ("Proof present, hashes match, telemetry confirms") → CLOSED ("Resume signal issued"). Missing proof or telemetry that disagrees fails verification. resolution_type: FIXED_ON_SITE or UNABLE_TO_RESOLVE; evidence types: PHOTO, CHECKLIST, CONFIRMATION_CODE, TELEMETRY.

Status

GET/api/v1/dashboardLive counters for your tenant

Who: owner, dispatcher, auditor, viewer

curl -H "Authorization: Bearer demo-viewer" https://exceptionops.enthernetservice.com/api/v1/dashboard
{
  "demo_mode": true,
  "active_assets": 5,
  "open_exceptions": 0,
  "pending_dispatches": 0,
  "operators_available": 4,
  "avg_dispatch_seconds": null,
  "avg_resolution_seconds": null,
  "sla_breaches": 0,
  "ai_assisted_cases": 0,
  "escalated": 0
}
GET/healthzHealth and mode

Who: anyone

curl https://exceptionops.enthernetservice.com/healthz
{"ok": true, "demo_mode": true, "ai_enabled": true}

States

RECEIVED → VERIFIED → CLASSIFIED → MATCHING → DISPATCHED → ACCEPTED → IN_PROGRESS → RESOLVED → VERIFICATION_PENDING → VERIFIED_COMPLETE → CLOSED

Side paths: UNCLASSIFIED (no rule matched; waits for a human, AI may only suggest), NO_OPERATOR_AVAILABLE, DISPATCH_TIMEOUT and OPERATOR_REJECTED (offered to the next operator), RESOLUTION_FAILED, VERIFICATION_FAILED, ESCALATED and CANCELLED. Every step is in history with who did it and why.

Errors

401{"detail":"invalid token"} for users; intake errors like {"error":"REPLAY"}
403{"error":"FORBIDDEN","detail":"role VIEWER lacks audit.read"}, or another tenant's exception
404{"error":"NOT_FOUND"}
409{"error":"ILLEGAL_TRANSITION"}: the action doesn't fit the current state
413Intake body over 64 KB
422INVALID_EVENT or a body that fails validation
429Rate limited