---
title: Agent Completions
description: Call the Sume Studio Agent like a model — messages or an instruction in, an async agent.run receipt out, with tools, media generation, and a sandbox.
---

An Agent Completion runs the Sume Agent on an ad-hoc prompt. Same runtime as the Agents chat UI —
full sandbox, tools, MCP bridge, media generation — reachable from your own backend with an API
key, with nobody watching.

Where a [schedule](/agents/actions) stores *what to do* and a [Format](/formats) stores *how
to do it*, an Agent Completion stores nothing. You send the task on every call.

## Which one do I want?

| Surface | Use it when | Start with |
|---|---|---|
| **[Format](/formats)** | You have a saved packaged workflow and only the inputs change. | `POST /v1/formats/{handle}/{slug}/runs` |
| **[Scheduled](/agents/actions)** | You need a schedule or a trigger on a saved automation. | `POST /v1/actions/{handle}/{slug}/runs` |
| **Agent Completions** | The task itself varies per call — you just want the Agent to do this thing. | `POST /v1/agent/completions` |

All three run the same agent and return the same receipt shape. They differ only in where the
instruction comes from and what Sume has saved on your behalf.

## Async, not a chat drop-in

An Agent Completion is **not** a synchronous chat completion. A real agent turn opens a sandbox,
calls tools, and may generate media, which takes far longer than an HTTP request should stay open.
So the create call returns `202` with a receipt and you poll it.

The request borrows OpenAI's `messages[]` shape so existing plumbing fits, but the response is a
run receipt, not `choices[]`. Streaming and a sync OpenAI-compatible wire are not available yet.

## Scopes

| Scope | Needed for |
|---|---|
| `agent_completions:read` | Read and list runs. |
| `agent_completions:write` | Create a completion, cancel a run. |

