Last updated: 2026-05-20.
Base URL in the current lab:
http://192.168.50.222:25480
Agent workspaces inside the API container should use:
http://localhost:8000
All request/response bodies are JSON unless noted. The API is the canonical
contract for tickets, agents, approvals, workflows, setup, tools, learning, and
audit across provider-backed enterprise work. It uses raw PostgreSQL through
asyncpg; no application ORM/Pydantic/SQLAlchemy models are used.
External product APIs are documented separately from this dashboard API. The reference Mailcow HTTP compatibility shim is documented in docs/MAILCOW_API_SHIM.md; it exposes read-only Mailcow-style domain, mailbox, and alias endpoints on the Mailcow host and is not part of the dashboard API namespace.
GET /health
Returns API status and version.
GET /api/tickets
Query parameters:
statuspriorityassigneeagent_onlylimitoffset
Returns canonical ticket rows with provider metadata and external_url when available.
POST /api/tickets
Creates a canonical ticket.
Example:
{
"title": "Investigate phishing report",
"description": "User reported suspicious email.",
"ticket_class": "Incident",
"status": "new",
"priority": "2",
"created_by": "dashboard",
"auto_assign": true
}Ticket creation syncs to the active external provider automatically when one is
configured, and falls back to provider_sync_status=local_only otherwise. For
iTop Incident/UserRequest creation, configure ITOP_DEFAULT_ORG_ID and
ITOP_DEFAULT_CALLER_ID. When auto_assign is true, the dashboard evaluates
enabled RACI rules with auto_assign_agent=true and spawns an agent only when
one of those rules matches.
GET /api/tickets/{ticket_id}
Returns ticket detail, current agent fields, change requests, and external_url.
GET /api/tickets/{ticket_id}/context
Returns the full agent context bundle:
- ticket
- notes
- attachments
- change requests
- recent tasks
- postmortems
- related tickets
- knowledge articles
- matching workflows
- global skills
- access requests linked to the ticket or created from the ticket
POST /api/tickets/{ticket_id}/notes
Example:
{
"body": "Scoped recipients and found no additional delivery.",
"author": "agent_26",
"source": "agent",
"visibility": "internal"
}When the note source is a human or ticketing provider source such as
dashboard, itop, servicenow, jira, provider, requester, or
user-response, the dashboard creates non-interrupting steering events for any
currently active ticket agents. The runner mirrors those updates into the
agent's work directory as agent_steering_inbox.json and AGENT_STEERING.md.
Agent-authored and control-plane notes are ignored for steering so agents do
not steer themselves from their own progress notes.
GET /api/agents/{agent_id}/steering
Lists recent steering events delivered or pending for an agent.
POST /api/agents/{agent_id}/steering/{event_id}/ack
Marks a steering event acknowledged after the agent incorporates it.
POST /api/tickets/{ticket_id}/attachments
Stores attachment metadata only. Binary storage should be external.
GET /api/tickets/{ticket_id}/access-requests
Lists permission-wall access requests linked to this ticket, including the child access ticket, approval gate, and grant status.
POST /api/tickets/{ticket_id}/access-request
Creates a child access request ticket assigned to the owning RACI group and a change approval gate linked to the original ticket/agent. Approval of that gate resumes the original ticket when no active task is already running.
Example:
{
"agent_id": 123,
"resource": "GitLab project demo/private-infra",
"permission": "Developer repository read access",
"account_ref": "agent-123",
"assignment_group": "DevSecOps",
"risk_level": "medium",
"sync_provider": false,
"reason": "Repository API returned 403; least-privilege read access is required before evidence can be reviewed."
}POST /api/tickets/{ticket_id}/request-info
Adds a user-visible clarification request note and sets ticket status to
awaiting_user_response.
POST /api/tickets/{ticket_id}/user-response
Records the requester answer, restores the previous ticket status, and can resume the assigned agent when no active task is already running.
POST /api/tickets/{ticket_id}/sync
Pulls a provider ticket into the canonical record.
POST /api/tickets/sync-all
Body can be omitted, a raw provider string, or:
{"provider": "itop"}POST /api/tickets/{ticket_id}/push-provider
Pushes an existing canonical ticket to a provider.
Example:
{"provider": "itop"}POST /api/tickets/{ticket_id}/status
Explicit ticket status update. Agent completion does not close tickets
automatically; default ticket workflows make the agent call this endpoint after
final evidence and verification. Human-review deployments can skip the call or
set close_provider: false when the external record should remain open.
Example:
{
"status": "resolved",
"actor": "agent-159",
"reason": "All approved containment steps completed, tests passed, residual risk documented.",
"close_provider": true
}Use close_provider: false when the dashboard status should change but the
external ITSM record should remain open for human review.
Compatibility: POST, PUT, and PATCH /api/tickets/{ticket_id} accept the
same explicit status payload. This exists for local agents that infer a REST
update on the ticket resource itself. The compatibility path uses the same
access checks, status validation, audit note, and provider-close opt-in behavior
as /status; new code should still prefer /status.
POST /api/tickets/{ticket_id}/assign-agent
Body:
{
"model": "qwen/qwen3.6-27b",
"harness": "hermes",
"profile_id": "local-only",
"prompt": "Optional override prompt"
}All fields are optional. If model is omitted, the runner resolves the active
Settings runtime profile for the ticket scope. This is the preferred path for
Ops Chat-created tickets. When CODEX_AUTH_MODE=oauth, stale local/provider
aliases such as local/agent-default, qwen/..., or deepseek/... are
repaired to the active Codex subscription model before the task is spawned.
POST /api/agents/{agent_id}/deploy/static-site
Publishes a static site artifact from the agent work directory to the
dashboard-managed /published/<slug>/ surface. This is the supported path for
demo-safe "make this web page reachable from the dashboard" work. It requires
an approved change gate linked to the same agent/ticket and the caller must
have deployments:write.
Example:
{
"change_id": 42,
"source_dir": "hello",
"slug": "hello-demo",
"public_base_url": "https://192.168.50.222:25443"
}Response:
{
"status": "deployed",
"change_id": 42,
"deployment": {
"relative_url": "/published/hello-demo/",
"public_url": "https://192.168.50.222:25443/published/hello-demo/"
}
}The adapter only accepts a static tree with index.html, blocks symlinks and
path escapes, writes a ticket evidence note, completes the change gate, and
records a static_site_deployed audit event. Container-local preview URLs such
as http://127.0.0.1:<port>/ are not deployments unless they are merely test
evidence before this adapter or another approved deployment target is used.
POST /api/tickets/{ticket_id}/postmortem
Spawns a postmortem agent.
POST /api/tickets/{ticket_id}/workflow
Spawns a workflow-build agent.
POST /api/tickets/{ticket_id}/unassign-agent
Terminates and unassigns the ticket's current agent.
GET /api/providers
Lists registered providers. Current providers:
localitop
POST /api/providers/{provider}/sync-all
Pull sync for one provider.
POST /api/providers/{provider}/sync-ticket
Body:
{
"ticket_class": "Incident",
"ticket_ref": "123"
}GET /api/agents
Filters:
statusticket_id
GET /api/agents/active
Returns active agent records.
GET /api/agents/stats
Returns aggregate counts and average duration.
GET /api/agents/models
Returns models and non-secret runtime settings from agent_models.json,
including active_profile, profiles, route_assignments,
max_concurrent_agents, default_timeout_minutes, and available harnesses.
GET /api/agents/config
Returns the editable runtime configuration used by the Settings page.
PUT /api/agents/config
Persists non-secret runtime configuration. Supported fields include
active_profile, profiles, route_assignments, max_concurrent_agents,
default_timeout_minutes, default_harness, default_model, and models.
Provider tokens, OAuth state, and API keys are never accepted here.
GET /api/agents/runner-health
Returns:
- selected harness availability
- Hermes, Claude Code, and Codex diagnostics when configured
- Codex
codex_auth_modeandcodex_login_status; OAuth/subscription deployments should showcodex_login_status.status=logged_inafter the device-auth enrollment gate completes - credentials mount status
- configured harness
- default model
- effective model API/proxy URL
- model API reachability
- permission mode and allowed tools
GET /api/agents/processes
Returns process diagnostics from inside the API container, including ps_path, process list, and tracked active processes.
POST /api/agents/spawn
Spawns an agent for an existing ticket.
POST /api/agents/create-from-prompt
Creates a local ticket and spawns an ad hoc agent.
GET /api/agents/tasks
Filters:
statusagent_idticket_id
GET /api/agents/tasks/{task_id}/logs
Returns output.log tail or DB output fallback.
GET /api/agents/{agent_id}
Returns agent detail, latest task, changes, and audit records.
GET /api/agents/{agent_id}/logs
Returns latest task logs for an agent.
GET /api/agents/{agent_id}/wazuh/manager/status
Returns Wazuh manager status only after the agent has an active scoped
wazuh/api/wazuh.manager/read vault lease. Response includes lease_id,
credential_ref, secret_values_returned: false, and Wazuh status data.
GET /api/agents/{agent_id}/wazuh/rules/{rule_id}
Returns Wazuh rule metadata after validating the same scoped Wazuh lease.
GET /api/agents/{agent_id}/wazuh/alerts/search
Query params:
rule_idsource_iplimit
Searches Wazuh indexer alerts after validating the scoped Wazuh lease. Requires
runtime WAZUH_INDEXER_* configuration. Secret values are never returned.
POST /api/agents/{agent_id}/wake
Refreshes active task heartbeat or spawns a replacement from latest task prompt.
POST /api/agents/{agent_id}/restart
Stops active task, terminates old agent row, and spawns replacement.
POST /api/agents/{agent_id}/stop
Body:
{"reason": "operator_stop"}POST /api/agents/{agent_id}/update
Legacy/manual status update endpoint.
GET /api/agents/ws
WebSocket for real-time agent events.
GET /api/changes
Filters:
statusagent_idticket_id
GET /api/changes/pending
Returns pending non-expired changes.
GET /api/changes/stats
Aggregate counts.
GET /api/changes/{change_id}
Full change detail.
GET /api/changes/{change_id}/status
Compact polling endpoint for agents.
POST /api/changes/request
Example:
{
"agent_id": 26,
"ticket_id": 28,
"action": "block_url",
"target": "https://example.invalid/phish",
"reason": "Confirmed phishing URL.",
"command": "no-op in lab",
"risk_level": "medium",
"approval_policy": {"requires_human": true}
}POST /api/changes/{change_id}/approve
{"approved_by": "operator", "reason": "Reviewed evidence and approved scoped action."}Demo/lab auto-approvers are test-only and should be enabled only by an
unattended regression script, for example with --auto-approve-gates. Live
demo and production-style flows should use a human/operator approval identity.
Manual approval audit payloads include approval_gate=true,
approval_mode=manual_approval, and auto_approved=false. Test-only
auto-approval payloads include approval_mode=demo_auto_approval and
auto_approved=true.
POST /api/changes/{change_id}/reject
{"rejected_by": "operator", "reason": "Insufficient evidence"}POST /api/changes/{change_id}/complete
{"completed_by": "agent_26", "result": "URL block applied in test environment with verification evidence."}Completed changes write both event and audit records. The agent supervisor can
also auto-complete approved agent-linked changes after a completed task, unless
the approval policy sets auto_complete=false or
manual_completion_required=true.
Change request, approval, rejection, and completion transitions also write canonical ticket notes so the ticket timeline shows the approval chain during demos and audits.
Postmortems:
GET /api/postmortemsGET /api/postmortems/evidence/{ticket_id}- compact postmortem evidence for agents, including notes, attachment metadata, change requests, task summaries with bounded log tails, CI/CD runs, prior postmortems, and audit/event entriesGET /api/postmortems/{id}- includespromotion_assetswith promoted knowledge articles, skills,workflow_key, and the promoted workflow resolved by promotion audit details or by key fallback.POST /api/postmortems/synthesize/{ticket_id}- supervisor fallback that creates aready_for_reviewpostmortem from bounded evidence when a model postmortem fails or stallsPOST /api/postmortemsPUT /api/postmortems/{id}POST /api/postmortems/{id}/reviewPOST /api/postmortems/{id}/promote- turns an approved/reviewed postmortem into reusable assets: a knowledge article, draft workflow, candidate skills, a ticket note, and audit/event records. The workflow is draft by default and includes an approval policy requiring human review before production activation. Promotion derivesworkflow_keyfrom the ticket class and postmortem lesson. Similar postmortems update/version the same non-superseded workflow and returnworkflow_actionplusworkflow_keyinstead of creating postmortem-id/name duplicates.
Workflows:
GET /api/workflowsGET /api/workflows/{id}POST /api/workflows- derives or honorsapproval_policy.workflow_key. If a non-superseded workflow with that key already exists, the route updates/versions that workflow; names are display labels, not identity. Create/update paths keep workflow status review-gated and do not silently activate automation.PUT /api/workflows/{id}- updates workflow content, recomputes/storesworkflow_key, writes it intoapproval_policy, and versions the workflow.POST /api/workflows/{id}/reviewPOST /api/workflows/{id}/runsPOST /api/workflows/runs/{run_id}/complete
Service desk intake:
GET /api/intake/raciPOST /api/intake/raci/groupsPUT /api/intake/raci/groups/{id}DELETE /api/intake/raci/groups/{id}POST /api/intake/raci/rulesPUT /api/intake/raci/rules/{id}DELETE /api/intake/raci/rules/{id}POST /api/intake/clarifyPOST /api/intake/classifyPOST /api/intake/submitGET /api/intake/sessions
RACI rules may include auto_assign_agent, auto_agent_model, and
auto_agent_prompt. The seeded phishing rule enables auto-assignment for
Security Operations phishing incidents; other rules remain manual unless this
flag is set. POST /api/intake/submit accepts auto_assign=false for smoke
tests or manual-only submissions.
CI/CD security:
GET /api/cicd/gitlab/templateGET /api/cicd/runsGET /api/cicd/runs/{run_id}- includesrepo_url, internal dashboard scanner report links, external provider artifact links parsed fromtool_results, and related before/after runs for the same ticket or repositoryGET /api/cicd/runs/{run_id}/reports/{tool}- returns the auth-protected dashboard report forsemgrep,trivy,owasp_zap, ornucleifrom the stored canonical run record so operators do not need CI provider credentials to read findingsPOST /api/cicd/runs
Global search:
GET /api/search/global?q=<query>&limit=60- RBAC-aware search across tickets, notes, agents, approval gates, postmortems, workflows, CI/CD runs, tools, and audit records. Ticket results are row-level scoped and the endpoint does not return raw secrets.
Ops Chat:
GET /api/ops-chat/sessionsGET /api/ops-chat/sessions/{session_id}/messagesPOST /api/ops-chat/message- Matrix/Element chat intake that creates or continues traceable tickets for operational work and queues real Hermes/Claude Code/Codex agent harness tasks. Optionalattachmentsentries can includefilename,content_type,size_bytes,storage_ref, and boundeddata_base64for Matrix-uploaded files. Optionalharness/agent_harnessandmodel/agent_modelfields can override the chat-intake harness for a targeted room, smoke, or demo request; otherwise the endpoint followsOPS_CHAT_AGENT_HARNESS/AGENT_HARNESSandOPS_CHAT_AGENT_MODEL/AGENT_DEFAULT_MODEL.GET /api/ops-chat/outbound/pending- Matrix bridge poll endpoint for user-facing ticket questions/status updates created by ticket agentsPOST /api/ops-chat/outbound/ack- idempotently acknowledges outbound Matrix delivery so bridge restarts do not duplicate ticket updatesGET /api/ops-chat/matrix/health- Matrix/Element/Keycloak bridge readiness metadataGET /api/ops-chat/openai/v1/models- legacy compatibility model list; Matrix/Element is the supported chat clientPOST /api/ops-chat/openai/v1/chat/completions- legacy compatibility endpoint that still routes operational work into tickets and real agents
POST /api/ops-chat/message is agent-intake-first. The dashboard invokes the
configured Hermes/Claude/Codex harness with ops_chat_tool.py; the harness must
finish with an answer tool call for harmless chat or a create-ticket tool
call for tracked work. It may ask one concise clarification before ticket
creation when the answer changes routing/scope/urgency. Approval gates are not
created by the chat intake turn; they are created later by real ticket execution
barriers such as access requests, scoped vault leases, workflow policy, or
provider permission failures.
The harness override is validation-only and modular. The endpoint rejects
unknown harness names and accepts only the names registered in
services.agent_harness (hermes, claude-code, codex in the current
deployment). Codex does not bypass the bridge; it is invoked through the same
toolbelt and dashboard runner contract as Hermes and Claude Code.
Claude Code uses the proxy's Anthropic Messages route. Runtime env must provide
the proxy token as ANTHROPIC_API_KEY as well as ANTHROPIC_AUTH_TOKEN, because
newer Claude Code builds report apiKeySource: none and retry indefinitely
when only the legacy token variable is present.
Ops Chat harness calls default to one-hour local-agent windows. The endpoint cleans up child processes on server-side timeout or client cancellation, but operators should not use short HTTP client timeouts for local model tests.
For chat uploads, the dashboard stores bounded file payloads under
OPS_CHAT_UPLOAD_DIR, links them to operational tickets as attachment metadata,
and copies them into the chat harness workspace under attachments/. Agent
artifacts returned through validate-artifact are stored under
OPS_CHAT_ARTIFACT_DIR; small artifacts may be returned to Matrix as
downloadable files.
POST /api/tickets/{ticket_id}/assignment
Updates the canonical dashboard assignment fields and writes a
ticket-assignment note for auditability. Use this when a chat/ticket agent or
operator discovers that the scope belongs to another queue or must move to Tier
2/Tier 3.
Request body:
{
"assignee_team": "Tier 2 Endpoint Support",
"owning_group": "Endpoint Support",
"assignee": "endpoint.tier2.demo",
"escalation_tier": "Tier 2",
"priority": "P2",
"actor": "ops-chat-reassignment-smoke",
"reason": "Requester clarified that endpoint packaging is required."
}Notes:
priorityacceptsP1-P4or numeric1-4.escalation_tieris audit evidence in the note trail; it is not a separate database column.- Provider-side assignment sync is adapter-specific. The canonical dashboard assignment and note are always updated.
Knowledge:
GET /api/knowledgeGET /api/knowledge/{id}POST /api/knowledgePUT /api/knowledge/{id}
Skills:
GET /api/skillsGET /api/skills/{id}POST /api/skillsPUT /api/skills/{id}DELETE /api/skills/{id}POST /api/skills/{id}/renderGET /api/skills/agent/{agent_id}
GET /api/dashboard/stats
Overview counts, trends, pending changes, and recent activity. Recent activity
includes audit, event, and note sources so the overview feed can show
human-readable ticket notes during agent work.
GET /api/dashboard/audit
Filters:
actoractionsourcecategoryleveltargetqticket_idagent_idlimit
Merges audit_log, event_log, and canonical ticket_notes.
source values:
audit: durable audit rows.event: operational events.note: ticket notes, including agent progress notes and agent-authored notes.
ticket_id and agent_id are normalized from JSON details when available so
the frontend can deep-link from a ticket detail modal into the full trail.
GET /api/toolsGET /api/tools/statusGET /api/tools/{id}GET /api/tools/{id}/historyPOST /api/tools/{id}/checkPOST /api/tools/check-all