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

# API and structured output

> When to start runs over the API instead of a channel, the invoke endpoints and what they return, continuing a conversation, reading tasks, and the four ways to make an agent answer in the same shape every time.

## Why the API

A channel is for a person. The API is for a system: a CI pipeline that wants a failure analysed, a ticketing system that hands a case to an agent, a nightly job in your own scheduler, a product that embeds an agent behind its own interface. The run is the same governed conversation as one started in Xpander Chat, with the agent's skills, approvals, budget and record, and the caller gets the result back in a shape it can parse. Choose the [webhook](/use/agents/channels#webhook) instead when the caller cannot hold a key.

## Start a run

Three endpoints share one handler and one body:

| Endpoint                                   | Returns                                                                                                      |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `POST /v1/agents/{agent_id}/invoke`        | Waits for the run and returns the finished task, with `result`                                               |
| `POST /v1/agents/{agent_id}/invoke/stream` | Server-sent events until `task_finished`: text, each skill call, plan updates, sub-task lifecycle, approvals |
| `POST /v1/agents/{agent_id}/invoke/async`  | Returns the task immediately with status `pending`; poll `GET /v1/tasks/{task_id}`                           |

Authenticate with `x-api-key` and an organization API key from **Settings > API keys**; a key can be limited to named agents. The agent's **Developer access** row shows a second address, `POST /v1/agents/{agent_id}/gateway/invoke`. That one runs a single turn of a conversation and returns the reply; it is the conversation API Xpander Chat itself uses. By contrast, `/invoke` starts a task and returns the task record. Both take the same key. The body needs only the input:

```bash theme={"dark"}
curl -X POST 'https://api.xpander.ai/v1/agents/<agent_id>/invoke' \
  -H 'x-api-key: $XPANDER_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"input": {"text": "Summarize the failures in last night'\''s test run"}}'
```

Behind a load balancer with an idle timeout, an AWS ALB answers `504` after 60 seconds by default, a synchronous call whose turn runs longer never returns. Use `/invoke/async` and poll the task, or the streaming endpoint, for turns that may take more than a minute.

Useful fields beside `input.text`:

* `input.files`: URLs the agent reads
* `input.user`: who the run is for, so the record and any per-person credential resolve to them
* `id`: an existing conversation id, to continue it
* `additional_context`, `instructions_override`, `title`
* `llm_model_provider` and `llm_model_name` for this run
* `tool_call_limit`
* `sub_tasks_mode`: `sync`, `async` or `auto`
* `version=draft` as a query parameter, to run the [staged version](/use/agents/create#publish) of the agent

A dropped stream does not stop the run; the answer lands in the task. On a self-hosted location the host is that location's API address.

## Read tasks

`GET /v1/tasks` lists tasks with filters for agent, user, parent task, status and dates. `GET /v1/tasks/{task_id}` returns the task, `/thread` its messages and skill calls, `/thread/full` the same including every sub-task, `/llm_usage` its model calls with cost. `POST /v1/tasks/{task_id}/stop` stops it. Task statuses are `pending`, `executing`, `paused`, `error`, `failed`, `completed` and `stopped`. The full reference is under [REST API](/api-reference).

## Get told when a run ends

An agent's `notification_settings`, set with `PATCH /v1/agents/{agent_id}`, send the outcome on success, on error and on budget events to email, Slack or a webhook of yours. The webhook receives the task's name, subject, body, type, duration, result and a link to the task, with any headers you configured:

```json theme={"dark"}
{"notification_settings": {"on_success": {"webhook": [{"url": "https://ci.example.com/hooks/xpander", "headers": {"X-Token": "..."}}]},
                           "on_error":   {"email": [{"to": "oncall@example.com"}]}}}
```

## Structured output

Four mechanisms, from the one to reach for first:

1. **The agent's output format.** In Agent settings, **Output > Structured output** takes a JSON Schema; **Generate with AI** drafts one from a sentence. Every answer is validated against it and repaired when it can be, on every channel, and the API returns the JSON as `result`. In Slack and the other chat channels the same answer is rendered as prose for the reader; the record keeps the JSON. This is the setting to use when a downstream system parses the answer.

<Frame caption="Output > Structured output on a new agent, with the seeded schema.">
  <img src="https://mintcdn.com/xpanderai-099931d1/OdLxX9OyFVicAV0l/images/verify/reach-structured-output.png?fit=max&auto=format&n=OdLxX9OyFVicAV0l&q=85&s=48304541963e96c09c8f59f62e305b63" alt="Agent settings Output section with Default, Markdown and Structured output options, a JSON schema editor holding a seeded AI Agent Result schema, and a Generate with AI button" width="1440" height="900" data-path="images/verify/reach-structured-output.png" />
</Frame>

2. **The required sections in the instructions.** For a human-readable answer that must always carry the same sections, write them into the agent's static prompt as a contract. Every turn's prompt carries the instructions, and they are never trimmed.
3. **A format per channel.** A Slack channel routed to the agent can carry its own format instructions, which override the agent's output format for that channel only; see [Channels](/use/agents/channels#slack-and-microsoft-teams).
4. **A schema per call.** The invoke body accepts `output_format` and `output_schema` for one run. It applies to runs the API starts directly; a turn that goes through the agent's conversation uses the agent-level schema instead, so prefer the agent setting when both are in play.

Over the webhook, a structured result is returned as JSON rather than a string, and `?getter=result.<field>` extracts one field.

## The same run from the SDK

The Python SDK wraps these calls (`Tasks.create`, events, `Agents`) against the same endpoints; see [SDK](/api-reference/sdk). Everything on this page also applies to a workflow, at `POST /v1/workflows/{workflow_id}/invoke`.
