Skip to content

Hall API Reference

The Hall API is an HTTP interface to the Hall registry and routing decision log. Each organization runs their own local instance. It is the backing service for Hall Monitor.

Start the Server

Terminal window
pip install pyhall-wcp flask
python -m hall_api.server
# [hall-api] PyHall API v0.3.0 starting on http://127.0.0.1:8765

Environment Variables

VariableDefaultDescription
HALL_API_PORT8765Port to listen on.
HALL_API_HOST127.0.0.1Host to bind.
HALL_DB_PATHhall.dbPath to the SQLite database.

CORS is configured to allow any origin. Restrict to your own domain in production.


Endpoints

GET /health

Health check. Returns 200 ok immediately with no database access.

Response 200:

{
"status": "ok",
"timestamp": "2026-02-28T12:00:00.000000+00:00",
"hall_hash": "sha256:...",
"hall_hash_banned": false
}

curl:

Terminal window
curl http://localhost:8765/health

GET /status

Hall status summary with enrollment and decision counts.

Response 200:

{
"hall_version": "0.3.0",
"hall_name": "PyHall",
"workers_enrolled": 3,
"decisions_total": 142,
"decisions_today": 17,
"denials_today": 2,
"timestamp": "2026-02-28T12:00:00.000000+00:00",
"online": true,
"account_standing": "ok",
"standing_checked_at": "2024-01-01T00:00:00Z"
}
FieldTypeDescription
hall_versionstringPyHall server version.
hall_namestringAlways "PyHall".
workers_enrolledintTotal workers enrolled in the registry.
decisions_totalintTotal routing decisions recorded.
decisions_todayintRouting decisions made today (UTC).
denials_todayintDenied decisions today (UTC).
timestampstringISO 8601 UTC timestamp.
onlinebooltrue when the Hall is serving requests.
account_standingstringAccount standing from the pyhall.dev registry ("ok", "degraded", etc.).
standing_checked_atstringISO 8601 UTC timestamp of the last standing check.

curl:

Terminal window
curl http://localhost:8765/status

GET /workers

List all enrolled workers.

Response 200:

{
"workers": [
{
"worker_id": "org.example.my-summarizer",
"worker_species_id": "wrk.doc.summarizer",
"capabilities": ["cap.doc.summarize"],
"risk_tier": "low",
"enrolled_at": "2026-02-28T10:00:00.000000+00:00",
"status": "active"
}
],
"count": 1
}
FieldTypeDescription
workersarrayList of enrolled worker objects.
workers[].worker_idstringUnique worker instance identifier.
workers[].worker_species_idstringWCP worker species ID.
workers[].capabilitiesarray[string]Capability IDs this worker handles.
workers[].risk_tierstringRisk tier declared at enrollment.
workers[].enrolled_atstringISO 8601 UTC enrollment timestamp.
workers[].statusstringAlways "active" in v0.1.
countintTotal workers returned.

curl:

Terminal window
curl http://localhost:8765/workers

GET /dispatches

Recent routing decisions, newest first.

Query parameters:

ParameterTypeDefaultMaxDescription
limitint50200Number of decisions to return.

Response 200:

{
"dispatches": [
{
"decision_id": "550e8400-e29b-41d4-a716-446655440000",
"decided_at": "2026-02-28T12:00:00.000000+00:00",
"capability_id": "cap.doc.summarize",
"tenant_id": "acme-corp",
"env": "dev",
"denied": 0,
"deny_reason": null,
"selected_worker": "wrk.doc.summarizer",
"blast_score": 2,
"artifact_hash": "sha256:abc123..."
}
],
"count": 1
}
FieldTypeDescription
decision_idstringUUID v4 decision identifier.
decided_atstringISO 8601 UTC timestamp.
capability_idstringCapability that was requested.
tenant_idstringRequesting tenant.
envstringEnvironment (dev, stage, prod, edge).
deniedint0 = allowed, 1 = denied.
deny_reasonstring|nullDenial reason string, or null.
selected_workerstring|nullDispatched worker species, or null.
blast_scoreint|nullComputed blast score (0–100).
artifact_hashstring|nullSHA-256 of the routing artifact.

curl:

Terminal window
curl "http://localhost:8765/dispatches?limit=20"

GET /dispatches/active

Currently in-flight dispatches. In v0.1, this is a stub that always returns an empty list.

Response 200:

{
"dispatches": [],
"count": 0
}

curl:

Terminal window
curl http://localhost:8765/dispatches/active

GET /alerts

Recent governance alerts, newest first. Returns up to 50 alerts.

Response 200:

