REST API & Service

interlace serve runs the Interlace daemon: HTTP API, background scheduler, stream flusher, and an embedded web UI — one process. It requires the service extra (pip install 'interlaced[service]').

Starting the Service

interlace serve                          # 127.0.0.1:8000, scheduler on
interlace serve --host 0.0.0.0 --port 9000
interlace serve --env dev                # serve a sandbox environment
interlace serve --no-scheduler           # API only; run `interlace scheduler` separately
interlace serve --quack quack:localhost:4213   # also share the warehouse

If the port is busy, the next free one is used. On boot it prints UI at http://127.0.0.1:8000/ui; interactive OpenAPI docs live at /schema/scalar.

Authentication

The API starts in keyless mode: while no API key exists, everything is open for local development. Creating the first key locks the API down immediately:

interlace apikey create ci --scope read
# ilk_2f4a...  (shown once — store it now)

Tokens are ilk_-prefixed and sent as a bearer header:

curl -H "Authorization: Bearer ilk_..." localhost:8000/models

Three scopes: read (all GETs and the query console), write (trigger runs and applies, publish events, run checks), admin (key management, environment drops, GC — and it satisfies every other requirement). A key carries any combination (--scope is repeatable); manage keys with interlace apikey create|revoke|list or the /apikeys endpoints. /health, /schema/*, and the /ui shell stay open — the UI’s API calls still enforce scopes.

The API at a Glance

AreaEndpoints
ModelsGET /models, GET /models/{name}, GET /models/{name}/impact (column blast radius)
Plan & applyGET /plan, POST /apply
RunsGET /runs, GET /runs/{id}, POST /runs, POST /runs/{id}/cancel
EnvironmentsGET /environments, DELETE /environments/{name}, GET /environments/{name}/history, POST /environments/{name}/rollback
ChecksGET /checks, POST /checks/run
StreamsGET /streams, GET /streams/{name}, POST /streams/{name}
QueryPOST /query (SELECT-only console)
LineageGET /lineage (whole graph, column-level)
SystemGET /engines, GET /schedules, GET /health, POST /gc
KeysGET /apikeys, POST /apikeys, DELETE /apikeys/{name}
EventsGET /events, GET /events/stream (SSE)

Full request/response shapes are in the API reference.

Triggering work

# enqueue a run on the durable queue (executed with leases + retries)
curl -X POST localhost:8000/runs -H 'content-type: application/json' \
  -d '{"selectors": ["orders+"]}'

# plan/apply, mirroring the CLI (breaking changes 400 without force)
curl -X POST localhost:8000/apply -H 'content-type: application/json' \
  -d '{"environment": "dev", "forward_only": false, "force": false}'

The query console

POST /query runs exactly one SELECT (a single Select/Union at the top level; DDL/DML is rejected before execution), capped at 10,000 rows. The SQL is parsed and fenced before it runs: every table source must be a real table or view — table functions and file/HTTP readers like read_csv, query, and glob are refused (named or not), so the console can only read the warehouse. The same fence backs interlace query on the CLI:

curl -X POST localhost:8000/query -H 'content-type: application/json' \
  -d '{"sql": "SELECT * FROM main.event_totals", "limit": 100}'

Returns columns, types, rows, row_count, truncated, and elapsed_ms (30s timeout).

Live Events

Everything the platform does lands on a durable event log: run.* (enqueued/started/succeeded/retrying/failed/cancelled), apply.* (started/finished/blocked), per-model build progress (model.start/model.done/model.failed), stream.flushed, environment.dropped, environment.rolled_back, gc.finished.

  • GET /events/stream — Server-Sent Events; reconnecting clients resume from Last-Event-ID with no gaps. EventSource can’t send an Authorization header, so once the API is keyed, clients poll GET /events instead
  • GET /events?after=<seq> — polling, 200 events per page

The Web UI

/ui serves a zero-build-step web app with ten views:

ViewWhat it shows
OverviewEnvironment, drift, recent runs, streams, checks at a glance
LineageWhole-graph canvas — trace a model’s blast radius or a single column across the pipeline; edges animate while builds run
ModelsEvery model with detail, SQL/source, and one-click runs
PlanThe live plan with SQL diffs; apply from the browser
RunsThe queue — rows expand in place with CLI-style build results; cancel runs
QueryThe SELECT console with a table browser
StreamsHeads, watermarks, recent payloads; publish test events
ChecksCheck history; run checks on demand
EnvironmentsPromote state and drift per environment; apply or drop
SystemEngines, schedules, API keys, GC

A build dock narrates the currently running build on every view, fed by the live event stream.

Next Steps