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

# Ship a run as a product surface

> Forward run.events to your own UI over SSE, tie cancellation to the request's own signal, and end the stream on the report — your chrome, not Vendo's.

A long piece of work with a progress view is still `run()`. `AgentRun` is
awaitable *and* iterable: `run.events` is the live feed, and awaiting the same
object gives you the report at the end.

<Note>
  **Vendo's chat UI is not handed over here, deliberately.** This lane gives
  you an event stream and a report — the surface is yours to build. If you want
  Vendo's own thread, overlay, and generated screens, that is the umbrella
  (`createVendo` + `<VendoProvider>`), not this SDK.
</Note>

## The route

```ts theme={null}
// app/api/reports/route.ts
import { THREAD_ID_HEADER } from "@vendoai/agents";
import { support } from "@/lib/agent";
import { auth } from "@/lib/auth";

export async function POST(req: Request) {
  const user = await auth(req);
  if (user === null) return new Response("unauthorized", { status: 401 });

  const { brief } = (await req.json()) as { brief: string };

  const run = support.run(brief, { as: user.id, signal: req.signal });

  const encoder = new TextEncoder();
  const stream = new ReadableStream<Uint8Array>({
    async start(controller) {
      for await (const event of run.events) {
        controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
      }
      const report = await run;
      controller.enqueue(
        encoder.encode(`event: report\ndata: ${JSON.stringify(report)}\n\n`),
      );
      controller.close();
    },
  });

  return new Response(stream, {
    headers: {
      "content-type": "text/event-stream",
      "cache-control": "no-cache",
      [THREAD_ID_HEADER]: run.threadId,
    },
  });
}
```

Three details carry their weight:

* **`run.threadId` is available immediately**, before the first event — so the
  header goes out with the response, not after it.
* **`signal: req.signal`** ties the run to the connection. The browser closes
  the tab, the request aborts, the run stops. There is no `cancel()` to call.
* **`await run` after the loop** is the report. Iterating the events does not
  consume the result; the same object is both.

## What comes over the wire

`run.events` yields `RunEvent`s, each tagged by `type`:

| `type`        | What it marks                                   |
| ------------- | ----------------------------------------------- |
| `text`        | the model's own words, as they arrive           |
| `status`      | where the run is                                |
| `tool-call`   | a tool the run is about to use                  |
| `tool-result` | what that tool answered                         |
| `error`       | the run failed, with the harness's own sentence |

Forward them whole, as above, and branch in your UI — a new event type then
reaches your client the day it ships instead of being swallowed by a server
that only knew five.

## Ending on the report

The report is the honest close: `status`, the model's `summary`, the
`toolCalls` it made, `refs.threadId` and `refs.approvals`, and `usage`.

```ts theme={null}
// in your client
source.addEventListener("report", (event) => {
  const report = JSON.parse(event.data);
  if (report.status === "stopped") showCancelled();
  else if (report.refs.approvals.length > 0) showApprovals(report.refs.approvals);
  else showDone(report.summary);
});
```

Anything parked for a human is in `refs.approvals`. Acting on one of those is
a **fresh `run()`**, not a resume of this one — see
[Approvals inside `run`](/agent-sdk/reference#approvals-inside-run).

<CardGroup cols={2}>
  <Card title="Reference" href="/agent-sdk/reference#agentrun">
    `AgentRun`, `RunEvent`, and the full report shape.
  </Card>

  <Card title="Vendo's own surfaces" href="/vendo-agent/quickstart">
    The umbrella, if you want the thread, the overlay, and generated screens.
  </Card>
</CardGroup>
