API reference
REST API for starting sessions, searching session history, streaming output, and discovering integrations.
The Ellipsis REST API lets you start agent sessions, search and read session history, stream live output, discover your connected integrations, and inspect your agent configs and usage from your own scripts and services. It is the same /v1 surface the Ellipsis CLI uses.
The base URL is https://api.ellipsis.dev. Every endpoint returns JSON.
Get an API key
Create an API key under Platform → API keys in the Ellipsis dashboard. The secret is shown only once, so copy it when you create it.
There are two kinds of credential:
- API keys authenticate scripts and automations against the public API as your organization. Use these for CI pipelines, deploy hooks, and backend services.
- User tokens authenticate the Ellipsis CLI as you. The CLI mints one for you through
agent login, so you rarely create these by hand.
Store the key in an environment variable rather than committing it. ELLIPSIS_API_TOKEN is the same name the CLI reads, so one variable serves both:
export ELLIPSIS_API_TOKEN="ellipsis_key_..."Authentication
Authenticate with your API key sent as a bearer token:
curl https://api.ellipsis.dev/v1/me \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"A request without a valid Authorization: Bearer header is rejected with 401.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /v1/me | The customer, user, and API key behind the current credential. |
GET | /v1/budget | Current spend and the configured run, daily, weekly, and monthly limits. |
GET | /v1/usage | Usage dashboard data for the account. |
POST | /v1/sessions | Start an agent session. |
GET | /v1/sessions | List agent sessions. |
GET | /v1/sessions/search | Search sessions by step text, recap text, created PR, or similarity. |
GET | /v1/sessions/{session_id} | Fetch a single agent session. |
GET | /v1/sessions/{session_id}/steps | Read a session's rendered steps. |
GET | /v1/sessions/{session_id}/turns | Read a durable conversation's turns and messages. |
GET | /v1/sessions/{session_id}/transcripts | Download a session's raw transcript files. |
POST | /v1/sessions/{session_id}/replay | Re-run a finished session. |
POST | /v1/sessions/{session_id}/stop | Stop an in-flight session. |
WS | /v1/sessions/{session_id}/stream | Stream a session's live output over WebSocket. |
POST | /v1/sessions/sync | Ingest a Claude Code session from a developer's machine. |
POST | /v1/configs | Create an agent config by opening a pull request. |
GET | /v1/configs | List the account's saved agent configs. |
GET | /v1/configs/{config_id} | Fetch a single saved agent config. |
GET | /v1/defaults | List the account and per-repository default agents. |
PUT | /v1/defaults | Set the account default agent, or one repository's. |
DELETE | /v1/defaults | Clear the account default agent, or one repository's. |
GET | /v1/templates | List the built-in starter templates. |
GET | /v1/templates/{template_id} | Fetch one template by slug. |
GET | /v1/sandboxes/variables | List the account's sandbox environment variable names. |
PUT | /v1/sandboxes/variables | Create or update sandbox environment variables. |
DELETE | /v1/sandboxes/variables/{name} | Delete a sandbox environment variable. |
GET | /v1/integrations | Every connected integration, in one call. |
GET | /v1/github/repos | Repositories connected to the GitHub installation. |
GET | /v1/github/members | The GitHub org roster, with linked Slack identities. |
GET | /v1/slack/channels | Channels in the connected Slack workspace. |
GET | /v1/slack/members | Members of the connected Slack workspace, with linked GitHub identities. |
GET | /v1/linear/teams | Teams in the connected Linear organization. |
GET | /v1/sentry/organizations | Connected Sentry organizations. |
GET | /v1/analytics/metrics | Pull request and review analytics: totals, trends, and leaderboards. |
GET | /v1/analytics/pull-requests | Pull request volume and trends, split human vs bot. |
GET | /v1/analytics/reviews | Review activity: totals, verdicts, and top reviewers. |
POST | /v1/assets | Upload an image an agent can link on a pull request. |
GET | /v1/assets | List the account's assets, newest first. |
GET | /v1/assets/{asset_id} | Fetch one asset, with a short-lived download URL. |
DELETE | /v1/assets/{asset_id} | Delete an asset. |
Start an agent session
POST /v1/sessions starts a session from a saved config, an inline config, a built-in template, or no config source at all. Pass config_id to run a saved agent, config to run an inline agent config, or template_id to run a template. A config source is optional: with none, the session resolves your team's default agent: the repository field's repo default if one is set, else the account default, else a bare ad-hoc config (an empty system prompt on claude-opus-4-8, enabled, no triggers, default sandbox and limits). The prompt is the per-session instruction either way.
curl https://api.ellipsis.dev/v1/sessions \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"config_id": "agent_x9Kd3Fq2",
"metadata": {"trigger": "nightly-batch"}
}'| Field | Type | Description |
|---|---|---|
config_id | string | ID of a saved agent config to run. |
config | object | An inline agent config to run instead of a saved one. |
template_id | string | Slug of a built-in template to run, e.g. welcome-to-ellipsis. |
metadata | object | String key/value pairs recorded on the session. |
config_override | object | A partial config merged onto the config named by config_id or template_id, or onto the resolved default when no source is given, then re-validated, e.g. {"budget": {"session": 5.0}} to set just this session's budget. With an inline config, fold the override into the config you send instead. |
config_override_yaml | string | The same override as a YAML/JSON string, e.g. read from a file. Pass one of config_override or config_override_yaml, not both. |
prompt | string | Per-session instructions, appended to the initial prompt after the config's system prompt. Use it to steer a single session without changing the config. With no config source, it is the session's sole instruction. |
idle_start | boolean | Start the session with no initial message: the sandbox spins up and the agent waits idle for the first message sent to it, which opens the conversation the way typing into a local claude does. This is what a promptless agent in the CLI sends. Cannot be combined with prompt or handoff; ignored when the resolved config sets interactive: false (that session runs its workflow kickoff as usual, since it can never receive a message). |
repository | string | The repository the client is standing in, as owner/name (the CLI sends its origin remote automatically). With no config source it selects that repository's default agent; either way the repository is cloned into the sandbox at its default branch, on top of the config's own sandbox.repositories. A repository outside your installation is ignored, with no error. |
handoff | object | Laptop-to-cloud handoff parameters: parent_session_id (the synced laptop session to continue), repo, sha (the pushed working-tree commit the sandbox checks out), and optional ref. The session runs the built-in Handoff agent, so omit config_id, config, and template_id. |
Pass at most one of config_id, config, or template_id, or omit all three to resolve the default agent. A handoff runs the built-in handoff config and cannot be combined with any config source or config override. The response is the created agent session, including its id and status, plus two resolution fields: resolved_config_name (the name of the config the session runs under) and resolution_source (repo_default, account_default, none for a bare ad-hoc start, or null when an explicit config source bypassed resolution). A default that is set but broken (its config was deleted, disabled, or its pull request has not merged) fails the request with 409 and a message naming the setting, never a silent fallback. A session's source is set automatically from your credential (an API key records api), so you can tell apart sessions started via the API, the CLI, the dashboard, or a webhook when you list them.
List agent sessions
GET /v1/sessions returns recent sessions, newest first. All query parameters are optional.
curl "https://api.ellipsis.dev/v1/sessions?config_id=agent_x9Kd3Fq2&limit=20" \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"| Query | Type | Description |
|---|---|---|
config_id | string | Only sessions for this config. |
source | string | Filter by session source. Repeat the parameter to pass several. |
author_id | integer | Only sessions attributed to this developer, by GitHub account id. Resolve ids with GET /v1/github/members. |
days | integer | Only sessions from the last N days. |
start | datetime | Only sessions at or after this timestamp. |
end | datetime | Only sessions at or before this timestamp. |
limit | integer | Maximum sessions to return. Defaults to 50. |
The response is { "sessions": [...] }.
Search sessions
GET /v1/sessions/search searches everything a session leaves behind and returns matches grouped by session, newest first. One query runs up to four match arms, and each result says which arms hit:
matched value | The session matched because |
|---|---|
steps | Every term in q appears in one of its steps. |
recap | Every term in q appears in its recap (the summary and retrospective written when it finished). |
pr | q is a pull request reference, and this session created that pull request. |
similar | Its recap is semantically close to q, even with no exact word overlap. |
Terms in q are split on whitespace and matched case-insensitively; a session matches the steps or recap arm only if every term is found. A PR-shaped q (#512, splitshift-hq/splitshift-api#512, or a full PR URL) additionally matches the session that created that exact pull request, so you never depend on the URL appearing in step text.
curl "https://api.ellipsis.dev/v1/sessions/search?q=shift+trade+webhook&source=laptop&author_id=5201153" \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"| Query | Type | Description |
|---|---|---|
q | string | Required. At least 3 characters. |
scope | string | steps, recaps, or both. Defaults to both. recaps skips step text; steps skips recap text and similarity. |
source | string | Filter by session source (laptop, cron, react, mention, manual, api, cli). Repeatable. |
author_id | integer | Sessions attributed to these GitHub account ids. Repeatable, and combined with agent_config_id as one author-or-agent filter. |
agent_config_id | string | Sessions run by these agent configs. Repeatable. |
session_ids | string | Restrict the search to these sessions. Repeatable. |
repo | string | Sessions on this repository: owner/name for an exact match, or a bare name like splitshift-api. |
status | string | Filter by session status. Repeatable. |
start / end | datetime | Only sessions inside this window. |
limit | integer | Maximum result sessions. Defaults to 20, capped at 100. |
The response is { "results": [...], "attributed_users": {...} }. Each result carries:
| Field | Type | Description |
|---|---|---|
session | object | The full agent session, including its status, recap output, and created PRs. |
matched | array | Which arms matched: steps, recap, pr, similar. |
recap_snippet | string | A short window of the recap text around the first match. Present when the recap arm matched. |
step_hits | array | Up to 5 matching steps, each with a snippet, step_type, step_index, and created_at, plus session context. |
step_hit_count | integer | How many steps matched in total, so you can render "and N more". |
attributed_users maps each result's attribution_id to a GitHub account (id, login, type, avatar_url) so you can show who ran the session without a second lookup.
A typical investigation composes three calls: resolve a teammate's GitHub account id with GET /v1/github/members, search with q, author_id, and a date window, then read the winning session's recap with GET /v1/sessions/{session_id} and its full steps with GET /v1/sessions/{session_id}/steps.
Fetch a session
GET /v1/sessions/{session_id} returns one session, including its status, cost, tokens, config snapshot, and output.
curl https://api.ellipsis.dev/v1/sessions/session_7Hq2mX4p \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"A session's status is one of scheduled, creating_sandbox, running, retrying, completed, error, cancelled, or stopped. Terminal sessions also carry an exit_status explaining why they ended, e.g. completed, budget_hit, or tool_call_failed.
For a durable conversation, status describes only the most recent turn's execution — it returns to scheduled and running each time a new message wakes the conversation. The conversation's own lifecycle is session_state: running (a turn is executing), idle (waiting for the next message), or closed (ended by its surface, e.g. a merged PR). Single-shot sessions have session_state: null, so it also tells you whether a session is a conversation at all. last_message_at records when the most recent message arrived.
Read a session's steps
GET /v1/sessions/{session_id}/steps returns the session's full stored steps: every event the agent produced, in order.
curl https://api.ellipsis.dev/v1/sessions/session_7Hq2mX4p/steps \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"The response is { "steps": [...] }, ordered by created_at then step_index. Each step is one event from the agent's run: an assistant turn, a tool call, a tool result, or the final result. The step_type says which, and data carries the full event payload, including message text, tool names and inputs, and tool output. Use it after search points you at a session: the search hit gives you the step_index to read around.
Steps from laptop sessions are parsed from a transcript that is redacted on the developer's machine before it is synced, so they never contain more than what the session's owner uploaded.
Read a conversation's turns
GET /v1/sessions/{session_id}/turns returns a durable conversation's structure: the messages its surface delivered and the turns that answered them. For a single-shot session both lists are empty.
curl https://api.ellipsis.dev/v1/sessions/session_7Hq2mX4p/turns \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"The response is { "turns": [...], "messages": [...] }. Turns are ordered by turn_index and each carries its own status, exit_status, timing, cost breakdown, and token totals, plus the react_action of the event that scheduled it. Messages are the conversation's inbox, oldest first: each has the rendered text the agent received (body), when it arrived, and delivered_turn_id, the turn that consumed it, or null while it is still waiting for one. A message with closes_session: true is the conversation's final message. The per-turn work itself (tool calls, output) lives in the steps.
Download a session's raw transcripts
GET /v1/sessions/{session_id}/transcripts returns download URLs for the session's raw transcript files: the unparsed Claude Code event stream, one gzipped JSONL file per process. A session that retried has one file per attempt, in order; the failed attempt's transcript is preserved. Where steps is the parsed view, this is the original artifact, suitable for archiving or for tooling that consumes the stream format directly.
curl https://api.ellipsis.dev/v1/sessions/session_7Hq2mX4p/transcripts \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"The response is { "session_id": ..., "transcripts": [...] }. Each entry carries the process_id, the format (claude_stream_json for cloud sessions, claude_transcript for synced laptop sessions), event_count, compressed bytes, written_at, a write_status, and a download_url.
The download_url is a presigned link that expires after expires_in seconds (60): fetch this endpoint, then GET the URL immediately — the JSON API never carries the file itself. The body is gzipped JSONL:
curl -s "$DOWNLOAD_URL" | gunzip > transcript.jsonlThree states to know about. A running session serves its transcript so far (write_status: null — the file grows as the session runs). A write_status of "failed" means the session's final flush did not land: the file is still usable but may be missing its tail. And a transcript past your log retention setting has been deleted — its download_url returns a 404 from storage.
A session with no stored transcripts returns an empty list. By default it reuses the original session's config snapshot; pass config_id, config_override or config_override_yaml, or prompt to rerun with changes, e.g. a higher budget.session budget.
curl https://api.ellipsis.dev/v1/sessions/session_7Hq2mX4p/replay \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
-X POSTStop a session
POST /v1/sessions/{session_id}/stop stops an in-flight session. The response is the session with status set to stopped.
curl https://api.ellipsis.dev/v1/sessions/session_7Hq2mX4p/stop \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
-X POSTStream a session
GET /v1/sessions/{session_id}/stream upgrades to a WebSocket and streams the session's live output. Pass the bearer token in the Authorization header, or as a ?token= query parameter when a header is not available (for example from a browser).
websocat "wss://api.ellipsis.dev/v1/sessions/session_7Hq2mX4p/stream?token=$ELLIPSIS_API_TOKEN"Each frame is a JSON object with a type:
| Type | Fields | Meaning |
|---|---|---|
status | status, session, run, cost_millicents, ts | The session's status or running cost changed. cost_millicents is the total cost so far; it climbs during a turn as the session spends. |
stdout / stderr | data, seq, ts | A line of output. seq increases monotonically. |
delta | text, output_tokens | In-progress assistant output: a streamed text fragment and the running token count for the current response. Ephemeral, no seq; the completed output arrives again as a stdout frame. |
done | status, session, run, exit_status, cost_millicents | The session finished. |
error | message | A server-side stream error. |
To resume after a disconnect without missing or duplicating output, reconnect with ?after_seq=<last_seq>, where <last_seq> is the highest seq you received. delta frames are not replayed on resume.
Sync a laptop session
POST /v1/sessions/sync ingests one Claude Code session from a developer's machine. It is the endpoint behind local session sync; call it directly only to build your own uploader. It requires a user token from agent login, not an API key: synced sessions attribute to the developer who ran them, and one developer's sync can never overwrite another's session.
| Field | Type | Description |
|---|---|---|
cc_session_id | string | Required. The Claude Code session uuid. One Ellipsis session exists per uuid per developer; syncs upsert it. |
transcript_gzip_b64 | string | Required. The transcript JSONL, redacted, gzipped, then base64-encoded. Caps: 8 MiB compressed, 64 MiB decompressed. |
reason | string | Which hook fired: stop (mid-session; the session shows running) or session_end (marks it completed). Defaults to stop. |
repo | string | The repository the session ran in, as owner/name. |
cwd | string | The session's working directory. |
git_branch | string | The checked-out branch. |
The response is { "session_id": ..., "process_id": ..., "event_count": ..., "accepted": ... }. accepted is false when the stored transcript was already at least as long: the longest snapshot wins, so repeated syncs of one session (including across claude --resume) are acknowledged without duplicating anything. After an accepted sync, the transcript is parsed into steps asynchronously.
List agent configs
GET /v1/configs returns the account's saved agent configs, including each config's GitHub source, last session, and any pending "store in GitHub" pull request.
curl https://api.ellipsis.dev/v1/configs \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"The response is { "configs": [...] }. Fetch one config with GET /v1/configs/{config_id}.
POST /v1/configs creates an agent the same way the dashboard does: it opens a pull request adding the YAML file to a repository you name, and the agent goes live when that pull request merges. Pass exactly one of config (an inline agent config) or template_id, plus the target repository (a bare repo name in your account) and an optional path (defaults to agents/<slug>.yaml).
Default agents
The default agent a session runs when you name no config source is a pointer to a saved config, set account-wide or per repository. These endpoints manage those pointers. They are addressed by rung, never by row id: the account default is repository omitted (or null), a repository default is repository as "owner/name".
GET /v1/defaults lists every rung that is set.
curl https://api.ellipsis.dev/v1/defaults \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"The response is { "defaults": [...] }. Each entry has the rung's repository ("owner/name", or null for the account default), the pointed-at config_id and config_name, updated_at, and broken: null when the pointer can serve sessions, else why it cannot (config_deleted, config_disabled, config_pending_pr, or repo_inaccessible). A broken rung fails session starts with 409 rather than silently running a different agent, so fix or clear it.
PUT /v1/defaults sets a rung's pointer, replacing whatever it pointed at before.
curl https://api.ellipsis.dev/v1/defaults \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
-H "Content-Type: application/json" \
-X PUT \
-d '{"repository": "acme/api", "config_id": "agent_x9Kd3Fq2"}'| Field | Type | Description |
|---|---|---|
repository | string | The repository whose default to set, as owner/name. Omit to set the account default. A repository outside your installation is rejected with 404 (unlike start session's repository context, which is silently ignored). |
config_id | string | The saved config to point the rung at. Only a live, synced config is accepted: an unknown or deleted id is 404, a disabled config or one whose pull request has not merged is 400. |
The response is the updated rung, in the same shape GET returns.
DELETE /v1/defaults clears the account default; DELETE /v1/defaults?repository=owner/name clears that repository's. Clearing a rung that is not set returns 404.
curl "https://api.ellipsis.dev/v1/defaults?repository=acme/api" \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
-X DELETEThe in-sandbox token an agent uses to reach the API can read the defaults ladder but cannot set or clear it: code running in a sandbox must not be able to repoint which agent your account runs by default. PUT and DELETE require an API key or user token; a sandbox token is rejected with 403.
Templates
GET /v1/templates lists the built-in starter templates, each with its slug, description, and complete YAML. Fetch one with GET /v1/templates/{template_id}. A template slug works anywhere a config does: template_id on start session runs it once, and on create config it opens the pull request that deploys it.
Sandbox variables
Sandbox variables are environment variables, stored once on your account, that an agent can pull into its sandbox at session start. Store a secret or shared config value here, then reference it by name from an agent's sandbox.variables so the secret never lives in your config file. Only the variables an agent names reach that agent's sandbox.
Values are write-only. You upload them, but the API never returns them. Listing a variable shows its name and timestamps, never its value.
The in-sandbox token an agent uses to reach the API can list sandbox variables but cannot create, update, or delete them: code running in a sandbox can see which variables exist but cannot change your stored secrets. PUT and DELETE require an API key or user token; a sandbox token is rejected with 403. (The GET works with any credential.)
GET /v1/sandboxes/variables lists the stored variables, sorted by name.
curl https://api.ellipsis.dev/v1/sandboxes/variables \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"The response is { "variables": [...] }, where each entry has a name, created_at, and updated_at.
PUT /v1/sandboxes/variables creates or updates one or more variables in a single request. Each variable is upserted by name: an existing name is overwritten, a new name is created, and variables you do not send are left untouched.
curl https://api.ellipsis.dev/v1/sandboxes/variables \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
-H "Content-Type: application/json" \
-X PUT \
-d '{
"variables": [
{"name": "NPM_TOKEN", "value": "npm_..."},
{"name": "API_BASE_URL", "value": "https://example.com"}
]
}'| Field | Type | Description |
|---|---|---|
name | string | Variable name. Must start with a letter or underscore and contain only letters, digits, and underscores. |
value | string | The value to store. |
Names are validated before anything is written, so one invalid name rejects the whole request with 400 and stores nothing. An account can hold up to 500 variables; a request that would exceed that is rejected with 400. Both GET and PUT return the full, current variable list.
DELETE /v1/sandboxes/variables/{name} removes one variable by name and returns the remaining list. Deleting a name that does not exist still succeeds.
curl https://api.ellipsis.dev/v1/sandboxes/variables/NPM_TOKEN \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
-X DELETEDiscover integrations
These read-only endpoints let a script (or an agent writing another agent's config) learn what is connected to the account: which repositories, channels, teams, and people a config can name. They work with any credential, including the in-sandbox token, and never return OAuth tokens or other secrets.
GET /v1/integrations returns everything in one call:
curl https://api.ellipsis.dev/v1/integrations \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"| Key | When connected | When not connected |
|---|---|---|
github | account_login, account_type, repository_selection (all or selected), suspended, repository_count | null (the GitHub App was uninstalled) |
slack | team_id, team_name, operations_channel_id | null |
linear | organization_id plus teams, each with id, name, key, and is_enabled | null |
jira | cloud_id | null |
sentry | A list of organizations, each with integration_id and organization_slug | [] |
The per-provider endpoints return the resources an agent config can reference:
GET /v1/github/reposreturns{ "repositories": [...] }: every repository connected to the installation, each withid,name,full_name,private,default_branch, anddescription. These are the valid values forrepositoryon create config and forsandbox.repositoriesin an agent config.GET /v1/github/membersreturns{ "members": [...] }: the GitHub organization's roster (for a personal account, just that account), each withid,login,name,avatar_url, androle(adminormember;nullfor a personal account). Theidvalues are whatauthor_idaccepts on list and search. When a member's Slack identity is linked,slackcarries theirslack_user_idandslack_email.GET /v1/slack/channelsreturns{ "team_id", "team_name", "channels": [...] }, each channel withid,name,is_private, andis_member. Listed live from Slack.GET /v1/slack/membersreturns{ "team_id", "team_name", "members": [...] }: the workspace's human members (bots are excluded), each withid,name,real_name,display_name, andemail. Listed live from Slack. When a member's GitHub identity is linked,githubcarries their GitHubidandlogin, which lets you go from a Slack mention to the same person's sessions.GET /v1/linear/teamsreturns{ "organization_id", "teams": [...] }, each team withid,name,key, andis_enabled.GET /v1/sentry/organizationsreturns{ "organizations": [...] }, each withintegration_idandorganization_slug. An empty list means Sentry is not connected.
The Slack and Linear endpoints return 404 when that integration is not connected. GitHub endpoints always work (an Ellipsis account is a GitHub account), and Sentry returns an empty list instead of 404 because the organization is the connection.
Analytics
The /v1/analytics endpoints return the same aggregation behind the dashboard's Analytics pages, so a script or an agent can answer questions like "which apps review the most pull requests?" over plain HTTP. They work with any credential and expose only the account's own GitHub pull request and review activity.
All three share the same windowing: pass days to look back N days (default 30), or an explicit start/end.
curl "https://api.ellipsis.dev/v1/analytics/metrics?days=90&account_type=bot" \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"GET /v1/analytics/metricsreturns the org-wide picture: a per-dayseries,totals, per-repository usage,contributorsandreviewersleaderboards, and the repo/author facets. Filters:repo(owner/name, repeatable),author(pull request author login, repeatable),account_type(all,user, orbot, wherebotmeans apps and agents), andstatus(open,draft,merged,closed, repeatable). Thereviewersleaderboard withaccount_type=botanswers the which-apps-review-the-most question directly.GET /v1/analytics/pull-requestsreturns pull request volume and trend with human-vs-bot splits per day:series,totals,facets,recent(newest first, capped), andtruncated(truewhen the window hit the server's scan cap and the figures undercount). Filters:account_type(raw GitHub account typesUser/Bot, repeatable),repository_id,author_id, andstatus(all repeatable).GET /v1/analytics/reviewsreturns review activity:reviewsandreview_commentsfeeds (newest first, capped), a per-dayseries,totals, andfacets. Filters:repo(repeatable),author(reviewer login, repeatable),account_type, andreview_state(APPROVED,CHANGES_REQUESTED,COMMENTED, repeatable).
The same data drives agent analytics reviewers|prs|reviews in the CLI.
Assets
An asset is a file an agent persists past its sandbox's lifetime, so a session can show its work: upload a screenshot, get back a link, paste the link on a pull request. v1 accepts PNG images only.
POST /v1/assets uploads one image, base64-encoded in the JSON body:
curl https://api.ellipsis.dev/v1/assets \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"filename\": \"schedule-grid.png\",
\"content_type\": \"image/png\",
\"data_b64\": \"$(base64 -i schedule-grid.png)\"
}"| Field | Type | Description |
|---|---|---|
filename | string | Original basename, for display only. |
content_type | string | Must be image/png. The decoded bytes must actually be a PNG; the declared type is never trusted. |
data_b64 | string | The file bytes, base64-encoded. 10 MiB cap per upload. |
The 201 response is { "asset": {...}, "url": "https://app.ellipsis.dev/assets/{asset_id}" }. The url is the link to paste: it is gated by org membership, so only members of the owning organization can open it, and anyone else gets a 404. Uploads from a sandbox record the originating session on asset.agent_session_id and are capped at 100 assets and 100 MiB per session.
GET /v1/assets lists the account's assets newest first, metadata only. agent_session_id scopes the list to one session's uploads; limit defaults to 50. GET /v1/assets/{asset_id} returns the metadata, the gated url, and a download_url: a presigned link for the raw bytes, valid for 60 seconds, so fetch it and download immediately.
curl -s https://api.ellipsis.dev/v1/assets/9f2c4b7d1e8a4c02b6f3a1d5e7c90842 \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN" | jq -r .download_url | xargs curl -s -o schedule-grid.pngDELETE /v1/assets/{asset_id} deletes an asset and returns 204 No Content. Afterward the asset is gone from GET /v1/assets and GET /v1/assets/{asset_id}, and its gated url stops resolving. A missing id, another account's id, and an already-deleted id all return an identical 404, so ids can't be enumerated. Sandbox tokens get a 403: an agent can upload but not delete, so deletion is reserved for API keys and user tokens.
curl -X DELETE https://api.ellipsis.dev/v1/assets/9f2c4b7d1e8a4c02b6f3a1d5e7c90842 \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"See Attach agent screenshots to pull requests for the end-to-end flow.
Identity, budget, and usage
GET /v1/me returns the customer, user, and API key behind the current credential. GET /v1/budget returns the account's current spend against its run, daily, weekly, and monthly limits. GET /v1/usage returns the usage dashboard: per-day and per-model token and cost breakdowns for the current period, with a prior-period comparison.
curl https://api.ellipsis.dev/v1/budget \
-H "Authorization: Bearer $ELLIPSIS_API_TOKEN"