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

# Copilot Chat

> AI-powered creative assistance for ad strategy, captions, and ideation

Send messages to the Nouvel AI copilot for help with ad strategy, caption writing, product research, and creative ideation. This is the API version of the dashboard copilot — it returns a complete JSON response instead of a server-sent event stream.

## Authentication

<ParamField header="Authorization" type="string" required>
  Your Nouvel API key with `copilot:chat` permission. Format: `Bearer nvl_xxxx`
</ParamField>

## Request Body

<ParamField body="messages" type="array" required>
  Chat message history. Each message must have `role` and `content`.

  <Expandable title="Message object">
    <ParamField body="messages[].role" type="string" required>
      Message role. One of `user` or `assistant`.
    </ParamField>

    <ParamField body="messages[].content" type="string" required>
      Message text content.
    </ParamField>
  </Expandable>

  <Note>
    Include the full conversation history to maintain context. The copilot is stateless — each request is independent.
  </Note>
</ParamField>

<ParamField body="productUrl" type="string">
  Product URL for competitor ad research. When provided, the copilot fetches competitor ad intelligence to inform its responses.
</ParamField>

<ParamField body="projectId" type="string">
  UUID of an active project. When provided, the copilot has context about the project's settings, scenes, scripts, and style configuration.
</ParamField>

## Response

<ResponseField name="response" type="string" required>
  The copilot's full text response.
</ResponseField>

<ResponseField name="model" type="string" required>
  The AI model used (determined by your plan, not configurable via API).
</ResponseField>

## Example Requests

<CodeGroup>
  ```bash cURL - Simple Chat theme={null}
  curl -X POST https://app.nouvel.ai/api/v1/copilot/chat \
    -H "Authorization: Bearer nvl_xxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "messages": [
        { "role": "user", "content": "Write me 3 Instagram captions for a protein powder targeting gym-goers" }
      ]
    }'
  ```

  ```bash cURL - With Product Context theme={null}
  curl -X POST https://app.nouvel.ai/api/v1/copilot/chat \
    -H "Authorization: Bearer nvl_xxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "messages": [
        { "role": "user", "content": "What angles are competitors using for this product?" }
      ],
      "productUrl": "https://example.com/products/whey-protein"
    }'
  ```

  ```bash cURL - Multi-turn Conversation theme={null}
  curl -X POST https://app.nouvel.ai/api/v1/copilot/chat \
    -H "Authorization: Bearer nvl_xxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "messages": [
        { "role": "user", "content": "Write me a caption for my protein powder" },
        { "role": "assistant", "content": "Here are some caption ideas..." },
        { "role": "user", "content": "Make the second one shorter and add emojis" }
      ]
    }'
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch('https://app.nouvel.ai/api/v1/copilot/chat', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.NOUVEL_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      messages: [
        { role: 'user', content: 'Write me 3 Instagram captions for a protein powder' },
      ],
      productUrl: 'https://example.com/products/whey-protein',
    }),
  });

  const { response: answer, model } = await response.json();
  console.log(`Copilot (${model}): ${answer}`);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://app.nouvel.ai/api/v1/copilot/chat',
      headers={
          'Authorization': f'Bearer {api_key}',
          'Content-Type': 'application/json',
      },
      json={
          'messages': [
              {'role': 'user', 'content': 'Write me 3 Instagram captions for a protein powder'}
          ],
          'productUrl': 'https://example.com/products/whey-protein',
      }
  )

  data = response.json()
  print(data['response'])
  ```
</CodeGroup>

## Response Examples

<CodeGroup>
  ```json 200 - Success theme={null}
  {
    "response": "Here are 3 Instagram captions for your protein powder:\n\n1. **The Gains Caption**: \"Every scoop brings you closer to your goals. 25g of pure whey protein, zero compromises. 💪 #FitnessJourney #ProteinPowder\"\n\n2. **The Lifestyle Caption**: \"Morning routine upgrade: coffee ☕ + [Brand] protein shake = unstoppable. What's in your post-workout shake?\"\n\n3. **The Social Proof Caption**: \"10,000+ athletes trust [Brand] for their recovery. Join the movement. Link in bio. 🏋️\"",
    "model": "balanced"
  }
  ```

  ```json 429 - Daily Limit Reached theme={null}
  {
    "error": "daily_limit_reached",
    "resetAt": "2026-03-10T00:00:00.000Z",
    "count": 100,
    "limit": 100
  }
  ```

  ```json 401 - Unauthorized theme={null}
  {
    "error": "Invalid API key"
  }
  ```

  ```json 403 - Forbidden theme={null}
  {
    "error": "This API key does not have the 'copilot:chat' permission"
  }
  ```
</CodeGroup>

## Error Codes

| Code | Description                                                                           |
| ---- | ------------------------------------------------------------------------------------- |
| 400  | Missing `messages` array or invalid format                                            |
| 401  | Invalid or missing API key                                                            |
| 402  | Plan doesn't include API access                                                       |
| 403  | API key missing `copilot:chat` permission                                             |
| 429  | Daily message limit reached. Check `resetAt` for when the limit resets (midnight UTC) |
| 500  | Internal server error                                                                 |

## Context Injection

The copilot automatically enriches its responses with context from your account:

| Context Source        | When Included                                                                                                       |
| --------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Brand kit**         | Always — business name, description, tone of voice, target audience, color palette, visual style, guardrails        |
| **Available avatars** | Always — platform actors + custom avatars you've uploaded                                                           |
| **Scraped URLs**      | When the last user message contains product URLs — they're scraped, classified, and only product pages are included |
| **Competitor ads**    | When `productUrl` is provided — fetches competitor ad intelligence from our ad research database                    |
| **Project context**   | When `projectId` is provided — includes project style settings, scenes, and scripts                                 |

## Daily Limits

The copilot enforces a daily message limit per user, determined by your plan. The limit resets at midnight UTC.

When the limit is reached, the API returns a `429` response with:

* `resetAt` — ISO timestamp when the limit resets
* `count` — number of messages used today
* `limit` — maximum messages allowed per day

## Differences from Dashboard Copilot

| Feature          | Dashboard                           | API                         |
| ---------------- | ----------------------------------- | --------------------------- |
| Auth             | Browser session (cookies)           | API key                     |
| Response format  | Server-Sent Events (SSE stream)     | JSON (complete response)    |
| File attachments | Supported                           | Not supported               |
| Product context  | Full form (actor, language, images) | `productUrl` parameter only |
| Max duration     | N/A (streaming)                     | 60 seconds                  |

<Tip>
  The API copilot returns the complete response as JSON, making it ideal for automation and integration workflows where you need the full response before proceeding.
</Tip>

## Best Practices

<AccordionGroup>
  <Accordion title="Include conversation history">
    The copilot is stateless. To maintain context across turns, include all previous messages in the `messages` array. This lets the copilot refine and build on previous responses.
  </Accordion>

  <Accordion title="Use productUrl for competitive insights">
    When asking about ad strategy or creative angles, include a `productUrl` to give the copilot access to competitor ad intelligence.
  </Accordion>

  <Accordion title="Monitor daily limits">
    Check the `X-RateLimit-Remaining` header to track your API rate limit, and handle `429` responses gracefully with the `resetAt` timestamp.
  </Accordion>
</AccordionGroup>
