Skip to main content
The reference pages in this section are generated from the API’s OpenAPI specification. Each operation page lists its parameters, request body, responses, and an interactive request builder. This page is the orientation: the base URL, how requests authenticate, the response conventions, and every operation in lifecycle order.

Base URL

Every path is versioned under /api/v1. Substitute the base URL from your onboarding if you have one.

Authentication

Every request carries a bearer token in the Authorization header:
The token is a workspace API key (prefix ish_sk_live_), minted in Settings, then Developers. Keys are minted with sessions:run, sessions:read, and tasks:read; ask for tasks:write, environments:read, or environments:write if the key manages a registry rather than just running against it. An ish user access token also works on the same header for personal scripts. See authentication.

What “session” means here

A session is one participant working toward one task in your environment, turn by turn. It is not the ish product’s notion of a study session (a person sitting down with a study), and it is unrelated to a browser session. Sessions live under /v1/sessions.

The operations

Twenty-one operations across four surfaces, and this is all of them. Sessions is the loop: open a session for a person, submit turns, read the trace, list your sessions, close it. The other three are registries you write once and reference by id. Environments names a target instead of describing it inline on every session. Environment versions are the declared variants of one environment, so a session can pin the exact variant it ran against. Tasks names an intent a session can bind by id, freezing its instructions and revision onto the session. You need none of the registries to run a session. Describe the environment inline, pass the instructions inline, and the sessions surface alone is a complete integration.

Sessions

Environments

Environment versions

Tasks

Operations that create a thing in a workspace, or list what it owns, nest under /workspaces/{workspace_id}/…. The rest act on an existing record by its globally-unique id, so the path carries no workspace: the id already resolves it. Both address the same workspace and authorize the same way.

Environments

An environment is your own product surface under test: the site, app, device, or world you are building. It is not a deploy target, a hosting environment, or a sandbox ish runs for you. You render it and you execute in it. Every session names the environment it runs in, one of two ways, and you must supply exactly one:
  • environment_id, a registered environment. It supplies the default operating notes for the resolver and a default action vocabulary, and its name and kind are snapshotted onto the session.
  • environment, an ad-hoc descriptor ({ name, kind }) described inline on the session.
An environment has a kind (web, device, world today; treat it as an open union) and a config validated per kind: web needs config.url, device needs config.app_artifact, and any kind may declare an origins allowlist. Identity is the environment_id, never the URL, so a target reached through a rotating tunnel host keeps the same environment across sessions. Renaming or deleting an environment never rewrites history: a session keeps the name and kind it was opened with, and its environment_id simply becomes null. The turn-by-turn contract that drives these (ordering, retries, indexing) is in run the session loop.

Response conventions

  • Correlation id. Every response carries an X-Request-Id header. Error bodies echo it as request_id. Quote it in support requests.
  • Two error envelopes. A request-shape failure (HTTP 422) uses the ValidationError envelope, a field-level object with error_code and an errors array so you can pinpoint the offending field. Everything else (401, 403, 404, 409, 502, and handler-raised 422s) uses the Error envelope, a { detail, request_id } object where detail may be a string or a structured object carrying a machine-readable error_kind such as idempotency_conflict, turn_index_mismatch, or decision_invalid. Every kind, with what it means and what to do, is in the error reference; where each one lands in the loop is in handling errors.
  • Open unions. Several enum-typed and block-typed fields (sentiment, session_status, resolution, the observation block type) may gain new values over time. Tolerate a value you do not recognize rather than failing on it.
  • Idempotency. Every create accepts an Idempotency-Key header (sessions, environments, environment versions, tasks); a retry with the same key and body replays the thing already created. See the create operations and run the session loop.
  • Pagination, two shapes. Reading one session pages its trace by index: pass after_index and limit (default 50, max 200), and page again while has_more is true. Listing a workspace’s sessions pages by opaque keyset instead: pass after and limit (default 25, max 100), then follow next_cursor while has_more is true, newest first. Never build a cursor yourself and never treat it as an offset. The environment, environment-version, and task lists return the whole collection under items and are unpaginated today; they carry has_more: false and next_cursor: null so pagination can land later without a wire break, so read those two fields rather than assuming one page forever.
  • Versioned path. Every path is versioned under /api/v1. What that promises, and what may change inside it, is in versioning.

Before you integrate

Quickstart

Run one turn end to end against the live wire.

Run the session loop

The full contract: ordering, retries, errors, pagination.

Observations

What a turn may send, and why state is refused.

Author your environment

Declare actions and render frames that decode.