> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omnicommerce.sg/llms.txt
> Use this file to discover all available pages before exploring further.

# Omni agent API

> Call the general Omni agent synchronously, or start a run and poll it.

Use the developer agent API to send a conversation to the general Omni agent. The same request body works for a call that waits and for a call that returns immediately.

Interactive playground: [API reference](/api-reference) · Spec: [openapi.json](/openapi.json)

## Authentication

```http theme={null}
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json
```

| Endpoint | Required scope |
| - | - |
| `POST /api/v1/agents/general/invoke` | `agents:invoke` |
| `POST /api/v1/agents/general/runs` | `agents:invoke` |
| `GET /api/v1/agents/general/runs/{runId}` | `agents:invoke` |

<Note>
  API keys created from organization settings receive all developer scopes by
  default. Existing keys created before this API shipped need `agents:invoke`
  added in organization settings.
</Note>

OAuth clients must include `agents:invoke` on the client. Session-authenticated calls include `organizationId` in the JSON body for writes and in the query string for the run lookup. API-key requests omit `organizationId`.

## Wait for the result

```http theme={null}
POST /api/v1/agents/general/invoke
```

The call stays open until the agent completes, pauses for approval, or fails. The route allows up to 800 seconds. TypeScript and Python callers set a client timeout of about 780 seconds. The CLI uses 780 seconds for this command when `--timeout` is omitted.

```bash theme={null}
curl --max-time 780 -X POST "https://omnicommerce.sg/api/v1/agents/general/invoke" \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      { "role": "user", "content": "How many products are missing a price?" }
    ]
  }'
```

A finished or paused run returns `200`:

```json theme={null}
{
  "run_id": "run_1",
  "thread_id": "thread_1",
  "status": "completed",
  "messages": [
    {
      "type": "ai",
      "role": "assistant",
      "content": "Twelve products have no price."
    }
  ],
  "structured_output": null,
  "approvals": null,
  "error": null
}
```

Pass the returned `thread_id` on the next call to continue the same conversation.

## Start and poll

```http theme={null}
POST /api/v1/agents/general/runs
```

This queues the same agent and returns `202` immediately:

```bash theme={null}
curl -X POST "https://omnicommerce.sg/api/v1/agents/general/runs" \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "thread_id": "orders-today",
    "messages": [
      { "role": "user", "content": "Summarize orders placed today." }
    ]
  }'
```

```json theme={null}
{
  "run_id": "run_1",
  "thread_id": "orders-today",
  "status": "queued",
  "messages": [],
  "structured_output": null,
  "approvals": null,
  "error": null
}
```

The `202` body is the run itself in `queued` state. The `Location` header points at the run, and `Retry-After` gives the seconds to wait before polling. Poll responses keep sending `Retry-After` while the run is `queued` or `running`.

Poll with the run id:

```bash theme={null}
curl "https://omnicommerce.sg/api/v1/agents/general/runs/run_1" \
  -H "Authorization: Bearer $OMNI_API_KEY"
```

`queued` and `running` responses have an empty `messages` array. `completed`, `awaiting_approval`, and `failed` include the stored result. A sync invoke is stored too, so the same GET works after a waiting call.

A thread can have one `queued` or `running` run. A second start or invoke on that thread returns `409` until the active run finishes. A run that stops reporting progress is marked `failed` after 20 minutes running, or after an hour still queued, so a crashed worker cannot hold a thread.

Thread ids belong to your organization. The same `thread_id` in another organization is a different conversation, and API threads never continue in-app chats.

## Request body

| Field | Required | Description |
| - | - | - |
| `messages` | Turn | Up to 50 messages. The last message is from the user. |
| `decisions` | Resume | Up to 20 `{ "action": "approve" \| "reject", "reason" }` objects. Exclusive of `messages`. |
| `thread_id` | Resume | Continue a conversation. Required with `decisions`. Omitted on a new turn, Omni assigns one. |
| `channel` | No | Label for the calling channel. Defaults to `api`. |
| `config` | No | Optional `enabled_tools`, `model`, `system_prompt`, and `structured_output`. |
| `organizationId` | Session | Required for session auth. |