{
"alerts": [
{
"alert_id": "550e8400-e29b-41d4-a716-446655440001",
"created_at": "2026-02-28T12:00:00.000000+00:00",
"level": "warning",
"code": "DENY_WORKER_TAMPERED",
"message": "Worker code hash mismatch detected for wrk.doc.summarizer",
"worker_id": "org.example.my-summarizer",
"capability_id": "cap.doc.summarize"
}
],
"count": 1
}
FieldTypeDescription
alert_idstringUUID v4 alert identifier.
created_atstringISO 8601 UTC timestamp.
levelstringAlert severity level.
codestringAlert code identifier.
messagestringHuman-readable alert message.
worker_idstring|nullAffected worker, if applicable.
capability_idstring|nullAffected capability, if applicable.

curl:

Terminal window
curl http://localhost:8765/alerts

POST /enroll

Enroll a worker in the registry. If the worker is already enrolled (same worker_id), the record is updated.

Request body:

{
"worker_id": "org.example.my-summarizer",
"worker_species_id": "wrk.doc.summarizer",
"capabilities": ["cap.doc.summarize"],
"risk_tier": "low",
"currently_implements": ["ctrl.obs.audit-log-append-only"],
"artifact_hash": "sha256:abc123..."
}
FieldTypeRequiredDescription
worker_idstringyesUnique worker instance identifier. Must not be empty.
worker_species_idstringyesWCP worker species ID. Must not be empty.
capabilitiesarray[string]yesNon-empty list of capability IDs.
artifact_hashstringyessha256: + hex digest of the record (without this field, sorted keys).
risk_tierstringnoRisk tier. Stored as-is.
currently_implementsarray[string]noControl IDs the worker implements.
(other fields)anynoAny additional fields are stored in the full record.

Computing artifact_hash:

import hashlib, json
record = {
"worker_id": "org.example.my-summarizer",
"worker_species_id": "wrk.doc.summarizer",
"capabilities": ["cap.doc.summarize"],
"risk_tier": "low",
}
payload = json.dumps(record, sort_keys=True, separators=(",", ":")).encode()
artifact_hash = "sha256:" + hashlib.sha256(payload).hexdigest()

Response 201 (new enrollment):

{
"enrolled": true,
"enrollment_id": "550e8400-e29b-41d4-a716-446655440002",
"worker_id": "org.example.my-summarizer",
"enrolled_at": "2026-02-28T12:00:00.000000+00:00",
"artifact_hash_verified": true
}

Response 200 (update — worker already enrolled):

{
"enrolled": true,
"updated": true,
"worker_id": "org.example.my-summarizer",
"artifact_hash_verified": true
}

Response 400 errors:

CodeCondition
{"error": "worker_id is required"}worker_id is empty or missing.
{"error": "worker_species_id is required"}worker_species_id is empty or missing.
{"error": "capabilities must be a non-empty list"}capabilities is empty or missing.
{"error": "artifact_hash is required..."}artifact_hash is missing.
{"error": "artifact_hash mismatch", "code": "DENY_WORKER_TAMPERED"}Hash does not match computed value.

curl:

Terminal window
curl -X POST http://localhost:8765/enroll \
-H "Content-Type: application/json" \
-d '{
"worker_id": "org.example.my-summarizer",
"worker_species_id": "wrk.doc.summarizer",
"capabilities": ["cap.doc.summarize"],
"risk_tier": "low",
"artifact_hash": "sha256:YOUR_HASH_HERE"
}'

POST /decisions/ingest

Record a routing decision produced by the PyHall router. Used to push decisions from your routing code into the Hall API for display in Hall Monitor.

Request body:

{
"decision_id": "550e8400-e29b-41d4-a716-446655440000",
"decided_at": "2026-02-28T12:00:00.000000+00:00",
"capability_id": "cap.doc.summarize",
"tenant_id": "acme-corp",
"env": "dev",
"denied": false,
"deny_reason": null,
"selected_worker": "wrk.doc.summarizer",
"blast_score": 2,
"artifact_hash": "sha256:abc123..."
}
FieldTypeRequiredDescription
decision_idstringnoUUID v4. Generated server-side if omitted.
decided_atstringnoISO 8601 timestamp. Defaults to server time.
capability_idstringnoCapability that was requested.
tenant_idstringnoRequesting tenant.
envstringnoEnvironment. Defaults to "dev".
deniedboolnoWhether the request was denied.
deny_reasonstring|nullnoDenial reason if denied.
selected_workerstring|nullnoDispatched worker species.
blast_scoreint|nullnoBlast score (0–100).
artifact_hashstring|nullnoSHA-256 of the routing artifact.

Duplicate decision_id values are silently ignored (INSERT OR IGNORE).

Response 201:

{
"recorded": true,
"decision_id": "550e8400-e29b-41d4-a716-446655440000"
}

Response 500:

{
"error": "database error message"
}

curl:

Terminal window
curl -X POST http://localhost:8765/decisions/ingest \
-H "Content-Type: application/json" \
-d '{
"capability_id": "cap.doc.summarize",
"tenant_id": "acme-corp",
"env": "dev",
"denied": false,
"selected_worker": "wrk.doc.summarizer"
}'

POST /api/route

Allows any language to submit a routing decision to Hall Server over HTTP. Returns the governance decision with selected worker species, denial reasons, and routing metadata.

Auth: Authorization: Bearer <token> header or pyhall_session cookie required.

Request body:

{
"capability_id": "cap.doc.summarize",
"env": "dev",
"data_label": "PUBLIC",
"tenant_id": "org.acme"
}
FieldTypeRequiredDescription
capability_idstringyesCapability identifier (e.g. cap.doc.summarize)
envstringyesEnvironment: dev, staging, or prod
data_labelstringyesData classification: PUBLIC, INTERNAL, or CONFIDENTIAL
tenant_idstringyesTenant or org identifier for routing scope
tenant_riskstringnoTenant risk level. Defaults to "low".
qos_classstringnoQoS class for priority routing. Defaults to "P2".
correlation_idstringnoCaller-supplied trace ID. Auto-generated (UUID v4) if omitted.
_rulesobjectnoCustom routing rules document. If omitted, rules are auto-generated from enrolled workers.

Response 200:

Returns a full RouteDecision object as JSON.

{
"decision_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-02-28T12:00:00Z",
"correlation_id": "abc123",
"tenant_id": "org.acme",
"capability_id": "cap.doc.summarize",
"matched_rule_id": "auto.cap.doc.summarize.wrk.doc.summarizer",
"env": "dev",
"data_label": "PUBLIC",
"denied": false,
"deny_reason_if_denied": null,
"selected_worker_species_id": "wrk.doc.summarizer",
"artifact_hash": "sha256:..."
}
FieldTypeDescription
decision_idstringUUID v4 for this routing decision
timestampstringISO 8601 UTC timestamp of the decision
correlation_idstringPropagated from request (or auto-generated)
capability_idstringThe capability that was requested
deniedbooleanfalse = routed; true = blocked by policy
deny_reason_if_deniedobject | nullDenial detail if denied; null when allowed
selected_worker_species_idstring | nullAssigned worker species ID; null if denied
artifact_hashstring | nullSHA-256 of the serialized RouteInput

Response 400: Missing required field. Body: {"error": "missing field: <field_name>"}.

Response 401: Missing or invalid auth token or session cookie.

Response 500: Internal routing error. Body: {"error": "<message>"}.

curl:

Terminal window
curl -X POST http://localhost:8765/api/route \
-H "Authorization: Bearer <session_token>" \
-H "Content-Type: application/json" \
-d '{
"capability_id": "cap.doc.summarize",
"env": "dev",
"data_label": "PUBLIC",
"tenant_id": "org.acme"
}'

See Integrations for language-specific examples (LangGraph, CrewAI, AutoGen, raw HTTP).


GET /openapi.json

Returns the full OpenAPI 3.0 spec for all Hall Server endpoints. Use this to auto-generate clients or validate route schemas.

Auth: No auth required (read-only spec).

Response 200:

{
"openapi": "3.0.0",
"info": {
"title": "Hall Server API",
"version": "0.3.0",
"description": "PyHall Hall Server — local API for Hall Monitor and orchestrator integrations"
},
"paths": {
"/api/route": { "...": "..." },
"/health": { "...": "..." }
}
}

curl:

Terminal window
curl http://localhost:8765/openapi.json


Database Schema

The server uses SQLite at HALL_DB_PATH (default: hall.db). Three tables are created automatically on startup.

-- Enrolled workers
CREATE TABLE enrollments (
enrollment_id TEXT PRIMARY KEY,
worker_id TEXT NOT NULL UNIQUE,
worker_species_id TEXT NOT NULL,
capabilities TEXT NOT NULL, -- JSON array
risk_tier TEXT NOT NULL,
enrolled_at TEXT NOT NULL,
artifact_hash TEXT NOT NULL,
record_json TEXT NOT NULL -- full record JSON
);
-- Routing decisions
CREATE TABLE decisions (
decision_id TEXT PRIMARY KEY,
decided_at TEXT NOT NULL,
capability_id TEXT NOT NULL,
tenant_id TEXT NOT NULL DEFAULT '',
env TEXT NOT NULL DEFAULT 'dev',
denied INTEGER NOT NULL DEFAULT 0,
deny_reason TEXT,
selected_worker TEXT,
blast_score INTEGER,
artifact_hash TEXT
);
-- Governance alerts
CREATE TABLE alerts (
alert_id TEXT PRIMARY KEY,
created_at TEXT NOT NULL,
level TEXT NOT NULL,
code TEXT NOT NULL,
message TEXT NOT NULL,
worker_id TEXT,
capability_id TEXT
);