API Reference

Base URL: https://api.horizon.example.com

All requests use JSON. All timestamps are in milliseconds (Unix epoch) with ISO 8601 representation.

Authentication

Include your API key in the X-API-Key header:

X-API-Key: your-api-key-here

Endpoints Overview

EndpointMethodDescription
/streams/{stream_id}/factsPOSTSeal a new fact
/streams/{stream_id}/factsGETList facts in a stream
/streams/{stream_id}/bundlesPOSTCreate proof bundle
/streams/{stream_id}/bundlesGETList bundles for a stream
/streams/{stream_id}/verifyPOSTVerify chain integrity
/facts/{fact_id}GETGet a single fact
/bundles/{bundle_id}GETGet a single bundle
/tenants/{tenant_id}/streamsGETList streams for a tenant
/healthGETHealth check

Seal a Fact

Creates a new fact in a stream. If the stream doesn't exist, it is created automatically.

POST /streams/{stream_id}/facts

Request Body

FieldTypeRequiredDescription
tenant_idstringYesYour tenant identifier
actorstringYesWho is making this declaration
custom_payloadobjectYesYour business data (opaque to Horizon)
client_refstringNoIdempotency key (prevents duplicates)

Example Request

{
  "tenant_id": "acme-corp",
  "actor": "ops-lead@acme-corp.com",
  "custom_payload": {
    "decision": "Approve emergency maintenance",
    "justification": "Memory leak confirmed",
    "affected_systems": ["api-server-01", "api-server-02"]
  },
  "client_ref": "incident-2024-01-26-approval-001"
}

Example Response

{
  "fact_id": "fact-01HRX7G2NB",
  "stream_id": "incident-2024-01-26",
  "tenant_id": "acme-corp",
  "actor": "ops-lead@acme-corp.com",
  "sealed_at_ms": 1706284801000,
  "sealed_at_iso": "2024-01-26T12:00:01.000Z",
  "custom_payload": {
    "decision": "Approve emergency maintenance",
    "justification": "Memory leak confirmed",
    "affected_systems": ["api-server-01", "api-server-02"]
  },
  "fact_hash": "a1b2c3d4e5f6...",
  "prev_hash": "9f8e7d6c5b4a...",
  "parent_fact_id": "fact-01HRX7F1MA"
}

Idempotency

If you provide client_ref, Horizon ensures exactly-once sealing. First request: fact is created. Subsequent requests with same client_ref: original fact is returned. This is safe for retries in unreliable network conditions.

List Facts (Timeline)

Returns all facts in a stream, in chain order.

GET /streams/{stream_id}/facts?limit=100&offset=0
{
  "facts": [
    {
      "fact_id": "fact-01HRX7F1MA",
      "sealed_at_ms": 1706284800000,
      "sealed_at_iso": "2024-01-26T12:00:00.000Z",
      "actor": "system:monitoring",
      "custom_payload": { "alert": "Memory usage at 95%" }
    },
    {
      "fact_id": "fact-01HRX7G2NB",
      "sealed_at_ms": 1706284801000,
      "sealed_at_iso": "2024-01-26T12:00:01.000Z",
      "actor": "ops-lead@acme-corp.com",
      "custom_payload": { "decision": "Approve emergency maintenance" }
    }
  ],
  "count": 2
}

Create Proof Bundle

Creates a signed, exportable snapshot of the stream.

POST /streams/{stream_id}/bundles
{
  "bundle_id": "bundle-01HRX8ABC",
  "stream_id": "incident-2024-01-26",
  "bundle_version": 1,
  "head_fact_id": "fact-01HRX7G2NB",
  "head_hash": "a1b2c3d4e5f6...",
  "facts_manifest": ["fact-01HRX7F1MA", "fact-01HRX7G2NB"],
  "created_at_ms": 1706284900000,
  "created_at_iso": "2024-01-26T12:01:40.000Z",
  "signature": "base64-encoded-signature...",
  "signature_alg": "ed25519",
  "key_id": "horizon-key-001"
}

Verify Chain Integrity

Verifies that the hash chain is intact.

POST /streams/{stream_id}/verify

Valid Response

{
  "valid": true,
  "facts_verified": 47
}

Tampered Response

{
  "valid": false,
  "facts_verified": 23,
  "error": "Hash chain broken at fact-01HRX8XYZ"
}

Error Codes

CodeHTTP StatusDescription
VALIDATION_ERROR400Invalid request payload
NOT_FOUND404Resource not found
UNAUTHORIZED401Missing or invalid API key
INTERNAL_ERROR500Server error

Rate Limits

TierRequests/minuteBurst
Standard1,000100
EnterpriseCustomCustom

Rate limit headers are included in all responses: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset