Ellipsis API

Platform

List models and rates, manage sandbox variables and assets, and read alerts, analytics, budget, and usage over the REST API.

These endpoints cover the platform around your agents: the models they can run on, the variables and assets their sandboxes use, what is connected to your account, and what everything costs. Authenticate with your API key as a bearer token, like the rest of the API.

Models

GET /v1/models lists the models an agent may run on, most expensive first. Each entry carries the id you put in an agent config's claude.model, a display name, and its rate card in cents per million tokens. Exactly one model has is_default_agent_model: true: the one a config gets when it names no model. The rates are the same ones every sandbox request is priced with, so this list never drifts from what you are charged.

curl https://api.ellipsis.dev/v1/models \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN"

The response is { "models": [...] }:

{
  "models": [
    {
      "id": "claude-opus-5",
      "display_name": "Claude Opus 5",
      "is_default_agent_model": true,
      "rate_card": {
        "input_cents_per_1m_tokens": 500,
        "cache_write_5m_cents_per_1m_tokens": 625,
        "cache_write_1h_cents_per_1m_tokens": 1000,
        "cache_read_cents_per_1m_tokens": 50,
        "output_cents_per_1m_tokens": 2500
      }
    },
    {
      "id": "claude-haiku-4-5-20251001",
      "display_name": "Claude Haiku 4.5",
      "is_default_agent_model": false,
      "rate_card": {
        "input_cents_per_1m_tokens": 100,
        "cache_write_5m_cents_per_1m_tokens": 125,
        "cache_write_1h_cents_per_1m_tokens": 200,
        "cache_read_cents_per_1m_tokens": 10,
        "output_cents_per_1m_tokens": 500
      }
    }
  ]
}

Which provider serves each model depends on your Models setup; the ids are the same either way.

Sandbox variables

Sandbox variables are environment variables, stored once on your account, that an agent pulls into its sandbox at session start. Store a secret here, then reference it by name from an agent's sandbox.variables so the value 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 variable names but cannot create, update, or delete them: code running in a sandbox must not be able to change your stored secrets. PUT and DELETE require an API key or user token; a sandbox token is rejected with 403.

GET /v1/variables lists the stored variables.

curl https://api.ellipsis.dev/v1/variables \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN"

The response is { "variables": [...] }, where each entry has a name, created_at, and updated_at.

PUT /v1/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/variables \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
  -H "Content-Type: application/json" \
  -X PUT \
  -d '{
    "variables": [
      {"name": "NPM_TOKEN", "value": "npm_..."},
      {"name": "SPLITSHIFT_API_BASE_URL", "value": "https://api.splitshift.dev"}
    ]
  }'
FieldTypeDescription
namestringVariable name. Must start with a letter or underscore and contain only letters, digits, and underscores.
valuestringThe 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/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/variables/NPM_TOKEN \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
  -X DELETE

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"
KeyWhen connectedWhen not connected
githubaccount_login, account_type, repository_selection (all or selected), suspended, repository_countnull (the GitHub App was uninstalled)
slackteam_id, team_name, operations_channel_idnull
linearorganization_id plus teams, each with id, name, key, and is_enablednull
jiracloud_idnull
sentryA 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/repos returns { "repositories": [...] }: every repository connected to the installation, each with id, name, full_name, private, default_branch, and description. These are the valid values for repository on create config and for sandbox.repositories in an agent config.
  • GET /v1/github/members returns { "members": [...] }: the GitHub organization's roster (for a personal account, that account alone), each with id, login, name, avatar_url, and role (admin or member; null for a personal account). The id values are what author_id accepts on list and search sessions. When a member's Slack identity is linked, slack carries their slack_user_id and slack_email.
  • GET /v1/slack/channels returns { "team_id", "team_name", "channels": [...] }, each channel with id, name, is_private, and is_member. Listed live from Slack.
  • GET /v1/slack/members returns { "team_id", "team_name", "members": [...] }: the workspace's human members (bots are excluded), each with id, name, real_name, display_name, and email. Listed live from Slack. When a member's GitHub identity is linked, github carries their GitHub id and login, which lets you go from a Slack mention to the same person's sessions.
  • GET /v1/linear/teams returns { "organization_id", "teams": [...] }, each team with id, name, key, and is_enabled.
  • GET /v1/sentry/organizations returns { "organizations": [...] }, each with integration_id and organization_slug.

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.

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)\"
  }"
FieldTypeDescription
filenamestringOriginal basename, for display only.
content_typestringMust be image/png. The decoded bytes must actually be a PNG; the declared type is never trusted.
data_b64stringThe 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.

Two account-wide limits apply on top of that, and both return 429. An account can hold 50 assets at once, so deleting one frees a slot immediately. An account can upload 10 GiB in total, counted over every asset ever uploaded; deleted assets still count against it, so deleting does not free room against this one. Email team@ellipsis.dev to raise either limit.

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.png

DELETE /v1/assets/{asset_id} deletes an asset and returns 204 No Content. Afterward the asset is gone from both GETs, 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 How to add screenshots to every pull request for the end-to-end flow.

Alerts