**Keys created before Agent Completions shipped do not carry these scopes.** An older key fails
every request with `403 insufficient_scope`, and scopes cannot be added to an existing key. Create
a new key at [API Keys](https://www.sume.com/dashboard/api-keys) and rotate to it — see
[Authentication](/authentication).

Service-account keys cannot create Agent Completions. They fail with `403 insufficient_scope` and
`details.reason` of `service_account_agent_completions_unsupported`.

## Create a completion

Fill in the required fields to rewrite the example. `generation_spend_cap_usd` has no default —
omit it and the request fails.

<!-- api-call-example:agent-completion -->

An accepted completion returns `202` with a receipt:

```json
{
  "data": {
    "id": "agrun_...",
    "object": "agent.run",
    "model": "sume-agent",
    "thread_id": "thr_...",
    "status": "queued",
    "status_url": "https://api.sume.com/v1/agent-runs/agrun_.../status",
    "cancel_url": "https://api.sume.com/v1/agent-runs/agrun_.../cancel",
    "created_at": "2026-08-01T16:00:00.000Z",
    "output": null,
    "artifacts": [],
    "usage": { "generation_spend_cap_usd_micros": 5000000 }
  }
}
```

### Request fields

| Field | Required | Notes |
|---|---|---|
| `instruction` | one of | The task, as a plain string. |
| `messages` | one of | `system` and `user` turns. Send exactly one of `instruction` or `messages`, never both. |
| `generation_spend_cap_usd` | **yes** | Ceiling for generation spend on this run. See below. |
| `model` | no | Only `sume-agent`. Omit it and you get the same agent. |
| `input` | no | Caller data, written whole to `/workspace/inputs/sume-action-input.json`; the prompt carries a bounded pointer at it. Treated as data, never as instructions. |
| `attachments` | no | Up to 30 images the agent can see and use. See [Attachments](#attachments). |
| `output_schema` | no | Bind the run's `output` to your own schema. Same contract as Action runs. |
| `primary_output_key` | no | Which `output` key holds the headline result. |
| `communication.webhook_url` | no | Public HTTPS URL notified when the run reaches a terminal status. See [Run webhooks](/agents/run-webhooks) — accepted, stored, and delivered on `api.dev.sume.com` and `api.sume.com`. |

`Idempotency-Key` behaves as it does on Action runs: replaying a key returns the original receipt
with `idempotency_hit: true`, and reusing it with a different payload returns `409
idempotency_conflict`.

### `messages[]`

```json
{
  "messages": [
    { "role": "system", "content": "Be terse." },
    { "role": "user", "content": "Summarize https://example.com/p" }
  ],
  "generation_spend_cap_usd": 2
}
```

`content` accepts a string or an OpenAI-style `[{ "type": "text", "text": "..." }]` array. Turns
are joined, in order, into one prompt.

`content` also accepts `{ "type": "input_text", "text": "..." }` as an alias for `text`, and
`{ "type": "input_image", ... }` parts — see [Attachments](#attachments).

`assistant` turns are **rejected**, not ignored. Accepting them would imply Sume replays a prior
conversation, which this endpoint does not do yet — every completion runs in a fresh thread. The
receipt's `thread_id` tells you which one.

## Attachments

Send images the agent can actually look at, either at the top level or as `input_image` content
parts. Both forms take the same item shape, and the two sources are merged into one list.

```bash
curl -sS -X POST "https://api.sume.com/v1/agent/completions" \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "messages": [
          {
            "role": "user",
            "content": [
              { "type": "input_text", "text": "Describe this product shot." },
              { "type": "input_image", "image_url": "https://cdn.example.com/shot.jpg" }
            ]
          }
        ],
        "output_schema": {
          "name": "caption",
          "schema": {
            "type": "object",
            "properties": { "caption": { "type": "string" } },
            "required": ["caption"],
            "additionalProperties": false
          }
        },
        "generation_spend_cap_usd": 2
      }'
```

An image-only turn is fine — omit the text part and the agent is told to use the attached files.

Attachments and `output_schema` compose: the images reach the agent, and the run's `output` is
still parsed against your schema after the run completes.

See [Format runs](/formats#attachments) for the item shape, limits, upload path, and error
codes — they are identical on both surfaces.

## The spend cap is required

`generation_spend_cap_usd` has no default. Omit it and the request fails with `400
invalid_request`.

This is deliberate. An Agent Completion is an unattended agent with tools and access to your
generation wallet, and the interactive spend-approval prompt that protects you in the chat UI is
not available to a backend caller. The cap is the substitute. Set it to the most you are willing
to spend on a single run, per run.

To size a cap, read the metered rates the run will draw against on the
[API pricing page](https://www.sume.com/pricing/api).

## Poll for the result

```bash
curl -sS "https://api.sume.com/v1/agent-runs/$RUN_ID" \
  -H "Authorization: Bearer $SUME_API_KEY"
```

Statuses match Action runs: `queued`, `processing`, `completed`, `failed`, `canceled`. Poll
`status_url` until `next_action` stops being `poll_status`.

A completed run fills `output` — by default the `sume/action-run-output/v1` shape, with the
Agent's closing text in `output.text` and any generated media in `output.images`, `output.videos`,
`output.audio`, and `output.files` — plus `artifacts` and the spend recorded in `usage`. Media
URLs are durable `media.sume.com` HTTPS URLs.

To stop a run in flight:

```bash
curl -sS -X POST "https://api.sume.com/v1/agent-runs/$RUN_ID/cancel" \
  -H "Authorization: Bearer $SUME_API_KEY"
```

`GET /v1/agent-runs` lists your completions, newest first.

## Errors

| Status | Code | Cause |
|---|---|---|
| `400` | `invalid_request` | Missing `generation_spend_cap_usd`, neither or both of `instruction`/`messages`, an `assistant` turn, or a malformed `input`. |
| `400` | `invalid_attachment` | Bad attachment item — wrong `type`, missing or non-HTTPS URL, both `image_url` and `asset_id`, or a source that is not an allowed image. |
| `400` | `attachment_not_found` | `asset_id` is unknown in this workspace. |
| `413` | `attachment_too_large` | An image is over 30 MB, or the set is over 500 MB total. |
| `502` | `attachment_fetch_failed` | Sume could not fetch the image — unreachable host, hotlink protection, or a non-2xx response. |
| `400` | `model_not_supported` | `model` was something other than `sume-agent`. |
| `403` | `insufficient_scope` | Key lacks `agent_completions:*`, or is a service-account key. |
| `404` | `agent_run_not_found` | Unknown run id, or a run belonging to another account. An Action or Format run id will not resolve here. |
| `409` | `idempotency_conflict` | `Idempotency-Key` reused with a different payload. |

## Not available yet

- Non-image attachments. `input_image` is the only `type` today; PDFs and other
  files follow later.
- Streaming, and a synchronous OpenAI-compatible `choices[]` response.
- Continuing a prior thread with `thread_id`, and `assistant` turns in `messages[]`.
- Team-owned threads. Completions are user-owned.
