Skip to main content
Horizons is self-hosted. The server you run locally is the same API surface your deployment will expose.
This quickstart uses the dev backends included with horizons_server.

Run the server

From the Horizons/ repo:
cargo run -p horizons_server -- serve --host 0.0.0.0 --port 8000

Pick a tenant org id

By default (dev mode), Horizons enforces tenant isolation via x-org-id. For local dev, generate a UUID (any UUID works):
uuidgen
Export it for convenience:
export HORIZONS_ORG_ID="<uuid>"
For production-style auth, create an API key and require bearer auth:
# create a key (stored in the Central DB)
cargo run -p horizons_server -- create-api-key --org-id "$HORIZONS_ORG_ID" --name dev

# then run the server with:
export HORIZONS_REQUIRE_AUTH=true
export HORIZONS_ALLOW_INSECURE_HEADERS=false

Create a project

curl -sS -X POST \
  -H "x-org-id: $HORIZONS_ORG_ID" \
  -H "content-type: application/json" \
  http://localhost:8000/api/v1/projects \
  -d '{}'

Publish an event

curl -sS -X POST \
  -H "x-org-id: $HORIZONS_ORG_ID" \
  -H "content-type: application/json" \
  http://localhost:8000/api/v1/events/publish \
  -d '{
    "direction": "outbound",
    "topic": "demo.hello",
    "source": "demo",
    "payload": {"hello": "world"},
    "dedupe_key": "demo:1"
  }'

Run an agent

Horizons uses identity headers for audit logging and action approvals.
curl -sS -X POST \
  -H "x-org-id: $HORIZONS_ORG_ID" \
  -H "x-agent-id: agent:demo" \
  -H "x-project-id: <project_uuid>" \
  -H "content-type: application/json" \
  http://localhost:8000/api/v1/agents/run \
  -d '{
    "agent_id": "dev.noop",
    "inputs": {"prompt": "hello"}
  }'

Propose and approve an action

Actions are the governance primitive in Horizons. Agents propose actions, and reviewers or policy approve/deny before execution.
ACTION_ID=$(curl -sS -X POST \
  -H "x-org-id: $HORIZONS_ORG_ID" \
  -H "x-project-id: <project_uuid>" \
  -H "x-agent-id: agent:demo" \
  -H "content-type: application/json" \
  http://localhost:8000/api/v1/actions/propose \
  -d '{
    "agent_id": "agent:demo",
    "action_type": "demo.notify",
    "payload": {"message": "hello from quickstart"},
    "risk_level": "low",
    "context": {"source": "quickstart"},
    "dedupe_key": "quickstart-action-1"
  }' | jq -r '.action_id')

curl -sS -X POST \
  -H "x-org-id: $HORIZONS_ORG_ID" \
  -H "x-project-id: <project_uuid>" \
  -H "content-type: application/json" \
  http://localhost:8000/api/v1/actions/$ACTION_ID/approve \
  -d '{"reason":"quickstart approval"}'
Verify action state and audit:
curl -sS \
  -H "x-org-id: $HORIZONS_ORG_ID" \
  -H "x-project-id: <project_uuid>" \
  "http://localhost:8000/api/v1/actions/pending?limit=20"

curl -sS \
  -H "x-org-id: $HORIZONS_ORG_ID" \
  -H "x-project-id: <project_uuid>" \
  "http://localhost:8000/api/v1/audit?limit=20"
Next: