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

# POST /render/enhance

> Transform AI text into structured UI components

The core endpoint. Sends raw AI text and receives structured `ContentBlock[]` — tables, charts, alerts, checklists, and more.

## Request

<ParamField body="content" type="string" required>
  The full AI text response to enhance. Can include markdown formatting.
</ParamField>

<ParamField body="context" type="string">
  The user's original prompt or conversation context. Improves classification accuracy — helps distinguish, for example, a comparison table from raw data.
</ParamField>

<ParamField body="preferences" type="object">
  Optional hints to control enhancement behavior.

  <Expandable title="Preferences fields">
    <ParamField body="preferences.preferredTypes" type="string[]">
      Component types to prefer when ambiguous. Values must be valid component keys from [`GET /components`](/docs/api/components).
    </ParamField>

    <ParamField body="preferences.maxComponents" type="integer">
      Maximum number of components to generate. Excess segments become text blocks with fallback.
    </ParamField>

    <ParamField body="preferences.density" type="string" default="normal">
      Layout density. One of `"compact"`, `"normal"`, or `"detailed"`.
    </ParamField>

    <ParamField body="preferences.fallbackPolicy" type="string" default="onFail">
      When to include fallback markdown. `"onFail"` includes it only on component blocks; `"always"` includes it on every block; `"never"` omits fallback entirely.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField query="stream" type="boolean" default="true">
  When `true` (default), returns Server-Sent Events (SSE). When `false`, returns a single JSON response after processing completes.
</ParamField>

## Response (JSON mode)

Set `?stream=false` to get a single JSON response:

```bash theme={null}
curl -X POST "https://api.outkit.dev/render/enhance?stream=false" \
  -H "Content-Type: application/json" \
  -H "x-outkit-api-key: your-api-key" \
  -d '{
    "content": "Here are the top 3 frameworks:\n\n| Framework | Stars | Language |\n|-----------|-------|----------|\n| React | 220k | JavaScript |\n| Vue | 207k | JavaScript |\n| Svelte | 78k | JavaScript |\n\n**Tip:** Svelte has the smallest bundle size.",
    "context": "User asked about frontend frameworks"
  }'
```

### Response Body

```json theme={null}
{
  "blocks": [
    {
      "component": "text",
      "version": "1.0",
      "props": {
        "content": "Here are the top 3 frameworks:",
        "variant": "prose"
      }
    },
    {
      "component": "table",
      "version": "1.0",
      "props": {
        "columns": ["Framework", "Stars", "Language"],
        "rows": [
          { "Framework": "React", "Stars": "220k", "Language": "JavaScript" },
          { "Framework": "Vue", "Stars": "207k", "Language": "JavaScript" },
          { "Framework": "Svelte", "Stars": "78k", "Language": "JavaScript" }
        ],
        "sortable": true
      },
      "meta": { "confidence": 0.95 },
      "fallback": "| Framework | Stars | Language |..."
    },
    {
      "component": "alert",
      "version": "1.0",
      "props": {
        "variant": "tip",
        "message": "Svelte has the smallest bundle size."
      },
      "meta": { "confidence": 0.90 }
    }
  ],
  "debug": {
    "componentsDetected": 2,
    "processingTimeMs": 1850,
    "provider": "gemini",
    "classifications": [
      { "component": "table", "confidence": 0.95, "sourceRange": [0, 0] },
      { "component": "alert", "confidence": 0.90, "sourceRange": [0, 0] }
    ]
  },
  "design": {
    "--outkit-primary": "#b30069",
    "--outkit-bg": "#fbf9f8",
    "--outkit-text": "#1b1c1c"
  }
}
```

<Note>
  The `design` field contains CSS custom properties from the design profile linked to your API key. Use these tokens to style rendered components consistently with your brand.
</Note>

### Wire Format

The raw API response uses compact single-character keys for bandwidth efficiency:

| Wire key | Expanded key | Description                           |
| -------- | ------------ | ------------------------------------- |
| `c`      | `component`  | Component type key                    |
| `v`      | `version`    | Spec version                          |
| `p`      | `props`      | Component-specific data               |
| `m`      | `meta`       | Optional metadata (confidence, title) |
| `f`      | `fallback`   | Fallback markdown text                |

The `@outkit-dev/react` SDK expands wire format automatically. If you integrate directly, you'll receive the wire format. The examples in these docs show the expanded format for readability.

## Response (Streaming mode)

With streaming enabled (the default), the response is delivered as Server-Sent Events:

```bash theme={null}
curl -X POST "https://api.outkit.dev/render/enhance" \
  -H "Content-Type: application/json" \
  -H "x-outkit-api-key: your-api-key" \
  -d '{ "content": "..." }'
```

```
data: {"type":"meta","design":{"--outkit-primary":"#b30069","--outkit-bg":"#fbf9f8"}}

data: {"component":"text","version":"1.0","props":{"content":"Here are the top 3 frameworks:","variant":"prose"}}

data: {"component":"table","version":"1.0","props":{...}}

data: {"component":"alert","version":"1.0","props":{...}}

data: [DONE]
```

The first event is always a `meta` event containing design tokens for your profile. Component blocks follow in document order — text content arrives as `component: "text"` blocks, same as any other component. The stream ends with `data: [DONE]`.

<Warning>
  Filter out `meta` events before rendering — they contain configuration data, not content blocks.
</Warning>

### Why Streaming?

Streaming lets you render content progressively as the LLM generates it:

1. **`meta` event** arrives → apply design tokens to your renderer
2. **LLM token chunks** arrive → the SDK parses structured blocks incrementally
3. **`[DONE]`** signals the stream is complete

The `@outkit-dev/react` SDK handles this automatically via `feedChunk` / `feedMeta` / `feedDone` — it accumulates raw chunks, parses partial JSON into renderable blocks, and shows skeleton placeholders while waiting for each component to complete. See the [React SDK guide](/docs/sdk/react) for the full integration pattern.

<Note>
  Your API key must stay on your server. Your backend should proxy the SSE stream to the browser — the React SDK handles parsing on the client side.
</Note>

## ContentBlock Schema

Every response contains an ordered array of blocks. See [ContentBlock reference](/docs/api/content-blocks) for the full schema of each block type.

## Code Examples

### JSON Mode

<CodeGroup>
  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.outkit.dev/render/enhance",
      params={"stream": "false"},
      headers={
          "Content-Type": "application/json",
          "x-outkit-api-key": "your-api-key",
      },
      json={
          "content": ai_response_text,
          "context": user_prompt,
      },
  )

  data = response.json()
  design = data.get("design", {})  # CSS custom properties for styling

  for block in data["blocks"]:
      if block.get("c"):  # wire format component block
          render_component(block["c"], block["p"])
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    "https://api.outkit.dev/render/enhance?stream=false",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-outkit-api-key": process.env.OUTKIT_API_KEY!,
      },
      body: JSON.stringify({
        content: aiResponseText,
        context: userPrompt,
      }),
    }
  );

  const { blocks, design } = await response.json();

  for (const block of blocks) {
    if (block.c) {
      // Wire format component block: { c, v, p, m?, f? }
      renderComponent(block.c, block.p);
    }
  }
  ```
</CodeGroup>

### Streaming Mode

<CodeGroup>
  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.outkit.dev/render/enhance",
      headers={
          "Content-Type": "application/json",
          "x-outkit-api-key": "your-api-key",
      },
      json={"content": ai_response_text},
      stream=True,
  )

  for line in response.iter_lines():
      line = line.decode("utf-8")
      if not line.startswith("data: "):
          continue
      data = line[6:]
      if data == "[DONE]":
          break

      import json
      block = json.loads(data)

      if block.get("type") == "meta":
          design_tokens = block.get("design", {})
          continue  # not a content block

      # Handle content blocks...
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch("https://api.outkit.dev/render/enhance", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-outkit-api-key": process.env.OUTKIT_API_KEY!,
    },
    body: JSON.stringify({ content: aiResponseText }),
  });

  const reader = response.body!.getReader();
  const decoder = new TextDecoder();
  let buffer = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop()!;

    for (const line of lines) {
      if (!line.startsWith("data: ")) continue;
      const data = line.slice(6);
      if (data === "[DONE]") return;

      const block = JSON.parse(data);

      if (block.type === "meta") {
        applyDesignTokens(block.design);
        continue; // not a content block
      }

      // Handle content blocks...
    }
  }
  ```
</CodeGroup>

## Quota Exhausted (Passthrough Mode)

When your monthly render quota is exhausted, the API returns your original content as a single `text` component block instead of returning an error. The response includes `mode: "passthrough"` in the debug metadata:

```json theme={null}
{
  "blocks": [
    { "component": "text", "version": "1.0", "props": { "content": "...your original content unchanged...", "variant": "prose" } }
  ],
  "debug": {
    "mode": "passthrough",
    "componentsDetected": 0,
    "processingTimeMs": 0
  }
}
```

Your application should handle passthrough gracefully — for example, by rendering the text as markdown. The `@outkit-dev/react` SDK handles this automatically.
