v0.1 · API stable

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:

Every transition is written to an append-only audit trail your auditor or regulator can read.

Core concepts

ConceptWhat it is
OrganizationYour tenant. Holds API keys and owns everything below.
AgentA registered AI agent with a name, risk level, and an optional approval threshold.
SessionA grouped run of actions, scoped to an agent and a user. Linked together in the audit trail.
ActionA single intent submitted for governance — e.g. send_email, wire_transfer, db_query.
PolicyA rule the policy engine evaluates against each action. Built-in patterns block SQL and prompt injection.
ApprovalA pending human decision on a high-risk action. Recorded with reason and approver.
Audit logAn 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
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
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
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
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

curl
curl "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.

Tip — interactive flow. The same end-to-end flow is exercised by 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

shell
pip install horkos-sdk

Configure

The simplest setup is zero-config — set HORKOS_API_KEY and the SDK uses https://api.horkos.eu by default.

shell
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:

python
horkos = 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.

python
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.

python
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:

python
from 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

python
entries = 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

ExceptionRaised when
PolicyViolationErrorThe policy engine blocked the action. e.violations lists why.
ApprovalRequiredErrorApproval is required and wait_for_approval=False.
ApprovalDeniedErrorA human explicitly denied the action.
RateLimitErrorThe agent exceeded its configured rate limit.
AuthenticationErrorMissing or invalid API key.
HorkosExceptionBase 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:

Defence in depth. Horkos runs in front of an edge WAF. Some payloads (e.g. obvious 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:

  1. Creates an Approval row with status=pending.
  2. Sends a notification (e.g. Slack webhook) to the configured approvers — the agent's owner, optionally a manager of the calling user_id, with a fallback to the org's admin.
  3. Writes approval.requested to the audit trail.
  4. The approver calls POST /v1/approvals/{id}/decide with approved or denied. The action transitions to executing or blocked, and the decision is audited as approval.approved or approval.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_typeFrameworkVersion
eu_ai_actEU Artificial Intelligence Act2024/1689
doraDigital Operational Resilience Act2022/2554
soc2SOC 2 Type IIAICPA 2017

Generate one

curl
curl -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:

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

GET/health

Liveness probe. No auth.

Organizations & API keys

POST/v1/orgs

Create an organization and receive its first API key. No auth.
Body: { name, slug, email }. Response: { org_id, api_key, message }.

POST/v1/orgs/api-keys

Create an additional key for the calling org. Query: name.

Agents

POST/v1/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.

GET/v1/agents

List all active agents for the org.

GET/v1/agents/{id}

Get one agent's full detail.

DELETE/v1/agents/{id}

Deactivate (soft delete). The agent's history is preserved.

Sessions

POST/v1/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.

POST/v1/sessions/{id}/end

End an open session.

GET/v1/sessions

List sessions. Optional filter: agent_id.

Actions

POST/v1/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.

PATCH/v1/actions/{id}/complete

Mark an executing action as completed with its output and (optional) cost/duration/tokens.

GET/v1/actions/{id}/approval

Poll for the approval status of an action. Used by the SDK while waiting.

GET/v1/actions

List actions with filters: agent_id, session_id, status, risk_level, limit, offset. Returns { actions, total }.

Approvals

GET/v1/approvals/pending

List approvals waiting for a human decision in the calling org.

POST/v1/approvals/{id}/decide

Approve or deny. Body: { decision: "approved" | "denied", decided_by, reason? }. Idempotent: re-deciding an already-decided approval returns 404.

Audit

GET/v1/audit

Query the immutable trail. Filters: agent_id, session_id, event_type, since_hours, limit.

GET/v1/audit/stats

Aggregated counters for the dashboard. Query: hours.

Reports

POST/v1/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.

Coming TypeScript SDK. A first-class TS/JS client matching the Python ergonomics (@horkos.agent decorator equivalent, async session.execute). Use the REST API directly in the meantime — it is stable.
Coming LangChain & LlamaIndex middleware. Drop-in adapters so existing chains/agents are governed without code changes.
Coming Stripe-billed plans. Free, Business (€299/month), Enterprise (custom). Today every org is on the free plan.
Coming Self-hosted Helm chart. Run the gateway in your own cluster, with the same audit trail and reports.
Found a gap? Talk to us at hello@horkos.eu — we prioritise based on what early users actually need.