Use the deployed origin, then choose a surface
The production API lives at https://lizzy.albinilabs.com/v1. The proxy is a base-URL swap for an OpenAI-compatible client; the rest of the API manages sources, datasets, rewards, runs, reports, deployments, events, and webhooks.
https://lizzy.albinilabs.com/v1https://lizzy.albinilabs.com/v1/proxyhttps://lizzy.albinilabs.com/v1/openapi.jsonhttps://lizzy.albinilabs.com/healthzhttp://localhost:8080/v1/v1/proxy/chat/completionsSend an OpenAI-compatible request through a configured source, or serve a deployment by using model: "lz:<alias>".
Authenticate server-side, then ask who you are
Send a secret API token as Authorization: Bearer <token>. Live tokens begin with lz_live_; test tokens begin with lz_test_. Each token belongs to one workspace, one mode, and one set of scopes. The full secret appears only in the token-creation response, so store it in a secret manager immediately.
Start every integration with GET /v1/whoami. It verifies the credential and makes workspace, environment, scopes, and the dated API version visible before any write.
export LIZZY_ORIGIN="https://lizzy.albinilabs.com"
: "${LIZZY_API_TOKEN:?Inject an lz_test_ token from your secret manager}"
curl --fail-with-body --silent --show-error --max-time 15 \
-H "Authorization: Bearer $LIZZY_API_TOKEN" \
"$LIZZY_ORIGIN/v1/whoami"
{
"object": "whoami",
"workspace": {
"id": "ws_example",
"name": "Example workspace",
"plan": "pro"
},
"credential": {
"type": "api_token",
"id": "tok_example",
"name": "Experiment runner",
"environment": "test",
"scopes": ["distill:read", "distill:write", "events:read"]
},
"livemode": false,
"api_version": "2026-07-01"
}
A token’s mode is a data boundary
Token mode is fixed when the token is created. A test token reads and writes test-mode resources; a live token reads and writes live resources. Passing livemode=trueon a list request cannot make a test token cross that boundary, and the dashboard-onlyLizzy-Mode: test header does not change an API token.
Test token
Separate test data, no ledger charge, and every Distill run is forced to dry-run.
Prefix: lz_test_Live token
Sees live resources and can trigger billable work when its scopes allow it.
Prefix: lz_live_Product sandbox
Dedicated sandbox routes and dashboard test conversations have their own semantics.
Do not infer mode from the word sandboxChoose scopes by job
full_access passes every scope check, but a long-lived automation should carry only what it uses. Reads and writes are separate for Distill, events, webhooks, tokens, billing, agents, conversations, sources, channels, and workspace administration.
distill:read, distill:writeevents:read, plus webhooks:read for delivery diagnosiswebhooks:read, webhooks:writetokens:read, tokens:write; keep this out of routine workersfull_access; reserve for tightly controlled administrationPreserve the headers that carry intent
Bearer lz_test_… or Bearer lz_live_….application/json for JSON writes; uploads document their binary type separately.error.request_id.One key means one logical write for 24 hours
Lizzy hashes the exact request-body bytes. The same key and byte-identical body replay the stored status and JSON body with Idempotent-Replayed: true. The same key with different bytes returns 409 idempotency_key_reused; a duplicate that overlaps an unfinished request returns 409 idempotency_key_in_flight. A failed operation releases its reservation so the original request can be retried.
request_body='{"name":"support-quality","description":"Preserve resolution quality"}'
: "${IDEMPOTENCY_KEY:=$(uuidgen)}"
curl --fail-with-body --silent --show-error --max-time 20 \
-X POST "https://lizzy.albinilabs.com/v1/distill/loops" \
-H "Authorization: Bearer $LIZZY_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
--data-binary "$request_body"
# Unknown transport outcome? Run the same curl command with the same two variables.
POST /distill/uploads, POST /distill/runs, and POST /distill/deployments.Walk cursor pages until has_more is false
List responses use data, has_more, andnext_cursor. limit defaults to 20; values above 100 are clamped to 100, while values below 1 fail validation. Treat cursors as opaque and URL-encode them.
{
"data": [{ "id": "evt_example", "object": "event" }],
"has_more": true,
"next_cursor": "eyJ0IjoiMjAyNi0wOC0xNVQxNzozMDowMCswMDowMCIsImlkIjoiZXZ0X2V4YW1wbGUifQ"
}
first_page=$(curl --fail-with-body --silent --show-error --get \
-H "Authorization: Bearer $LIZZY_API_TOKEN" \
--data-urlencode "limit=20" \
"https://lizzy.albinilabs.com/v1/events")
printf '%s' "$first_page" | jq '.data'
cursor=$(printf '%s' "$first_page" | jq -r '.next_cursor // empty')
if [ -n "$cursor" ]; then
curl --fail-with-body --silent --show-error --get \
-H "Authorization: Bearer $LIZZY_API_TOKEN" \
--data-urlencode "limit=20" \
--data-urlencode "cursor=$cursor" \
"https://lizzy.albinilabs.com/v1/events" | jq .
fi
Persist asynchronous resource IDs before you poll
Uploads, connector pulls, dataset versions, runs, and deployments can outlive the request that created or discovered them. Store the returned ID, poll its GET endpoint with a capped delay and deadline, and stop only on that resource’s documented terminal state.
Use the enriched Distill contract for agent discovery
The versioned OpenAPI 3.1 document inventories the public Distill surface and enriches it with operation IDs, request models, workflow stages, prerequisites, async terminal states, recovery notes, human-only boundaries, and transport guarantees. Internal control-plane routes are removed.
Open/v1/openapi.json curl --fail --silent --show-error --max-time 20 \
"https://lizzy.albinilabs.com/v1/openapi.json" \
| jq '{openapi, info, servers, distill_run: .paths["/v1/distill/runs"].post}'
BearerAuth HTTP security scheme. The shared-report capability URL explicitly declares no bearer requirement; possession of its revocable share token is the authority.x-lizzy-idempotency-supported, x-lizzy-idempotency-required, and the header’s required flag distinguish optional from mandatory keys exactly.X-Request-Id; idempotent mutations also document Idempotent-Replayed.