ish

ish Events API

The events API from ish — 1 operation(s) for events.

GitHub OpenAPI

Operations 1

GET /api/v1/workspaces/{workspace_id}/events List the workspace's events #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/ishlabs-io:ishlabs-io-events-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

ishlabs-io-events-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  contact:
    name: ish developer platform
    url: https://docs.ishlabs.io/
  description: 'The ish Developer API: the entry point for getting human feedback on whatever you''re building, in whichever format you need. Its first surface is **sessions**: you open a session for one of your people inside *your own* environment, submit what that person perceives (human-sensory observations: a rendered screen, an error message) along with the set of actions your environment currently offers, and they decide one action per turn and tell you how it felt. Your code executes the chosen action and loops. **The loop is decide-only: ish never locates, drives, or executes anything inside your environment.** You stay in control of what runs; we tell you what a person would do next and why. ## Vocabulary


    The public vocabulary is **workspace**, **person**, **environment**, and **session**, and the wire uses these names directly:


    - `workspace_id`: the workspace that owns the person and is billed for the session (the create path is `/workspaces/{workspace_id}/sessions`). - `person_id`: the person the session drives. Two kinds are available to you: the ones your workspace owns, and the ones published in the shared ish library. Both are listed and searchable under `/v1/workspaces/{workspace_id}/people`, and each carries an `owner` saying which it is. - `environment_id`: a registered environment in that workspace: a named target with its own operating notes and default action vocabulary. Register one and reference it, or describe an ad-hoc one inline on the session; supply exactly one of the two. **"Session" here means one API session: a single person working toward one task in your environment, turn by turn.** It is not the ish product''s notion of a study session (someone sitting down with a study), and it is unrelated to a browser session. Sessions live under `/v1/sessions`. ## Authentication


    All requests use a bearer token: `Authorization: Bearer `. The token is a **workspace API key** (prefix `ish_sk_live_`), a workspace-scoped machine principal minted in Settings > Developers and shown exactly once at mint. Scopes: `sessions:run` (create, submit turns, close), `sessions:read` (read the session and trace), `environments:read` / `environments:write` for the environment registry, `tasks:read` / `tasks:write` for the task registry, `people:read` / `people:write` for the people you can run a session for, and `usage:read` for this workspace''s consumption, limits and rate-limit budgets. Keys are minted with `sessions:run`, `sessions:read`, and `tasks:read`; ask for any `:write` scope, and for `people:read` and `usage:read`, explicitly. `usage:read` is never implied by another scope: it covers the shape of the whole workspace''s operation, not just the lane a key was minted for. An ish user access token (JWT) also works on the same header for personal scripts; server integrations should use keys. ## Versioning & forward-compatibility


    The API is versioned in the URL path (`/api/v1`). Several enum-typed and block-typed fields are **open unions**: new values may be added over time, and clients MUST tolerate values they do not recognize rather than failing on them. The field descriptions call these out. ## Correlation & errors


    Every response carries an **`X-Request-Id`** header; quote it in support requests. Error responses additionally echo it in the body as `request_id`. Two error-body shapes exist; parse defensively, they differ:


    - **`ValidationError`**: the request-validation layer (HTTP 422): a malformed body (wrong types, missing fields, an invalid observation `type`, a bad enum value, `parameters` that don''t fit the model). A field-level envelope (`error_code`, `errors[]`, `suggestions[]`; `suggestions` is CLI-oriented boilerplate; ignore it for API use). - **`Error`**: everything else, a `{ detail, request_id }` envelope. This covers the handler layer''s business-rule 422s (e.g. `exactly one image observation block is required`; `person has no renderable background`) as well as 401 / 402 / 403 / 404 / 409 / 429 / 502.'
  license:
    identifier: LicenseRef-Proprietary
    name: Proprietary
  title: ish Developer Events API
  version: 1.11.0
servers:
- description: Production
  url: https://api.ishlabs.io