Message `content` is a string or an array of parts. A text part is `{ "type": "text", "text" }`. An image part is `{ "type": "image", "url", "filename"? }`. `role` is `user`, `assistant`, or `system`. When `role` is omitted it is `user`.

Image parts belong on the latest user message. `url` is an already uploaded `http` or `https` address. A message can contain up to 20 parts and can be images only. The same image parts work on invoke and on start.

```bash theme={null}
curl --max-time 780 -X POST "https://omnicommerce.sg/api/v1/agents/general/invoke" \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "role": "user",
        "content": [
          { "type": "text", "text": "What product is this?" },
          { "type": "image", "url": "https://cdn.example.com/shoe.jpg" }
        ]
      }
    ]
  }'
```

`config.enabled_tools` limits the run to named Omni agent tools. Omit it to use the organization's agent tools. An empty array runs with no tools. Unknown tool names return `400`. `config.system_prompt` replaces the agent prompt for that call. `config.structured_output` is a JSON Schema object (`"type": "object"`). When the agent completes, `structured_output` in the response is the value that matches that schema. A schema mismatch fails the run.

## Approvals

A tool that needs approval returns `200` from invoke, or a polled run, with `status: "awaiting_approval"`. `approvals` lists each pending action:

```json theme={null}
{
  "interrupt_instance_id": "interrupt_1",
  "tool_name": "update_product",
  "tool_args": { "productId": "prod_1" },
  "message": "Update the product price."
}
```

Resume on the same `thread_id` with decisions and no new messages. One decision applies to every pending action. Otherwise send one decision per pending action, in order. Decisions on a thread whose latest run is not `awaiting_approval` return `409`.

```bash theme={null}
curl --max-time 780 -X POST "https://omnicommerce.sg/api/v1/agents/general/invoke" \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "thread_id": "thread_1",
    "decisions": [{ "action": "approve" }]
  }'
```

The same body works on `POST /api/v1/agents/general/runs`.

## Status values

| Status | Meaning |
| - | - |
| `queued` | Accepted and waiting for a worker. |
| `running` | The agent is working. |
| `completed` | The agent finished. `messages` and any `structured_output` are set. |
| `awaiting_approval` | The agent paused. Send `decisions` on this `thread_id`. |
| `failed` | The run failed. `error` explains why. |

## Errors

| Status | Meaning |
| - | - |
| `400` | Invalid body, an unknown tool or model, or a decision count that does not match the pending approvals. |
| `401` | Missing or invalid credentials. |
| `402` | The organization is out of AI credits. |
| `403` | The key lacks `agents:invoke` or Assistant access. |
| `404` | The run id is not in this organization. |
| `409` | This thread already has an active run, or decisions were sent while no run awaits approval. |
| `500` | The agent failed, including a structured-output mismatch. |
| `504` | The waiting call reached its time limit. |

Error bodies use the standard developer API envelope. When a run was created before the failure, `data.run_id` and `data.thread_id` identify it, so `GET /api/v1/agents/general/runs/{runId}` returns the stored transcript.

## SDK and CLI

```ts theme={null}
const run = await client.agents.general.invoke(
  {
    messages: [
      { role: "user", content: "How many products are missing a price?" },
    ],
  },
  { timeoutInSeconds: 780 },
);

const started = await client.agents.general.start({
  messages: [{ role: "user", content: "Summarize orders placed today." }],
});
const polled = await client.agents.general.getRun({ runId: started.run_id });
```

```python theme={null}
from omnicommerce import RequestOptions

run = client.agents.general.invoke(
    messages=[{"role": "user", "content": "How many products are missing a price?"}],
    request_options=RequestOptions(timeout_in_seconds=780),
)

started = client.agents.general.start(
    messages=[{"role": "user", "content": "Summarize orders placed today."}],
)
polled = client.agents.general.get_run(run_id=started["run_id"])
```

```bash theme={null}
omni agents general invoke --json '{"messages":[{"role":"user","content":"How many products are missing a price?"}]}'
omni agents general start --json '{"messages":[{"role":"user","content":"Summarize orders placed today."}]}'
omni agents general get-run --run-id run_1
```

`omni agents general invoke` waits up to 780 seconds unless you pass `--timeout`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.