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

# Demos

> End-to-end walkthroughs for the most common HERO integrations.

Practical, copy-pasteable examples.<br />
Each demo assumes you have an API key (`hero_ak_*`) — see [Working with the API](./working-with-the-api) if you don't.

```bash theme={null}
export HERO_API_KEY=hero_ak_your_key_here
```

## 1. Fetch your default workspace

The simplest possible call — verifies your key is wired up correctly and returns the workspace your account lands on by default.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://app.myhero.so/api/workspace/default \
    -H "Authorization: Bearer $HERO_API_KEY"
  ```

  ```ts Node.js theme={null}
  const res = await fetch("https://app.myhero.so/api/workspace/default", {
    headers: { Authorization: `Bearer ${process.env.HERO_API_KEY}` },
  });
  console.log(await res.json());
  ```

  ```python Python theme={null}
  import os, requests
  r = requests.get(
      "https://app.myhero.so/api/workspace/default",
      headers={"Authorization": f"Bearer {os.environ['HERO_API_KEY']}"},
  )
  print(r.json())
  ```
</CodeGroup>

If you get a **401**, the key is wrong or expired. <br />
If you get a **403** with `WorkspaceScopeViolation`, the key is workspace-scoped and the default workspace isn't in its scope — pass an explicit `workspaceId` to a scoped endpoint instead.

## 2. Create a document programmatically

Two calls: list your projects, then create a document inside one.

<Steps>
  <Step title="List your projects">
    ```bash theme={null}
    curl https://app.myhero.so/api/project/all/$WORKSPACE_ID \
      -H "Authorization: Bearer $HERO_API_KEY"
    ```

    Pick the `_id` of the project you want to write into.
  </Step>

  <Step title="Create the document">
    ```bash theme={null}
    curl -X POST https://app.myhero.so/api/document \
      -H "Authorization: Bearer $HERO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "projectId": "<project-id>",
        "name": "My new doc",
        "type": "FILE"
      }'
    ```

    Returns `{ "data": { "_id": "...", "name": "My new doc", ... } }`. Open it in the HERO app at `app.myhero.so/<projectId>/<documentId>/editor`.
  </Step>
</Steps>

## 3. Stream an AI agent (and watch it use tools)

`POST /api/ai-agent/stream` runs HERO's general agent and emits Server-Sent Events as it thinks, calls tools, and edits your workspace. You see the model's reply stream in token-by-token, get notified every time it invokes a tool ([`create-document`](./mcp/tools/project-tree/create-document), [`add-row`](./mcp/tools/tables-data/add-row), [`search-web`](./mcp/tools/web/search-web), and 50+ others), and at the end receive a **change summary** of every entity it touched — undoable in one shot if the user changes their mind.

The five event types over `text/event-stream`:

| `type`        | `data`                                                  |
| ------------- | ------------------------------------------------------- |
| `delta`       | `{ content: string }` — incremental token output        |
| `tool_call`   | provider-specific payload — agent is invoking a tool    |
| `tool_result` | provider-specific payload — tool returned               |
| `done`        | `{ finalContent, usage, sessionId, hasUndo, changes? }` |
| `error`       | `{ message: string }`                                   |

### Quick peek with cURL

`-N` disables curl's buffering so you see the chunks land in real time.

```bash theme={null}
curl -N -X POST https://app.myhero.so/api/ai-agent/stream \
  -H "Authorization: Bearer $HERO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chatMessages": [
      { "type": "user", "text": "Create a document called Sprint 14 retro in my Engineering project, then add an H1 \"What went well\" and three bullet points." }
    ],
    "context": { "workspaceId": "<workspace-id>", "projectId": "<project-id>" }
  }'
```

You'll see a flurry of `data: {"type":"delta",...}` lines as the agent narrates what it's about to do, then `data: {"type":"tool_call",...}` as it invokes [`create-document`](./mcp/tools/project-tree/create-document) and [`write-document-markdown`](./mcp/tools/editor/write-document-markdown), then a final `data: {"type":"done",...}` with the change summary.

### Full Node.js client — parse, react, capture

```ts theme={null}
const res = await fetch("https://app.myhero.so/api/ai-agent/stream", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.HERO_API_KEY}`,
    "Content-Type": "application/json",
    Accept: "text/event-stream",
  },
  body: JSON.stringify({
    chatMessages: [
      { type: "user", text: "Summarise my contracts table and add the summary as a new doc in the Legal project." },
    ],
    context: { workspaceId: process.env.HERO_WORKSPACE_ID },
  }),
});

if (!res.ok || !res.body) throw new Error(`Stream failed: ${res.status}`);

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let sessionId: string | undefined;

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  // SSE frames are separated by a blank line.
  const frames = buffer.split("\n\n");
  buffer = frames.pop() ?? "";

  for (const frame of frames) {
    const line = frame.split("\n").find((l) => l.startsWith("data: "));
    if (!line) continue;

    const event = JSON.parse(line.slice(6));
    switch (event.type) {
      case "delta":
        process.stdout.write(event.data.content);
        break;
      case "tool_call":
        console.log(`\n→ tool: ${event.data?.name ?? "(unknown)"}`);
        break;
      case "tool_result":
        // optional: log a short preview of the result
        break;
      case "done":
        sessionId = event.data.sessionId;
        console.log(`\n\n✓ run complete (session ${sessionId})`);
        if (event.data.hasUndo) {
          console.log(
            `  changed ${event.data.changes?.length ?? 0} entities — undoable via POST /api/ai-agent/undo with { sessionId }`,
          );
        }
        break;
      case "error":
        console.error(`\n✗ ${event.data.message}`);
        break;
    }
  }
}
```

### Undo a run

If the agent did something unwanted, ship the `sessionId` from the `done` event back:

```bash theme={null}
curl -X POST https://app.myhero.so/api/ai-agent/undo \
  -H "Authorization: Bearer $HERO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sessionId": "<session-id>" }'
```

Every entity the agent touched (documents, tables, projects) is rolled back to the snapshot it took before the run.

### Lighter alternatives

* For a single-prompt rewrite (no tool use, no streaming), use [`POST /ai-agent/transform-text`](./api-reference/ai/ai-transform-text) with one of the built-in operations (`rephrase`, `fixGrammar`, `simplify`, `translate`, `shorten`, `summarize`, etc.).
* For an agentic run where you don't need incremental output, use [`POST /ai-agent`](./api-reference/ai/ai-chat) — same body shape, returns the full `chatMessages` array in one response.

## 4. Drive HERO from your AI client

The MCP path. After [installing for your client](./mcp/overview), try prompts like these in Cursor, Claude Desktop, or Claude Code — the AI will route them to the right HERO tools automatically.

```text theme={null}
"List my workspaces and tell me which has the most projects."
```

```text theme={null}
"Create a new document called 'Sprint 14 retro' in my Engineering project,
then add an H1 heading 'What went well' and a bulleted list with three items."
```

```text theme={null}
"Search my documents for any contracts that mention NDA, and summarise
the renewal dates."
```

The MCP server exposes 50+ tools across documents, tables, projects, and web search — browse the full [Tool catalog](./mcp/tools).

## 5. Receive a webhook

Subscribe to events ([Webhooks](./webhooks)) and accept POSTs at your endpoint.

```ts Node.js / Express theme={null}
import express from "express";
const app = express();

app.post("/hero-webhook", express.json(), (req, res) => {
  const { event, data } = req.body;
  console.log(`[${event}]`, data);
  // ... your handling
  res.sendStatus(200);
});

app.listen(3000);
```

Verify the signature header before trusting the payload — see [Webhooks](./webhooks) for the signing scheme.