security:
- bearerAuth: []
tags:
- name: Events
paths:
  /api/v1/workspaces/{workspace_id}/events:
    get:
      description: 'Events for one workspace, newest first.


        Retained for 30 days. The window is enforced on this read as well as by the

        sweep, so a late or un-scheduled sweep can never surface an event older than

        the documented lifetime.


        An unknown value in `types` is refused rather than silently matching

        nothing: a typo''d filter that returns an empty page reads exactly like "your

        integration produced no events", which is the most expensive way for a

        caller to lose a day.'
      operationId: listEvents
      parameters:
      - in: path
        name: workspace_id
        required: true
        schema:
          format: uuid
          title: Workspace Id
          type: string
      - description: Filter to these event types. Repeat the parameter to pass several. Omit for all types.
        in: query
        name: types
        required: false
        schema:
          anyOf:
          - items:
              type: string
            type: array
          - type: 'null'
          description: Filter to these event types. Repeat the parameter to pass several. Omit for all types.
          title: Types
      - description: Opaque cursor from `next_cursor`.
        in: query
        name: after
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: Opaque cursor from `next_cursor`.
          title: After
      - in: query
        name: limit
        required: false
        schema:
          default: 25
          maximum: 100
          minimum: 1
          title: Limit
          type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventListResponse'
          description: Successful Response
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            x-ratelimit-limit:
              $ref: '#/components/headers/XRateLimitLimit'
            x-ratelimit-remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            x-ratelimit-reset:
              $ref: '#/components/headers/XRateLimitReset'
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: 'The bearer token is missing, malformed, unknown, or revoked (`detail.error_kind = ''invalid_api_key''`). One kind covers all four deliberately: distinguishing them would confirm which guesses are real keys.'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            x-ratelimit-limit:
              $ref: '#/components/headers/XRateLimitLimit'
            x-ratelimit-remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            x-ratelimit-reset:
              $ref: '#/components/headers/XRateLimitReset'
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: The API key lacks the `events:read` scope (`detail.error_kind = 'insufficient_scope'`, with `detail.required_scope = 'events:read'`). Mint a key that includes it; an ish user access token is unconstrained by scopes.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            x-ratelimit-limit:
              $ref: '#/components/headers/XRateLimitLimit'
            x-ratelimit-remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            x-ratelimit-reset:
              $ref: '#/components/headers/XRateLimitReset'
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: The workspace (`workspace_id`) was not found, or is not reachable with this credential. `detail.error_kind` is `workspace_not_found`.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            x-ratelimit-limit:
              $ref: '#/components/headers/XRateLimitLimit'
            x-ratelimit-remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            x-ratelimit-reset:
              $ref: '#/components/headers/XRateLimitReset'
        '422':
          content:
            application/json:
              schema:
                oneOf:
                - $ref: '#/components/schemas/ValidationError'
                - $ref: '#/components/schemas/Error'
          description: Validation error. A request-shape failure uses the `ValidationError` envelope; a handler-raised domain-validation refusal uses the `Error` envelope.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            x-ratelimit-limit:
              $ref: '#/components/headers/XRateLimitLimit'
            x-ratelimit-remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            x-ratelimit-reset:
              $ref: '#/components/headers/XRateLimitReset'
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: 'The API key''s request rate limit is exceeded (`detail.error_kind = ''rate_limited''`). Transient: retry after the `Retry-After` interval, also echoed as `detail.retry_after_seconds`. Not applied to user access tokens.'
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
            x-ratelimit-limit:
              $ref: '#/components/headers/XRateLimitLimit'
            x-ratelimit-remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            x-ratelimit-reset:
              $ref: '#/components/headers/XRateLimitReset'
      security:
      - bearerAuth: []
      summary: List the workspace's events
      tags:
      - Events
