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

# Provision Agent from URL

> POST /v1/agents:from-url — kick off an asynchronous build of a chat agent from a creator's YouTube or Instagram channel URL.

# Provision Agent from URL

Kick off an asynchronous build that turns a creator's channel URL (YouTube or
Instagram) into a published, chat-ready agent. The request returns immediately
with `202 Accepted` and a `buildId`; you observe progress over
[Server-Sent Events](/api-reference/provisioning/build-events-sse), a
[polling snapshot](/api-reference/provisioning/get-build), or an optional
[webhook](/api-reference/provisioning/webhook). Once the build reaches `ready`,
visitors chat with the agent over the
[anonymous chat endpoints](/api-reference/provisioning/anonymous-chat).

<ParamField method="POST" path="/v1/agents:from-url" />

<Warning>
  This endpoint is authenticated with a `brs_live_` **API key — a server-side
  secret**. NEVER ship the key to a browser. Your backend calls this endpoint and
  relays SSE/webhook events to the browser. The browser only ever calls the
  anonymous chat endpoints directly. See
  [Authentication & rate limits](/developer/provisioning-auth).
</Warning>

## Authentication

| Header            | Value                      | Required                         |
| ----------------- | -------------------------- | -------------------------------- |
| `Authorization`   | `Bearer brs_live_YOUR_KEY` | Yes                              |
| `Content-Type`    | `application/json`         | Yes                              |
| `Idempotency-Key` | `<uuid>`                   | Optional — dedups double-submits |

The key is **organization-scoped**; all provisioned agents and their cost bill
to that org. The required scope is `agents:write`. A key missing the scope
returns `403 insufficient_scope`. See
[Authentication & rate limits](/developer/provisioning-auth).

## Request Body

<ParamField body="url" type="string" required>
  A YouTube or Instagram channel URL (e.g. `https://youtube.com/@channel` or
  `https://instagram.com/handle`). Normalized and platform-detected server-side.
  An unsupported platform returns `400 unsupported_url`.
</ParamField>

<ParamField body="turnstileToken" type="string">
  A Cloudflare Turnstile token, verified server-side. **Required when the
  platform has `TURNSTILE_SECRET_KEY` configured.** A missing or invalid token
  returns `400 turnstile_failed`.
</ParamField>

<ParamField body="mode" type="string" default="preview">
  Ingestion mode.

  * `preview` (default) — bounded demo ingestion (\~8 most-recent items,
    transcripts-only). Indexing finishes in seconds to about a minute.
  * `full` — full ingestion (deferred to a real signup; preview is the
    recommended path for landing-page / demo flows).
</ParamField>

<ParamField body="intent" type="string">
  Optional agent intent (e.g. `creator`). Drives the default tool set selected
  for the agent.
</ParamField>

<ParamField body="model" type="string">
  Optional OpenRouter model id (e.g. `openai/gpt-4o-mini`). Defaults to the
  platform default model when omitted.
</ParamField>

<ParamField body="deliver" type="object">
  Optional delivery preferences.

  <Expandable title="deliver object">
    <ParamField body="stream" type="boolean">
      Set `true` to signal intent to open the SSE stream.
    </ParamField>

    <ParamField body="webhookUrl" type="string">
      An HTTPS URL to receive out-of-band lifecycle events (at minimum the
      terminal event). Powers "email-me-a-link" UX without holding a connection.
      Payload is signed — see the
      [Webhook reference](/api-reference/provisioning/webhook).
    </ParamField>
  </Expandable>
</ParamField>

## Response — `202 Accepted`

<ResponseField name="buildId" type="string">
  Build identifier (`bld_…`). Use it to construct the stream and poll URLs.
</ResponseField>

<ResponseField name="status" type="string">
  `queued` for a fresh build, or `ready` immediately on a cache / handle-dedup
  hit (a fresh existing published agent for the same normalized handle is
  reused).
</ResponseField>

<ResponseField name="agentSlug" type="string">
  Present only when `status` is `ready` — the public slug to chat with.
</ResponseField>

<ResponseField name="streamUrl" type="string">
  Relative SSE URL: `/v1/agents/builds/bld_…/events`.
