API reference · Conventions

API conventions

Everything that is true of every endpoint, in one place, so the individual reference pages can stay short. Read this once and the rest of the API stops surprising you.

Base URL and versioning

Every endpoint lives under /v1.

basetext
https://api.esy.com/v1

The version is in the path, and a breaking change means a new path — /v1 will not change shape under you. Additive changes, such as a new field in a response, happen within /v1, so parse responses tolerantly and ignore fields you do not recognise.

Authentication

One bearer token on every request. See Authentication.

headertext
Authorization: Bearer esy_sk_…

Casing

The wire format is camelCase in both directions — templateId, workspaceId, actualCostUsd. The Python service behind it uses snake_case internally, which occasionally leaks into an error message; the documented contract is camelCase.

Identifiers

Most ids are prefixed, which means you can tell what an id refers to without context — useful in logs.

ObjectFormatExample
Runrun-run-fb0677b2
Artifactartifact-artifact-5a6a9501
Stepstep-step-3b
Orderorder-order-1f2e3d4c
Budgetbudget-budget-7a1c…
Workspace / projectUUID9a1b6d4c-d1dc-…
Workflowhuman sluggenerate-illustration
Workflow ids are slugs, not opaque handles
A workflow id is a readable, stable slug you can hard-code. It is never reused for a different workflow, so pinning one in your source is safe.

Pagination

List endpoints return an envelope with items and total, and take offset and limit query parameters. limit is capped at 100.

list envelopejson
{
  "items": [ … ],
  "total": 128
}
Pagination is not universal
Some endpoints take no offset/limit at all and return everything, and a few use a cursor instead. Check the page for the endpoint you are calling rather than assuming. Where you see total, it is the count of matching records, not the count in this page.

Filtering

Most list endpoints accept workspaceId and projectId, and collection endpoints for runs also accept status. Some require workspaceIdoutright — omitting it returns a 422 naming the missing query parameter.

Idempotency

There is no server-side idempotency yet. POST /v1/runs creates a run every time it is called, so a client that retries after a timeout can start — and pay for — the same work twice.

what is safe to retrytext
// Safe: reads can be retried freely.
GET /v1/runs/run-fb0677b2

// Not safe to blind-retry: a second call starts a second run and bills twice.
POST /v1/runs

If a run creation times out, list recent runs and look for yours before retrying. For batch work, prefer Generation Orders, which carry an orderDedupeKey so duplicate items collapse rather than multiply.

Errors

Every error body has a top-level detail, which may be a string, an object, or an array. Errors covers each code with real responses.

Rate limits

None are enforced per key today, and no rate-limit headers are returned. Do not take that as licence to hammer the API: bound your own concurrency, especially when fanning out runs. Use an order rather than a thousand parallel POSTs.

The endpoints you will use most

GET/v1/catalog/workflowsBrowse runnable workflows. Public — no key needed.
POST/v1/runsStart a run.
GET/v1/runs/{run_id}Read a run and its step telemetry.
GET/v1/runs/{run_id}/eventsStream a run over SSE instead of polling.
GET/v1/artifacts/{artifact_id}Read an artifact.
POST/v1/ordersPlan a batch of runs.
GET/v1/queueSee what is waiting on a human.
GET/v1/costsAggregate spend.

The full surface

The API is considerably larger than these docs cover — publications, outlets, workers, documents, media, finance. The authoritative list is the OpenAPI document, which is generated from the routers and therefore cannot drift:

machine-readable spectext
https://api.esy.com/openapi.json

These pages document the parts that are stable and that an external caller needs. If something appears in the spec but not here, treat it as internal and subject to change.

In short
  • Everything is under /v1, camelCase both ways, bearer auth on every call.
  • Lists return { items, total } with offset/limit, capped at 100 — but not every endpoint paginates.
  • No idempotency keys: never blind-retry a POST /v1/runs.
  • openapi.json is the authoritative surface; these pages are the curated part.