components:
  headers:
    XRateLimitReset:
      description: '**Seconds from now** until that window resets (a delta, NOT a Unix timestamp). Measured when the response is written. Omitted on the rare occasion the reset instant could not be read, while `limit` and `remaining` are still published.'
      schema:
        type: integer
    XRequestId:
      description: Per-response correlation id, present on EVERY response (success and error). Error bodies echo it as `request_id`.
      schema:
        type: string
    XRateLimitRemaining:
      description: Requests left in that window AFTER this one was counted. Pace off this rather than waiting for a `429`.
      schema:
        type: integer
    XRateLimitLimit:
      description: Ceiling for the rate-limit window this request charged. Present on responses made with an API key against a rate-limited operation, including the `429` itself; absent for user access tokens (which carry no per-key budget) and when the rate-limit backend could not be read.
      schema:
        type: integer
  schemas:
    SessionEventTask:
      description: The task the session ran, as identifiers only.
      properties:
        id:
          anyOf:
          - format: uuid
            type: string
          - type: 'null'
          description: 'The registered task, or null when the session was opened with inline instructions. Null is a real answer, not a gap: an inline session''s intent has no registry identity.'
          title: Id
        revision:
          anyOf:
          - type: integer
          - type: 'null'
          description: The task's revision at bind time. Two runs of one task are only comparable within a single revision of its instructions.
          title: Revision
      required:
      - id
      - revision
      title: SessionEventTask
      type: object
    SessionEventResponse:
      properties:
        created_at:
          format: date-time
          title: Created At
          type: string
        data:
          $ref: '#/components/schemas/SessionEventData'
        id:
          description: Stable id for this event; safe to dedupe on.
          format: uuid
          title: Id
          type: string
        type:
          description: '`session.completed` or `session.failed`. The split is who ENDED the run, not whether the person succeeded: the person or caller ending it leaves a usable trace (including `gave_up`, which is a finding rather than a fault), while a limit cutting it short does not. Route on this; read `data.object.status` for precision. Open union; new types may be added.'
          title: Type
          type: string
      required:
      - id
      - type
      - created_at
      - data
      title: SessionEventResponse
      type: object
    FieldError:
      description: One field-level entry in a ValidationError envelope.
      properties:
        allowed_values:
          description: For enum/literal failures, the accepted values.
          items: {}
          title: Allowed Values
          type: array
        input:
          anyOf:
          - type: string
          - type: number
          - type: boolean
          - type: 'null'
          description: Offending value (truncated); omitted for containers.
          title: Input
        loc:
          description: Path to the offending field.
          items:
            anyOf:
            - type: string
            - type: integer
          title: Location
          type: array
        msg:
          title: Message
          type: string
        type:
          title: Error Type
          type: string
      required:
      - loc
      - msg
      - type
      title: FieldError
      type: object
    ValidationError:
      description: Request body / parameter validation failure (HTTP 422). A field-level envelope so callers can pinpoint the offending field, value, and allowed values without parsing prose.
      properties:
        detail:
          title: Detail
          type: string
        error:
          title: Error
          type: string
        error_code:
          description: Stable machine code, e.g. `validation_error`.
          title: Error Code
          type: string
        errors:
          items:
            $ref: '#/components/schemas/FieldError'
          title: Errors
          type: array
        retryable:
          title: Retryable
          type: boolean
        status:
          title: Status
          type: integer
        suggestions:
          items:
            type: string
          title: Suggestions
          type: array
      required:
      - error
      - error_code
      - status
      - retryable
      - detail
      - errors
      title: ValidationError
      type: object
    SessionEventObject:
      description: The session this event describes.
      properties:
        ended_reason:
          anyOf:
          - type: string
          - type: 'null'
          description: Finer than `status` where the status is ambiguous. Two exhaustion kinds share `balance_exhausted`, and this is what separates `credits_exhausted` (top up) from `plan_allowance_exhausted` (the period allowance, which returns on renewal). Null for an ordinary ending.
          title: Ended Reason
        livemode:
          description: False for a test session. Never act on a test session as though it were real.
          title: Livemode
          type: boolean
        session_id:
          format: uuid
          title: Session Id
          type: string
        status:
          description: 'The precise terminal status the two coarse event types flatten: `completed`, `gave_up`, `closed`, `max_turns`, `spend_cap_reached`, `balance_exhausted`. Open union: treat an unfamiliar value as ''ended somehow'' rather than failing.'
          title: Status
          type: string
        task:
          $ref: '#/components/schemas/SessionEventTask'
        turn_count:
          title: Turn Count
          type: integer
        url:
          description: 'Path to the session this event describes, relative to the API root, to be fetched under your own credentials. Relative on purpose: composed per response rather than frozen into the log, so a change of API host can never strand historical events holding a dead absolute link.'
          title: Url
          type: string
        workspace_id:
          format: uuid
          title: Workspace Id
          type: string
      required:
      - session_id
      - workspace_id
      - status
      - ended_reason
      - turn_count
      - livemode
      - task
      - url
      title: SessionEventObject
      type: object
    Error:
      description: Generic error envelope for 4xx/5xx responses other than request-shape validation.
      properties:
        detail:
          anyOf:
          - type: string
          - additionalProperties: true
            type: object
          description: A human-readable message, OR a structured object carrying a machine-readable `error_kind` (e.g. `idempotency_conflict`, `turn_index_mismatch`, `decision_invalid`).
          title: Detail
        docs_url:
          description: Link to this error's entry in the error catalog. Present whenever `detail` carries a registered `error_kind`; absent on unclassified errors.
          title: Docs Url
          type: string
        request_id:
          description: Correlation id; identical to the `X-Request-Id` response header. Quote it in support requests.
          title: Request Id
          type: string
      required:
      - detail
      - request_id
      title: Error
      type: object
    EventListResponse:
      description: One page of events, newest first.
      properties:
        has_more:
          description: True when more events exist beyond this page.
          title: Has More
          type: boolean
        items:
          items:
            $ref: '#/components/schemas/SessionEventResponse'
          title: Items
          type: array
        next_cursor:
          anyOf:
          - type: string
          - type: 'null'
          description: Opaque keyset cursor for the next page (pass as `after`); null on the last page. Treat it as opaque; its encoding is not part of this contract.
          title: Next Cursor
      required:
      - items
      - has_more
      - next_cursor
      title: EventListResponse
      type: object
    SessionEventData:
      properties:
        object:
          $ref: '#/components/schemas/SessionEventObject'
      required:
      - object
      title: SessionEventData
      type: object
  securitySchemes:
    bearerAuth:
      description: 'Workspace API key as a bearer token: `Authorization: Bearer ish_sk_live_...`. Keys are workspace-scoped machine principals minted in Settings > Developers (shown once at mint). Scopes: `sessions:run` (create a session, submit turns, close), `sessions:read` (read the session and its decision trace), `environments:read` and `environments:write` (manage the workspace''s registered environments), `tasks:read` and `tasks:write` (manage the workspace''s registered tasks), `people:read` and `people:write` (the people the workspace can run a session for), and `usage:read` (the workspace''s own consumption, spend limits and rate-limit budgets). A key is minted with `sessions:run`, `sessions:read`, and `tasks:read` by default; every other scope must be requested, `people:read` and `usage:read` included. An ish user access token also works on the same header for personal scripts.'
      scheme: bearer
      type: http