</ResponseField>

<ResponseField name="pollUrl" type="string">
  Relative snapshot URL: `/v1/agents/builds/bld_…`.
</ResponseField>

## Request / Response Examples

<CodeGroup>
  ```bash curl theme={null}
  curl -i -X POST https://app.brainstormer.io/v1/agents:from-url \
    -H "Authorization: Bearer brs_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: 7f1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d" \
    -d '{
      "url": "https://youtube.com/@channel",
      "turnstileToken": "0.aBcDeF...",
      "mode": "preview",
      "intent": "creator",
      "model": "openai/gpt-4o-mini",
      "deliver": { "stream": true, "webhookUrl": "https://yourapp.com/api/hook" }
    }'
  ```

  ```typescript Node / TypeScript theme={null}
  import { randomUUID } from "node:crypto";

  const res = await fetch("https://app.brainstormer.io/v1/agents:from-url", {
    method: "POST",
    headers: {
      Authorization: "Bearer brs_live_YOUR_KEY",
      "Content-Type": "application/json",
      "Idempotency-Key": randomUUID(),
    },
    body: JSON.stringify({
      url: "https://youtube.com/@channel",
      turnstileToken: turnstileToken, // from the browser, relayed to your backend
      mode: "preview",
      intent: "creator",
      model: "openai/gpt-4o-mini",
      deliver: { stream: true, webhookUrl: "https://yourapp.com/api/hook" },
    }),
  });

  if (!res.ok) throw new Error(`from-url failed: ${res.status}`);
  const build = await res.json();
  // build.buildId, build.status, build.streamUrl, build.pollUrl, build.agentSlug?
  ```
</CodeGroup>

**Response (202) — fresh build:**

```json theme={null}
{
  "buildId": "bld_8c9d0e1f2a3b",
  "status": "queued",
  "streamUrl": "/v1/agents/builds/bld_8c9d0e1f2a3b/events",
  "pollUrl": "/v1/agents/builds/bld_8c9d0e1f2a3b"
}
```

**Response (202) — instant cache / handle-dedup hit:**

```json theme={null}
{
  "buildId": "bld_8c9d0e1f2a3b",
  "status": "ready",
  "agentSlug": "channel-creator-agent",
  "streamUrl": "/v1/agents/builds/bld_8c9d0e1f2a3b/events",
  "pollUrl": "/v1/agents/builds/bld_8c9d0e1f2a3b"
}
```

## Behaviors

* **Turnstile** is verified server-side; supply a fresh token per submit.
* **URL normalization + platform detection** — `youtube` / `instagram` /
  `unsupported`. Unsupported platforms return `400 unsupported_url`.
* **Handle dedup** — a fresh existing published agent for the same normalized
  handle is **reused** and returned as `ready` instantly, with **no new build**
  and no new cost.
* **Idempotency** — supplying `Idempotency-Key` makes a double-submit return the
  same build instead of spawning a second one.
* **Pre-flight failures** (plan limit, insufficient credits) come back as a
  `failed` **build event** — observed over SSE / poll / webhook — not as a
  synchronous HTTP error on this request.

## HTTP Status Codes

| Status                   | Meaning                                                                        |
| ------------------------ | ------------------------------------------------------------------------------ |
| `202 Accepted`           | Build queued (or returned `ready` on a dedup hit).                             |
| `400 unsupported_url`    | URL is not a readable YouTube/Instagram channel.                               |
| `400 turnstile_failed`   | Missing or invalid Turnstile token.                                            |
| `401`                    | Missing or invalid `Authorization` header.                                     |
| `403 insufficient_scope` | Key lacks the `agents:write` scope.                                            |
| `429`                    | Rate limit hit (per-key or per-IP). Includes a `Retry-After` header (seconds). |

## Build Status & Error Model

The build progresses through a finite state machine. The complete
`ProvisioningStatus` and `ProvisioningErrorCode` enums — with `retryable`
semantics — are documented in the
[SSE reference](/api-reference/provisioning/build-events-sse#provisioningevent-contract)
and explained as concepts in
[Build lifecycle](/developer/provisioning-concepts).
