Skip to main content

Name

groundhog-http: v1 client API over a Unix domain socket.

Transport

The service speaks HTTP/1.1 over the Unix socket configured by [server].socket. It does not listen on TCP. HTTP/1.1 requires a Host header, but Groundhog ignores its value. With curl:
Request and response bodies are UTF-8 JSON. A body request accepts Content-Type: application/json or a missing content type. The optional charset=utf-8 parameter is accepted. Other media types, parameters, or any Content-Encoding return 415 before the body is read.

Authentication

If [server].token is non-empty, every request must include:
A missing or mismatched token returns:
Authorization happens before routing, admission, or body reading. When the configured token is empty, the authorization header is ignored.

Current routes

An unknown path returns 404. A known path with another method returns 405 and an Allow header. HEAD returns the same status and headers as GET with no body.

Error documents

Most errors use:
Per-event batch validation uses:
Clients should match HTTP status and document structure rather than message wording. Repeated query parameters, duplicate JSON member names, and unknown body members are rejected. Control envelopes are bounded at 1 MiB. JSON ingest has its own larger bound.

POST /v1/events

Appends one atomic batch:
v defaults to 1 when omitted. source and batch_id are batch-level fields. A submitted event has exactly stream, record_key, kind, optional occurred_at, and payload. The batch is rejected whole if any event is invalid. Empty batches are invalid. Limits are 10,000 events and 32 MiB of encoded body.

Idempotency

(source, batch_id) is reserved for the lifetime of the data directory. Groundhog computes a canonical digest from the submitted content before assigning server fields. A new batch returns:
An identical retry returns the original receipt with status: "duplicate" and writes nothing. Reusing the key for different content returns 409 and writes nothing. A successful ingest receipt means the batch is durable. A lost response leaves the client uncertain; retrying identical content under the same key safely converges on one batch.

Naming and bounds

  • source, stream, and ordinary name-like values are bounded and source/stream names match [a-z0-9_][a-z0-9_.-]*;
  • record_key and batch_id must be non-empty;
  • source system is reserved;
  • submitted batch IDs beginning with groundhog/ are reserved; and
  • occurred_at, when supplied, is UTC with a Z suffix.
See Events for the full fixed event envelope.

GET /v1/events

Replays authoritative history in increasing event_id order:
All parameters are optional and exact-match. after is exclusive. limit must be a positive integer no greater than [replay].max_limit; omission uses [replay].default_limit. Response:
last_event_id is the last matching event returned and is omitted on an empty match. It is not the progress cursor for a filtered subscription. next_after is the position through which the request actually scanned. When the snapshot is exhausted it advances to the snapshot frontier even if no event matched. Persist it for the next poll so unrelated history cannot stall a filtered consumer. snapshot_through_event_id is the coherent frontier captured for this request, or null for an empty log.

POST /v1/query

Executes one confined read-only statement against one pinned published warehouse generation:
sql is required. format defaults to json, the available result format. limit and timeout_secs default to configuration and must be positive and within their configured maxima. Success:
The query boundary allows one SELECT or WITH statement over published relations. It rejects mutations, multiple statements, file/network table functions, extension installation/loading, attach/copy/import/export, secrets, shell access, mutating pragmas/settings, volatile functions, and unproved order-dependent constructs. A result cut by the server row limit requires a total top-level ORDER BY. The order must cover all output columns; ORDER BY ALL is the simplest general form. Nested LIMIT or OFFSET blocks require the same proof. Without a total order, an over-limit result is rejected instead of returning an arbitrary subset. Queries execute only against the last published generation. Events appended after its receipt remain available through replay but are absent from SQL until project or rebuild publishes a new frontier.

GET /v1/catalog

Returns published stream-level metadata:
Both narrowing parameters are optional. Response:
The receipt is from the same pinned warehouse generation as the stream list.

Common status codes

A 429 rejected before mutation enqueue has written nothing and is retryable. Once mutation work is enqueued, it is not abandoned because the client disconnects. A 503 means the writer cannot accept further mutation until the service is closed and reopened through recovery.

Unavailable forms

The binary does not implement NDJSON streaming ingest, POST /v1/imports, observation ingest, or Arrow query results. Sending their media types, routes, or formats does not activate partial behavior.

See also

serve, configuration, events, warehouse, getting started