Alerts are the notifications Ellipsis raises about your account; today they are all spend alerts. One fires when trailing spend crosses a threshold you set (80% and 100% of the account spend limit by default, plus any absolute dollar thresholds), and when sessions are being blocked because an account, per-agent, or per-developer spend limit is exhausted. The same alerts appear in the dashboard bell; these endpoints read them over the API, with any credential, so an agent can check "is this account near budget?" mid-task.

GET /v1/alerts lists alerts, newest first:

curl "https://api.ellipsis.dev/v1/alerts?status=open" \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN"
QueryTypeDescription
statusstringOnly alerts with this status: open, dismissed, or expired.
sourcestringOnly alerts from this source. budget is the only source today.
limitintegerMaximum alerts to return. Defaults to 100, capped at 200.

The response is { "alerts": [...] }:

{
  "alerts": [
    {
      "id": "alert_6Rw3nD8kPq2v",
      "customer_id": "cust_2Vb9tG4xLm7s",
      "created_at": "2026-07-30T09:00:14+00:00",
      "updated_at": "2026-07-30T09:00:14+00:00",
      "source": "budget",
      "severity": "info",
      "title": "7 day spend limit at 80%",
      "body": "You've defined an account-wide spend limit of $500.00 for trailing 7 day periods. You have currently spent $412.30, or 82%. Agent sessions won't start once you hit this limit. This email is just a warning notification. You can update your spend limit on the dashboard.",
      "cta_url": null,
      "cta_label": null,
      "dedupe_key": "budget:account:7:percent:80",
      "status": "open",
      "resolved_by": null,
      "resolved_at": null
    }
  ]
}

title is one line of plain text; body is markdown. severity is info for a warning threshold and warning once a limit is reached or sessions are blocked; it affects how the alert is displayed, nothing else. An alert about a specific agent carries a cta_url and cta_label pointing at that agent's config page. At most one open alert exists per condition, so a condition that persists does not pile up duplicates; when the condition clears (trailing spend falls back well under the threshold), Ellipsis resolves the alert itself and status becomes expired.

GET /v1/alerts/{alert_id} returns one alert in the same shape. An unknown or foreign id is 404.

POST /v1/alerts/{alert_id}/dismiss dismisses an open alert and returns it with status: "dismissed", resolved_at set, and resolved_by recording who dismissed it. Only an open alert can be dismissed: dismissing one that is already dismissed or expired is refused with 409.

curl https://api.ellipsis.dev/v1/alerts/alert_6Rw3nD8kPq2v/dismiss \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN" \
  -X POST

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. Passing both days and start is 400.

curl "https://api.ellipsis.dev/v1/analytics/metrics?days=90&account_type=bot" \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN"
  • GET /v1/analytics/metrics returns the org-wide picture: a per-day series, totals, per-repository usage in repositories, contributors and reviewers leaderboards, and the available_repos/available_authors filter facets. Filters: repo (owner/name, repeatable), author (pull request author login, repeatable), account_type (all, user, or bot, where bot means apps and agents), and status (open, draft, merged, closed, repeatable). The reviewers leaderboard with account_type=bot answers the which-apps-review-the-most question directly.
  • GET /v1/analytics/pull-requests returns pull request volume and trend with human-vs-bot splits per day: series, totals, facets, recent (newest first, capped), and truncated (true when the window hit the server's scan cap and the figures undercount). Filters: account_type (raw GitHub account types User/Bot, repeatable), repository_id, author_id, and status (all repeatable).
  • GET /v1/analytics/reviews returns review activity: reviews and review_comments feeds (newest first, capped), a per-day series, totals, and facets. Filters: repo (repeatable), author (reviewer login, repeatable), account_type, and review_state (APPROVED, CHANGES_REQUESTED, COMMENTED, repeatable).

The same data drives agent analytics reviewers|prs|reviews in the CLI.

Identity, budget, and usage

GET /v1/me returns who is behind the current credential: the customer_id and customer_login of the account, plus whichever of user_id (and the full gh_user account), api_key_id, and sandbox_id applies. A script can use it to confirm which account a key belongs to before doing anything else.

curl https://api.ellipsis.dev/v1/me \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN"

GET /v1/budget returns the account's spend in the current monthly billing window (anchored to your subscription anniversary) against its monthly budget, pre-computed so nothing needs client-side arithmetic:

curl https://api.ellipsis.dev/v1/budget \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN"
{
  "period": "monthly",
  "window": {
    "start": "2026-07-14T00:00:00+00:00",
    "end": "2026-08-14T00:00:00+00:00"
  },
  "budget_usd": 100.0,
  "spent_usd": 36.42,
  "remaining_usd": 63.58,
  "fraction_used": 0.3642,
  "pause_at_limit": true
}

GET /v1/usage returns the usage dashboard's data for the current billing period: period_start, period_end, total_tokens, and total_cost_millicents (costs are in millicents; 1000 millicents is $0.01), with prior_total_tokens and prior_total_cost_millicents for period-over-period comparison. daily holds one point per day, oldest first and zero-filled, each splitting tokens by type (tokens_input, tokens_output, tokens_cache_read, tokens_cache_creation) and cost into the four billed categories (cost_tokens_millicents, cost_sandbox_cpu_millicents, cost_sandbox_memory_millicents, cost_fee_millicents). by_model breaks the same categories down per model over the whole period, highest cost first.

curl https://api.ellipsis.dev/v1/usage \
  -H "Authorization: Bearer $ELLIPSIS_API_TOKEN"