Documentation
Horkos is an AI agent governance layer. Every agent action passes through it: policies are evaluated, high-risk calls are routed to human approval, and everything lands in an immutable audit trail.
Introduction
What Horkos does
You wrap your AI agent code with the Horkos SDK (or call the API directly). Before each action runs, Horkos checks it against your organization's policies. The action then takes one of three paths:
- Allowed → the action is marked
executingand your code continues. - Blocked → the policy engine detected a violation (e.g. SQL injection, prompt injection). The action never runs.
- Awaiting approval → the action's risk crosses the agent's approval threshold. A human approver is notified and must approve or deny before execution.
Every transition is written to an append-only audit trail your auditor or regulator can read.
Core concepts
| Concept | What it is |
|---|---|
Organization | Your tenant. Holds API keys and owns everything below. |
Agent | A registered AI agent with a name, risk level, and an optional approval threshold. |
Session | A grouped run of actions, scoped to an agent and a user. Linked together in the audit trail. |
Action | A single intent submitted for governance — e.g. send_email, wire_transfer, db_query. |
Policy | A rule the policy engine evaluates against each action. Built-in patterns block SQL and prompt injection. |
Approval | A pending human decision on a high-risk action. Recorded with reason and approver. |
Audit log | An immutable event in the trail. Never updated, never deleted. |
Risk levels
Risk is one of low, medium, high, critical. Each agent has a require_approval_above threshold — actions at or above that level need human approval before they execute.
Quickstart
Five steps with curl: create an organization, register an agent, open a session, submit an action, and read the audit trail. The API lives at https://api.horkos.eu.
1. Create an organization and get an API key
The only unauthenticated endpoint (besides /health). The API key is returned once — store it immediately.
curl -X POST https://api.horkos.eu/v1/orgs \
-H "Content-Type: application/json" \
-d '{
"name": "Acme AI",
"slug": "acme-ai",
"email": "founder@acme.example"
}'
Response:
{
"org_id": "f3c7…",
"api_key": "hks_live_…",
"message": "Save your API key — it will not be shown again."
}
2. Register an agent
Set require_approval_above so high-risk actions are routed to approval instead of executing straight away.
curl -X POST https://api.horkos.eu/v1/agents \
-H "Authorization: Bearer hks_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "payments-agent",
"risk_level": "high",
"require_approval_above": "high",
"owner": "ops@acme.example"
}'
3. Open a session
You generate the session_id client-side (any UUID). The agent is resolved by name.
curl -X POST https://api.horkos.eu/v1/sessions \
-H "Authorization: Bearer hks_live_…" \
-H "Content-Type: application/json" \
-d '{
"session_id": "01HX…",
"agent_name": "payments-agent",
"user_id": "user_42"
}'
4. Submit an action
A low-risk action returns executing; a SQL/prompt-injection payload returns blocked; a high-risk action returns awaiting_approval.
curl -X POST https://api.horkos.eu/v1/actions \
-H "Authorization: Bearer hks_live_…" \
-H "Content-Type: application/json" \
-d '{
"action_id": "act_01HX…",
"agent_id": "ag_…",
"session_id": "01HX…",
"action_type": "wire_transfer",
"input_data": { "amount": 250000, "to_account": "IBAN-…" },
"risk_level": "high",
"user_id": "user_42"
}'
Response (high-risk path):
{
"action_id": "act_01HX…",
"status": "awaiting_approval",
"approval_status": "pending",
"message": "Approval required from: ops@acme.example"
}
5. Read the audit trail
curlcurl "https://api.horkos.eu/v1/audit?since_hours=1&limit=50" \
-H "Authorization: Bearer hks_live_…"
You will see action.executing, action.blocked, and approval.requested events with the relevant action_id embedded in details.
gateway/scripts/smoke_test_live.py in the repo. Read it as a working reference; it is what we run against production after every deploy.
Python SDK
The SDK wraps the same API in two ergonomic patterns: a decorator on your agent function, or a context manager for fine-grained control.
Install
shellpip install horkos-sdk
Configure
The simplest setup is zero-config — set HORKOS_API_KEY and the SDK uses https://api.horkos.eu by default.
export HORKOS_API_KEY="hks_live_…"
python
from horkos import Horkos
horkos = Horkos() # reads HORKOS_API_KEY, hits api.horkos.eu
Or pass the key and (optionally) a self-hosted gateway URL explicitly:
pythonhorkos = Horkos(
api_key="hks_live_…",
base_url="https://api.horkos.eu", # optional; this is the default
fail_open=False, # raise on gateway errors instead of masking
timeout=30,
)
fail_open=True (the default) lets your code keep running if the gateway is unreachable; the action is logged as failed with horkos_unavailable in policy_violations. For regulated environments use fail_open=False so a gateway outage surfaces as an exception.
Pattern 1 — decorator
Wrap an async function. Each call opens a session, submits one action of type <function name>, runs the function, and ends the session.
from horkos import Horkos, RiskLevel
horkos = Horkos()
@horkos.agent(name="email-agent", risk_level=RiskLevel.MEDIUM)
async def send_email(to: str, subject: str, body: str) -> dict:
# your real email-sending code
return {"sent_to": to}
await send_email("user@example.com", "hello", "...")
Pattern 2 — context manager
Use when you want explicit control over each execute call, the risk level, or whether to wait for approval.
from horkos import Horkos, RiskLevel
from horkos.exceptions import PolicyViolationError, ApprovalDeniedError
horkos = Horkos()
async with horkos.session("payments-agent", user_id="user_42") as s:
try:
result = await s.execute(
action_type="wire_transfer",
input_data={"amount": 250000, "to_account": "IBAN-…"},
risk_level=RiskLevel.HIGH,
wait_for_approval=True, # poll until approved/denied/timeout
approval_timeout_seconds=300,
)
# result.status is ActionStatus.EXECUTING / COMPLETED / …
# result.action_id, result.policy_violations, result.duration_ms
except PolicyViolationError as e:
# Blocked by the policy engine. e.violations is a list of reasons.
...
except ApprovalDeniedError as e:
# A human explicitly denied this action.
...
Register agents explicitly
For long-running services you usually pre-register your agents at startup instead of relying on the decorator to do it lazily:
pythonfrom horkos import Horkos, AgentConfig, RiskLevel
horkos = Horkos()
agent_id = await horkos.register_agent(AgentConfig(
name="payments-agent",
description="Handles wire transfers and invoice payments",
risk_level=RiskLevel.HIGH,
require_approval_above=RiskLevel.HIGH,
owner="ops@acme.example",
max_actions_per_minute=60,
))
Read the audit trail and generate reports
pythonentries = await horkos.get_audit_trail(limit=100, since_hours=24)
report = await horkos.generate_compliance_report(report_type="eu_ai_act", days=30)
print(report["summary"]["compliance_score"])
Exceptions
| Exception | Raised when |
|---|---|
PolicyViolationError | The policy engine blocked the action. e.violations lists why. |
ApprovalRequiredError | Approval is required and wait_for_approval=False. |
ApprovalDeniedError | A human explicitly denied the action. |
RateLimitError | The agent exceeded its configured rate limit. |
AuthenticationError | Missing or invalid API key. |
HorkosException | Base class. With fail_open=False, gateway errors surface as this. |
Governance model
Action lifecycle
Every action travels one of three terminal paths after the policy engine and approval threshold are evaluated:
pending ─┬─→ executing ─→ completed
│
├─→ blocked (policy violation)
│
└─→ awaiting_approval ─┬─→ executing (approved)
└─→ blocked (denied)
The policy engine
The engine runs before the action is persisted. It inspects input_data and the action_type, and returns a verdict that is either allowed, blocked, or requires approval. Built-in detections include:
- SQL injection — payloads like
DROP TABLE,OR 1=1, or stacked-query patterns. - Prompt injection — phrases like
ignore previous instructionsin LLM input. - Risk threshold — if
action.risk_level >= agent.require_approval_above, the action requires approval.
DROP TABLE) are blocked at the edge before they reach the app; subtler ones are caught by the app-level engine. Both paths are auditable.Human approval
When an action requires approval the gateway:
- Creates an
Approvalrow withstatus=pending. - Sends a notification (e.g. Slack webhook) to the configured approvers — the agent's
owner, optionally a manager of the callinguser_id, with a fallback to the org's admin. - Writes
approval.requestedto the audit trail. - The approver calls
POST /v1/approvals/{id}/decidewithapprovedordenied. The action transitions toexecutingorblocked, and the decision is audited asapproval.approvedorapproval.denied.
The audit trail is immutable
Audit rows are write-once. The application has no endpoint to UPDATE or DELETE them, and database migrations preserve them. Every state change relevant to compliance — an action executing, a block, a requested or decided approval — produces an entry. Writes happen as BackgroundTasks after the API response, so under load there can be a small delay (seconds) before an event appears in GET /v1/audit.
Compliance reports
Generate a structured report against a regulatory framework. Reports are computed live from the actions and approvals in the requested window — they are derived, not stored, so they always reflect the current state of the trail.
Available frameworks
| report_type | Framework | Version |
|---|---|---|
eu_ai_act | EU Artificial Intelligence Act | 2024/1689 |
dora | Digital Operational Resilience Act | 2022/2554 |
soc2 | SOC 2 Type II | AICPA 2017 |
Generate one
curlcurl -X POST https://api.horkos.eu/v1/reports \
-H "Authorization: Bearer hks_live_…" \
-H "Content-Type: application/json" \
-d '{ "report_type": "eu_ai_act", "days": 30 }'
Returns a JSON document containing:
summary— totals (actions, blocked, approved-with-oversight, high-risk, agents) and acompliance_score(0–100).findings— material issues detected in the window, each tagged with a severity and the framework article it relates to.recommendations— concrete actions to address the findings.framework— the framework's name, version, and the applicable articles.
The score is derived from the block rate and the proportion of high-risk activity; the formula lives in gateway/routers/reports.py and is intentionally simple so an auditor can verify it.
API reference
Every endpoint except GET /health and POST /v1/orgs requires Authorization: Bearer hks_live_…. The fully interactive reference, generated from the running code, lives at horkos.onrender.com/docs (Swagger UI).
Health
Liveness probe. No auth.
Organizations & API keys
Create an organization and receive its first API key. No auth.
Body: { name, slug, email }. Response: { org_id, api_key, message }.
Create an additional key for the calling org. Query: name.
Agents
Register an agent (idempotent on name within an org).
Body: name, risk_level, optional description, permissions, max_actions_per_minute, require_approval_above, tags, owner.
List all active agents for the org.
Get one agent's full detail.
Deactivate (soft delete). The agent's history is preserved.
Sessions
Start a session. Body: session_id (client-generated UUID), agent_name, optional user_id, metadata. Unknown agent names are auto-registered with safe defaults — logged but never silent.
End an open session.
List sessions. Optional filter: agent_id.
Actions
Submit an action for governance — the critical path. Returns one of executing, blocked, or awaiting_approval.
Body: action_id, agent_id, session_id, action_type, input_data, risk_level, optional user_id, metadata.
Mark an executing action as completed with its output and (optional) cost/duration/tokens.
Poll for the approval status of an action. Used by the SDK while waiting.
List actions with filters: agent_id, session_id, status, risk_level, limit, offset. Returns { actions, total }.
Approvals
List approvals waiting for a human decision in the calling org.
Approve or deny. Body: { decision: "approved" | "denied", decided_by, reason? }. Idempotent: re-deciding an already-decided approval returns 404.
Audit
Query the immutable trail. Filters: agent_id, session_id, event_type, since_hours, limit.
Aggregated counters for the dashboard. Query: hours.
Reports
Generate a compliance report. Body: { report_type, days }.
Roadmap
These are not available today. We list them here so you know what is on the way; nothing in this section can be installed or called yet.
@horkos.agent decorator equivalent, async session.execute). Use the REST API directly in the meantime — it is stable.