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

# Streaming builds

> Start builds from code, read progress, and distinguish deployment from verified completion.

Builds take minutes. Read the response as a stream to receive progress and the outcome of the run. An HTTP `200` means the stream opened, not that a function is ready.

Both endpoints below use `Authorization: Bearer $NOTTE_API_KEY`. Keep that key on your server; do not embed a shared key in a public web client.

## Choose a transport

| Endpoint | Response | Use it for |
| - | - | - |
| `POST https://anything.notte.cc/api/marketplace/build` | Named SSE events: `progress`, `result`, `error` | A simple build client that needs a newly created function ID. |
| `POST https://anything.notte.cc/api/anything/start` | AI SDK UI-message stream, with JSON chunks in SSE `data:` frames | Agent text, tool activity, thread IDs, follow-up turns, and verified build completion. |

The formats differ. Do not use a parser that expects named `event: result` frames for the AI SDK stream. The [public OpenAPI specification](https://anything.notte.cc/api/marketplace/openapi.json) describes the first endpoint and its event payloads.

## Simple build stream

Send a `query` describing the function you want. `curl -N` disables output buffering so events appear as they arrive.

```bash theme={"system"}
curl -N https://anything.notte.cc/api/marketplace/build \
  -H "Authorization: Bearer $NOTTE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"Create a new private function that returns the top three Hacker News posts with title, URL, and score."}'
```

The response has `Content-Type: text/event-stream`. Each event ends with a blank line. Representative frames:

```text theme={"system"}
event: progress
data: {"message":"Starting build agent (this takes 2-10 minutes)..."}

event: result
data: {"data":{"function_id":"123e4567-e89b-42d3-a456-426614174000","name":"Hacker News posts","description":"Returns the top posts.","variables":[],"workspace":"personal","message":"Function created. Execute it with run(function_id, variables)."}}
```

An unsuccessful build sends an `error` event instead of a `result`:

```text theme={"system"}
event: error
data: {"error":"The build finished without producing a function. Refine the query (describe one concrete web task) and try again."}
```

Parse the JSON after `data:` using the event name from `event:`. Progress is informational; only `result` or `error` is terminal. Check for an HTTP rejection before reading the stream, and handle errors inside a stream that already returned `200`.

> [!WARNING]
>
> This endpoint returns its result when it detects a deployed function. That is not proof that the agent has finished testing it. Use the full agent stream below when your application needs to distinguish creation from verified completion.

## Full agent stream

The default response from `/api/anything/start` is streaming. Omit `detach`, or set it to `false`.

```bash theme={"system"}
curl -N -i https://anything.notte.cc/api/anything/start \
  -H "Authorization: Bearer $NOTTE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"Create and test a new private function that returns the top three Hacker News posts with title, URL, and score.","version":"v2"}'
```

Save the `x-thread-id` response header. This endpoint uses the AI SDK UI-message wire format but does not forward the `x-vercel-ai-ui-message-stream` header, so do not require that header in your client. The stream format is separate from the retired Anything API v1 backend.

JSON chunks arrive in `data:` frames. Relevant chunks include:

| Chunk | Meaning |
| - | - |
| `text-delta` | Agent text in `delta`, including explanations of blockers. |
| `tool-input-available` | A tool call, identified by `toolCallId` and `toolName`. |
| `tool-output-available` | The output of that tool call, matched by `toolCallId`. |
| `data-function_uploaded` | Function metadata. An initial deployment can emit this before testing is complete. |
| `error` | A stream error, with the explanation in `errorText`. |
| `finish` | The agent turn ended. This alone does not mean a function was built. |
| `[DONE]` | End-of-stream sentinel, not JSON. |

### Detect verified completion

Track the `toolCallId` of a `tool-input-available` chunk whose `toolName` is `complete_build`. Its successful `tool-output-available` output contains a JSON object, possibly encoded as a string, with:

```json theme={"system"}
{
  "status": "build_completed",
  "function_id": "123e4567-e89b-42d3-a456-426614174000",
  "thread_id": "820a7cff-613b-4529-9ba4-52c7a6777713",
  "version": "v20260824_142124",
  "name": "Hacker News posts"
}
```

This example shows only the fields a completion reader needs. Require the same `thread_id` as the response header and a nonempty function ID and version. Wait for normal stream termination, and treat any subsequent error as a failure. Do not infer success from an arbitrary tool's stdout or an early deployment event.

A turn can finish normally after the agent explains a blocker, without a stream `error`. If no successful `complete_build` result arrived, report that the run ended without a verified function and show the agent's explanation.

### Python reader

This server-side example uses `requests`. Its line iterator handles network chunk boundaries; collect `data:` lines until the blank line that terminates each SSE frame.

```python theme={"system"}
import json
import os
import requests

with requests.post(
    "https://anything.notte.cc/api/anything/start",
    headers={"Authorization": f"Bearer {os.environ['NOTTE_API_KEY']}"},
    json={"query": "Create and test a new private function that returns the top three Hacker News posts.", "version": "v2"},
    stream=True,
    timeout=(10, 1800),
) as response:
    response.raise_for_status()
    thread_id = response.headers["x-thread-id"]
    print("Thread:", thread_id)  # Persist this before reading the stream.
    completion_calls = set()
    completed = None
    finished = False
    explanation = ""
    data_lines = []

    for line in response.iter_lines(decode_unicode=False):
        line = line.decode("utf-8")
        if line:
            if line.startswith("data:"):
                data_lines.append(line[5:].lstrip())
            continue
        if not data_lines:
            continue
        payload = "\n".join(data_lines)
        data_lines = []
        if payload == "[DONE]":
            finished = True
            break
        chunk = json.loads(payload)
        kind = chunk.get("type")
        if kind == "text-delta":
            text = chunk.get("delta", "")
            print(text, end="", flush=True)
            explanation = (explanation + text)[-2000:]
        elif kind == "error":
            raise RuntimeError(chunk.get("errorText", "Build stream failed"))
        elif kind == "tool-input-available" and chunk.get("toolName") == "complete_build":
            completion_calls.add(chunk["toolCallId"])
        elif kind == "tool-output-available" and chunk.get("toolCallId") in completion_calls:
            output = chunk.get("output")
            if isinstance(output, str):
                try:
                    output = json.loads(output)
                except ValueError:
                    continue  # A rejected complete_build call can return plain text.
            if (isinstance(output, dict)
                and output.get("status") == "build_completed"
                and output.get("thread_id") == thread_id
                and output.get("function_id")
                and output.get("version")):
                completed = output
        elif kind == "finish":
            finished = True

    if not finished:
        raise RuntimeError("Stream interrupted. Check the saved thread before retrying.")
    if not completed:
        raise RuntimeError(explanation.strip() or "Run ended without a verified function.")
    print("\nReady:", completed["function_id"], completed["version"])
```

Persist the outcome in your own application. A dropped connection or read timeout is an unknown outcome, not permission to resubmit a build. Starting another request without the saved thread ID creates a new thread and can duplicate work and charges.

## Detached builds and follow-ups

`POST /api/anything/start` also accepts `detach: true`. It returns `202 Accepted` immediately:

```json theme={"system"}
{
  "thread_id": "820a7cff-613b-4529-9ba4-52c7a6777713",
  "url": "https://anything.notte.cc/personal/thread/v2/820a7cff-613b-4529-9ba4-52c7a6777713",
  "status": "started"
}
```

`started` acknowledges submission only. The returned URL opens the thread in the app. There is no documented public polling endpoint to recover a detached build's final status or transcript. Choose streaming when your client needs the outcome programmatically; detached mode does not return a stream to consume later.

To send a follow-up after the turn finishes, call `/api/anything/start` again with a new `query` and the saved `thread_id`. This starts another agent turn in the same thread; it is not a read-only status request or stream reconnection.

For unattended builds, make that constraint explicit in `query`: describe the inputs, expected output, authentication available to the run, whether a new function is required, visibility, and how the agent should report missing prerequisites without asking for human input.


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