Skip to content
BreachPilot

Endpoint Matrix

Generated from code — no invented routes. Handler names are the Python functions decorated with @router.* in each tools/api/routes/*.py. Auth column is the FastAPI dependency applied (bearer on every route except GET /health; WS uses first-message bearer). Frontend consumer is the WebUI hook/component that calls the endpoint (from webui/src/api/* and webui/src/features/*); when no dedicated consumer exists the cell is (direct fetch still possible).

Base prefix: http://127.0.0.1:8765 (default api.host + api.port). All REST routes below are under /api/v1 except WS /ws/v1/runs/{run_id} which is at /ws/v1.

System — tools/api/routes/system.py (APIRouter(prefix="/api/v1", tags=["system"])) — app.py:148

MethodRouteHandlerRequestResponseAuthFrontend consumerTests
GET/api/v1/healthhealth (system.py:56)200 {version:"v1", ready:true}nonewebui/src/api/hooks.ts — no auth check neededtests/test_api_auth.py:test_health_no_auth
GET/api/v1/capabilitiescapabilities (system.py:62)200 {api_version:"v1", features:[], constraints:{max_concurrent_runs,loopback_only,manual_tool_calls}, run_options:{modes,kinds,flags}}bearer system._require_authwebui/src/api/hooks.ts:useCapabilitiestests/test_api_auth.py, tests/test_api_webui.py
GET/api/v1/configget_config (system.py:136)200 <redacted config dict>bearerwebui/src/api/hooks.ts:useConfig, SettingsPagetests/test_api_auth.py:test_config_redacts_secrets
PATCH/api/v1/configpatch_config (system.py:190)dict (partial config)200 {status:"ok", config:<sanitized>}bearerwebui/src/api/hooks.ts:useUpdateConfigtests/test_api_frontend.py
GET/api/v1/secretsget_secrets (system.py:205)`200 {keys:{ENV:"configuredmissing"}}`bearerwebui/src/api/hooks.ts:useSecrets
PUT/api/v1/secretsput_secrets (system.py:227){secrets:{name:value}}200 {status:"ok", written:[]}bearerwebui/src/api/hooks.ts:usePutSecretstests/test_api_auth.py:test_secret_write_*
GET/api/v1/modelslist_models (system.py:261)200 {provider, default_alias, registry, info, chatgpt?}bearerwebui/src/api/hooks.ts:useModelstests/test_api_frontend.py
POST/api/v1/modelsadd_model (system.py:284){alias:str, model:str}200 {status:"ok", alias, model, registry}bearer
DELETE/api/v1/models/{alias}remove_model (system.py:302)200 {status:"ok", alias, deleted:true}bearer
POST/api/v1/models/providerset_model_provider (system.py:323)`{provider:"ollamaopencode_gochatgpt"}`200 {status:"ok", provider}bearer
POST/api/v1/models/refreshrefresh_models (system.py)200 {ok, host, available_count, updates:{alias:{old,new}}, registry, persisted} or 503 {ok:false, error} / 400 invalid_providerbearerwebui/src/api/hooks.ts:useSyncModelstests/test_api_models.py:test_refresh_models_*
GET/api/v1/system/infoget_system_info (system.py:338)200 {hostname, platform, os, python, local_ips, public_ip}bearerwebui/src/features/settings/SystemInfo
GET/api/v1/system/telemetryget_telemetry (system.py:380)200 {summary, recent:[50]}bearerwebui/src/routes/StatsPage.tsx
GET/api/v1/system/memoryget_memory (system.py:520)200 {lessons:[], confidence:[], attack_memory:[]}bearerwebui/src/routes/MemoryPage.tsxtests/test_api_memory.py
POST/api/v1/system/resetreset_system (system.py:526)200 {status:"ok", runs_deleted, removed:[], research_cleared}bearerwebui/src/features/settings/SettingsPage.tsxtests/test_api_reset.py
GET/api/v1/pluginslist_plugins (system.py:611)200 {plugins:[]}bearerwebui/src/api/hooks.ts:usePlugins
GET/api/v1/skillslist_skills (system.py:622)200 {skills:[{name,description,tags}]}bearerwebui/src/api/hooks.ts:useSkills
GET/api/v1/skills/searchsearch_skills (system.py:638)?q200 {results:[{name,description}][:20]}bearerwebui/src/api/hooks.ts:useSkillSearch
POST/api/v1/diagnostics/doctorrun_doctor (system.py:664)200 {exit_code, output}bearerwebui/src/features/settings/SettingsPage.tsxtests/test_api_frontend.py:test_doctor_returns_output
POST/api/v1/diagnostics/self-testrun_self_test (system.py:671)200 {exit_code, output}bearerwebui/src/features/settings/SettingsPage.tsxtests/test_api_frontend.py:test_self_test_returns_output
GET/api/v1/attack/moduleslist_attack_modules (system.py:688)200 {modules:[{name,description,family,target_services,target_ports,required_cves,destructive_ics}]}bearerwebui/src/routes/AttackModulesPage.tsx
GET/api/v1/goalslist_goals (system.py:713)200 {goals:[{name,description,risk,compatible}]}bearerwebui/src/routes/GoalsPage.tsxtests/test_api_frontend.py:test_goals_list
GET/api/v1/config/schemaget_config_schema (system.py:743)200 {schema:CONFIG_SCHEMA}bearerwebui/src/api/hooks.ts:useConfigSchematests/test_api_frontend.py:test_config_schema
GET/api/v1/models/livelist_live_models (system.py:753)`200 {models:[], source:"ollamachatgpt"}or503 {models, source:"registry", error}`bearerwebui/src/api/hooks.ts:useLiveModels
GET/api/v1/providersget_providers (system.py:857)200 {provider, chatgpt:{enabled,authenticated,proxy_running,host,port,default_model,we_started}}bearerwebui/src/features/settings/SettingsPage.tsx
POST/api/v1/providers/chatgpt/loginchatgpt_login (system.py:881)200 {ok, url?, reason?}bearerwebui/src/features/settings/SettingsPage.tsx
POST/api/v1/providers/chatgpt/proxy/startchatgpt_proxy_start (system.py:895)200 {ok, base_url?, reason?}bearerwebui/src/features/settings/SettingsPage.tsx
POST/api/v1/providers/chatgpt/proxy/stopchatgpt_proxy_stop (system.py:903)200 {ok, stopped}bearerwebui/src/features/settings/SettingsPage.tsx
GET/api/v1/skills/{name}get_skill (system.py:922)200 {name,description,body,sections,tags,references,nist_csf,mitre_attack,domain,subdomain,version} 404bearerwebui/src/features/skills/SkillDetailtests/test_api_frontend.py:test_skill_detail_not_found
POST/api/v1/skillsinstall_skill (system.py:1023){name, markdown}201 {name,description,tags} `400409`bearer
DELETE/api/v1/skills/{name}remove_skill (system.py:1101)200 {name,deleted:true} `400404`bearer

Runs — tools/api/routes/runs.py (APIRouter(prefix="/api/v1", tags=["runs"])) — app.py:149

MethodRouteHandlerRequestResponseAuthFrontend consumerTests
POST/api/v1/runscreate_run (runs.py:184)RunCreateRequest201 {run_id, preview, state, decision?} 409 conflictbearerwebui/src/features/run-create/RunWizard.tsxtests/test_api_runs.py:test_create_run_returns_preview
GET/api/v1/runslist_runs (runs.py:226)?limit(1..200)&offset&sort(created_desc..)&q&state200 {runs:[{id,state,created_at,target,mode,goal_name,target_ip,model_alias,title}], sort, total}bearerwebui/src/api/hooks.ts:useRunstests/test_api_runs.py:test_list_runs, tests/test_api_frontend.py:test_list_runs_includes_target_and_mode
GET/api/v1/runs/{run_id}get_run (runs.py:265)200 {id,state,created_at,updated_at,request,preview,result,error,title,cancelled_at,resumed_from,decisions} 404bearerwebui/src/api/hooks.ts:useRuntests/test_api_runs.py
POST/api/v1/runs/{run_id}/cancelcancel_run (runs.py:290)200 {run_id, state:"cancelled"} `404504`bearerwebui/src/api/hooks.ts:useCancelRun
POST/api/v1/runs/{run_id}/resumeresume_run (runs.py:297)200 {run_id,resumed_from,preview:{run_id,target_ip}} `404409`bearerwebui/src/routes/RunPage.tsx
POST/api/v1/runs/{run_id}/titleset_run_title (runs.py:319)TitleRequest{title?,regen}200 {run_id,title,regenerated} 404bearerwebui/src/api/hooks.ts:useUpdateRunTitle
GET/api/v1/runs/{run_id}/toolsget_tools (runs.py:358)200 {tools:[schemas]}bearerwebui/src/api/hooks.ts:useRunTools
POST/api/v1/runs/{run_id}/tools/{tool_name}/callscall_tool (runs.py:365)ToolCallRequest{arguments:dict}200 {tool,result} `400403404409
GET/api/v1/runs/{run_id}/artifactslist_artifacts (runs.py:376)200 {artifacts:[{name,bytes,exists}]}bearerwebui/src/api/hooks.ts:useArtifactstests/test_api_frontend.py:test_list_artifacts_*
GET/api/v1/runs/{run_id}/artifacts/{name:path}get_artifact (runs.py:401)200 <bytes contentType> 404bearerwebui/src/routes/ArtifactsPage.tsxtests/test_api_frontend.py:test_get_artifact*
GET/api/v1/runs/{run_id}/workspacelist_workspace (runs.py:432)200 {files:[{path,bytes}]}bearerwebui/src/api/hooks.ts:useWorkspace
GET/api/v1/runs/{run_id}/workspace/{path:path}get_workspace_file (runs.py:447)200 <bytes> 404bearer
GET/api/v1/runs/{run_id}/auditget_audit (runs.py:465)200 {records, chain_valid, chain_reason}bearerwebui/src/api/hooks.ts:useAudittests/test_api_frontend.py:test_audit_*
GET/api/v1/runs/{run_id}/witnessget_witness_flags (runs.py:513)200 {flags:[]} 404bearerwebui/src/features/witness/WitnessPanel.tsx
GET/api/v1/runs/{run_id}/swarmget_swarm_state (runs.py:544)200 {state:json} 404bearerwebui/src/api/hooks.ts:useSwarmStatetests/test_api_frontend.py:test_swarm_state_*
GET/api/v1/runs/{run_id}/campaignget_campaign_state (runs.py:551)200 {state:json} 404bearerwebui/src/api/hooks.ts:useCampaignStatetests/test_api_frontend.py:test_campaign_state_*
GET/api/v1/runs/{run_id}/logs/{name}get_log (runs.py:563)?tail(1..2000)&attempt_id&target_ip200 {name,lines,total_lines_returned,total_lines_in_file} `400404`bearerwebui/src/api/hooks.ts:useLogs
GET/api/v1/runs/{run_id}/credentialslist_credentials (runs.py:640)200 {credentials:[{username,target_host,password:"[REDACTED]",index}]}bearerwebui/src/routes/LootPage.tsxtests/test_api_frontend.py:test_credentials_redacted
POST/api/v1/runs/{run_id}/credentials/{index}/revealreveal_credential (runs.py:667)200 {index,username,target_host,password} `404500auditscredential_access.jsonl`bearerwebui/src/routes/LootPage.tsx
POST/api/v1/runs/{run_id}/credentials/{index}/confirmconfirm_credential (runs.py:712)200 {index,username,target_host,confirmed} `404500`bearer
GET/api/v1/runs/{run_id}/lootlist_loot (runs.py:770)200 {loot:[to_json]}bearerwebui/src/routes/LootPage.tsxtests/test_api_frontend.py:test_loot_*
DELETE/api/v1/runs/{run_id}delete_run (runs.py:796)?purge=true200 {run_id,deleted:true,purged:bool} `404409`bearerwebui/src/api/hooks.ts:useDeleteRun

Decisions — tools/api/routes/decisions.py (APIRouter(prefix="/api/v1", tags=["decisions"])) — app.py:150

MethodRouteHandlerRequestResponseAuthFrontend consumerTests
GET/api/v1/runs/{run_id}/decisionslist_decisions (decisions.py:56)200 {decisions:[{id,run_id,kind,prompt_text,required_text,options_json,status,answer,created_at,answered_at}]} 404bearerwebui/src/api/hooks.ts:useDecisionstests/test_api_frontend.py:test_get_single_decision (via single)
GET/api/v1/runs/{run_id}/decisions/{decision_id}get_decision (decisions.py:63)200 DecisionOut 404bearerwebui/src/api/hooks.ts:useDecisiontests/test_api_frontend.py:test_get_single_decision
POST/api/v1/runs/{run_id}/decisions/{decision_id}answer_decision (decisions.py:91){answer:str}200 {decision_id,status:"answered"} `400404409`bearer

Events — tools/api/routes/events.py (APIRouter(prefix="/api/v1", tags=["events"])) — app.py:151

MethodRouteHandlerRequestResponseAuthFrontend consumerTests
GET/api/v1/runs/{run_id}/eventsget_events (events.py:66)?after≥0&tail1..1000&before&limit1..1000200 {run_id,events,oldest_sequence,latest_sequence,has_more_before,first_returned_sequence,last_returned_sequence,omitted_before,next_before} `404503`bearerwebui/src/api/ws.ts:seedEvents + EventViewer
GET/api/v1/runs/{run_id}/events/streamstream_events (events.py:99)?after≥0200 StreamingResponse text/event-stream data: {json}\n\n headers no-cache,keep-alive,X-Accel-Buffering:nobearer header (never query)webui/src/api/sse.ts fallback
WS/ws/v1/runs/{run_id}ws_run_events (events.py:146)first JSON {"auth":"<token>","after":int≥0} 5 sstreams EventOut JSON, heartbeats every 30 sfirst-message bearer + Origin loopback (tools/api/auth.py:128)webui/src/api/ws.ts:useRunEvents

Graph legacy — tools/api/routes/graph.py (APIRouter(prefix="/api/v1", tags=["graph"])) — app.py:152

MethodRouteHandlerRequestResponseAuthFrontend consumerTests
GET/api/v1/runs/{run_id}/graphget_run_graph (graph.py:167)200 {run_id,nodes:[{id,type,label,...}],edges:[{source,target,relation}]} 404 run not found / 404 graph_disabled when api.graph_route:falsebearerwebui/src/routes/GraphPage.tsx (legacy)

Graph explorer — tools/api/routes/graph_explorer.py (APIRouter(prefix="/api/v1/graph", tags=["graph-explorer"])) — app.py:153 — Detail: Graph Explorer Endpoints

All below additionally gated api.graph_route404 graph_disabled (graph_explorer.py:61); unknown node/run 404; invalid enum filter silently ignored.

MethodRouteHandlerRequestResponseAuthFrontend consumerTests
GET/api/v1/graph/runs/{run_id}get_graph (graph_explorer.py:86)?node_type=[...]&status=[...]&q&limit1..500 default300200 {run_id,scope,nodes:[to_dict],edges:[to_dict],total_nodes,truncated}bearerwebui/src/features/graph/AttackGraphPage.tsx
GET/api/v1/graph/runs/{run_id}/summaryget_summary (graph_explorer.py:102)200 {run_id,summary:{nodes,edges,total_nodes,total_edges},stats:{hosts,domains,ips,services,findings,hypotheses,evidence,observations,vulnerability_candidates,confirmed,likely,refuted,highest_degree_node,conflict_count}}bearersame
GET/api/v1/graph/runs/{run_id}/conflictsget_conflicts (graph_explorer.py:111)200 {run_id,conflicts:[{node_value,reason,existing_confidence,proposed_confidence,node_id,scope,built_at}]}bearersame
GET/api/v1/graph/runs/{run_id}/nodes/{node_id}get_node (graph_explorer.py:120)200 {run_id,node,edges,neighbors} 404 node_not_foundbearersame
GET/api/v1/graph/runs/{run_id}/nodes/{node_id}/neighborsget_neighbors (graph_explorer.py:133)?max_hops1..4 default1&max_nodes1..200 default50200 {run_id,start_node,nodes,edges} 404bearersame
GET/api/v1/graph/runs/{run_id}/pathsget_paths (graph_explorer.py:154)?start&end&max_length1..8 default4&max_paths1..8 default5200 {run_id,paths:[[[{distance,node,edge}]]]} unknown endpoints []bearersame

Benchmarks — tools/api/routes/benchmarks.py (APIRouter(prefix="/api/v1/benchmarks", tags=["benchmarks"])) — app.py (wired unconditionally; backed by tools/benchmark/) — Detail: Benchmarks Endpoints

MethodRouteHandlerRequestResponseAuthFrontend consumerTests
GET/api/v1/benchmarksbenchmarks_overview (benchmarks.py:94)200 {suites:[SuiteInfo],runs:[RunIndexRow],active:{run_id,state,error},baseline:BaselineMeta}bearerwebui/src/features/benchmarks/api.ts:fetchOverviewtests/test_benchmark_api.py
GET/api/v1/benchmarks/suiteslist_suites_route (benchmarks.py:107)200 {suites:[SuiteInfo]}bearersame fetchSuitessame
GET/api/v1/benchmarks/suites/{suite_id}/scenarioslist_scenarios_route (benchmarks.py:120)200 {suite,scenarios:[ScenarioInfo]} 404 unknown suitebearersame fetchSuiteScenariossame
GET/api/v1/benchmarks/suites/{suite_id}/readinesssuite_readiness_route (benchmarks.py)200 {suite,ready,lab_command,targets:[{scenario_id,target_type,target_host,target_ports,reachable,self_provisioned,detail}]} 404 unknown suitebearersame fetchSuiteReadiness (RunBenchmarkPanel lab warning)tests/test_benchmark_api.py
GET/api/v1/benchmarks/runslist_runs (benchmarks.py:137)?suite&limit1..200 default50200 {runs:[RunIndexRow]}bearersame fetchRunssame
GET/api/v1/benchmarks/runs/{run_id}get_run (benchmarks.py:145)200 RunDetail(+summary) 404 run not foundbearersame fetchRunsame
GET/api/v1/benchmarks/runs/{run_id}/scenariosget_run_scenarios (benchmarks.py:152)200 {run_id,scenarios:[Trial]}bearersame fetchRunScenariossame
GET/api/v1/benchmarks/runs/{run_id}/eventsget_run_events (benchmarks.py:170)?after≥0&trial_id&limit1..5000200 {run_id,events:[BenchmarkEvent],latest_sequence}bearersame fetchRunEventssame
GET/api/v1/benchmarks/runs/{run_id}/events/streamstream_run_events (benchmarks.py:184)?after≥0SSE data: {event} heartbeats, closes after ~60 s idlebearer— (polling used)same
POST/api/v1/benchmarks/runstart_run (benchmarks.py:222)BenchmarkRunRequest{suite,scenarios?,tags?,trials?,model?,reasoning?,sandbox_required?,timeout_seconds?,save_baseline?,check_regression?}200 {run_id,state} 409 conflict/invalidbearersame startBenchmarkRun (RunBenchmarkPanel)same
POST/api/v1/benchmarks/runs/{run_id}/cancelcancel_run (benchmarks.py:230)200 {run_id,cancelled} 404 not activebearersame cancelBenchmarkRunsame
GET/api/v1/benchmarks/baselineget_baseline (benchmarks.py:254)200 BaselineMetabearersame fetchBaselinesame
POST/api/v1/benchmarks/baselinesave_baseline_route (benchmarks.py:259){run_id}200 {saved,path,run_id} 404/409bearersame saveBaselinesame
GET/api/v1/benchmarks/comparecompare_runs (benchmarks.py:276)?run_a&run_b200 RunComparison{run_a,run_b,comparison:{metrics,scenarios,categories}} 404/409bearersame compareRuns (ComparisonView)same

Ops — tools/api/routes/ops.py (APIRouter(prefix="/api/v1/ops", tags=["ops"])) — app.py (wired unconditionally; read-only rollup) — Detail: Ops Endpoints

MethodRouteHandlerRequestResponseAuthFrontend consumerTests
GET/api/v1/ops/summaryops_summary (ops.py)200 {killchain:{enabled,goal_state,require_verification},snapshots:{enabled,provider,counterfactual},eval:{enabled,baseline_path,baseline_exists},browser:{enabled,backend},provider:{active}}bearerwebui/src/routes/OpsPage.tsxtests/test_ops_summary.py

Users/annotations — tools/api/routes/users.py (APIRouter(prefix="/api/v1", tags=["users"])) — app.py:154 only when api.multi_operator:true — Detail: Users Endpoints

MethodRouteHandlerRequestResponseAuthFrontend consumerTests
POST/api/v1/userscreate_user (users.py:98){username(1..64),password(1..256)}201 {id,username,created_at,last_login} 409 duplicatebearerwebui/src/features/auth/MultiOperator.tsx
POST/api/v1/users/loginlogin (users.py:116){username,password}200 {id,username,created_at,last_login} 401bearersame
GET/api/v1/userslist_users (users.py:134)200 [{id,username,created_at,last_login}] (no hashes)bearersame
POST/api/v1/runs/{run_id}/annotationsadd_annotation (users.py:149){body(1..4096),finding_ref≤256,user_id,username}201 {id,run_id,user_id,username,body,finding_ref,created_at} 404 run/userbearersame
GET/api/v1/runs/{run_id}/annotationslist_annotations (users.py:176)200 [{...Annotation}] 404bearersame
DELETE/api/v1/annotations/{annotation_id}delete_annotation (users.py:184)204 404bearersame

Non-API mounts

MethodRouteHandlerNotes
GET/docs, /openapi.json, /redocFastAPI built-insNot bearer-protected; filtered to hide webui routes when api.serve_webui (app.py:214)
GET/assets/* + /{full_path:path} SPA fallbackStaticFiles + FileResponse (app.py:172 /_webui_spa)only when api.serve_webui:true and webui/dist/index.html exists

Coverage Notes

  • Every @router.* decorator above was extracted via Select-String -Pattern "@router\.(get|post|put|patch|delete|websocket)" over the six route modules; no manual list was invented. mcp_exploit_server.py is not part of this daemon (separate MCP stdio server).
  • graph_explorer paths are the six APIRouter(prefix="/api/v1/graph") routes verified above — not conflated with legacy GET /api/v1/runs/{run_id}/graph.
  • users routes are conditional; CI exercises them only when api.multi_operator:true so absent-from-schema is expected in default tests.
source: repo docs (build sync)Edit this page on GitHub →