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

# Generated summaries and recommendations

> Generate natural-language summaries and actionable recommendations for an analysis, and learn which analysis id to use.

Beyond attention heatmaps and clarity scores, the EyeQuant API can generate natural-language **summaries** and **recommendations** for an analysis.

* **Summary** — a concise description of what the analysis found.
* **Recommendations** — concrete, actionable suggestions for improving the design.

Both features run asynchronously: trigger generation with a `POST`, then poll with a `GET` until the content is ready.

<Note>
  When generation completes, the content is returned in the `data` field as a **Markdown string**. Render it as Markdown to preserve headings and lists.
</Note>

## Which ID to use

This is the most common source of confusion, so it is worth getting right.

`POST /analyses` returns the **screenshot id**. Use that id to poll [Get an analysis](/api-reference/get-analysis). When the analysis succeeds, that response also returns `_internal_exposed_id` — the **analysis id**. Use the analysis id for:

* [Update analysis metadata](/api-reference/update-analysis-meta)
* [Generate](/api-reference/generate-summary) and [Get](/api-reference/get-summary) a summary
* [Generate](/api-reference/generate-recommendations) and [Get](/api-reference/get-recommendations) recommendations

```mermaid theme={null}
sequenceDiagram
    participant C as Your client
    participant A as EyeQuant API
    C->>A: POST /analyses
    A-->>C: returns screenshot id
    C->>A: GET /analyses/{screenshot id}
    A-->>C: success + _internal_exposed_id (analysis id)
    C->>A: PUT /update-meta/screenshot/{analysis id}
    A-->>C: success
    C->>A: POST /analyses/summary/{analysis id}
    A-->>C: status started
    loop until completed
        C->>A: GET /analyses/summary/{analysis id}
        A-->>C: status processing
    end
    C->>A: GET /analyses/summary/{analysis id}
    A-->>C: status completed + data (markdown)
```

<Steps>
  <Step title="Add context (optional)">
    Before triggering generation, you can attach context to the analysis so the generated content focuses on the page, audience, and business outcome that matter to you. See [Update analysis metadata](/api-reference/update-analysis-meta).

    ```bash theme={null}
    curl \
      -X PUT \
      -H "Authorization: Bearer $apikey" \
      -H "Content-Type: application/json" \
      -d '{ "description": "Homepage hero", "goal": "Increase sign-ups" }' \
      https://api.eyequant.com/v2/update-meta/screenshot/$analysisId
    ```
  </Step>

  <Step title="Trigger generation">
    Trigger a summary (use the `recommendations` path for recommendations). Calling `POST` again is safe — if generation was already triggered, it returns the current status.

    ```bash theme={null}
    curl \
      -X POST \
      -H "Authorization: Bearer $apikey" \
      https://api.eyequant.com/v2/analyses/summary/$analysisId
    ```
  </Step>

  <Step title="Poll for the result">
    Poll the `GET` endpoint every few seconds until `status` is `completed` (or `failed`, or a `410 Gone`). When complete, the Markdown content is in `data`.

    ```bash theme={null}
    curl \
      -H "Authorization: Bearer $apikey" \
      -X GET \
      https://api.eyequant.com/v2/analyses/summary/$analysisId
    ```
  </Step>
</Steps>

## JavaScript example

```javascript theme={null}
const baseUrl = 'https://api.eyequant.com/v2';

async function requestJson(path, options = {}) {
  const headers = {
    Authorization: `Bearer ${apiKey}`,
    ...options.headers,
  };

  if (options.body) {
    headers['Content-Type'] = 'application/json';
  }

  const response = await fetch(`${baseUrl}${path}`, { ...options, headers });
  const body = await response.json();

  if (!response.ok) {
    throw new Error(body.msg || body.error?.message || 'Request failed');
  }

  return body;
}

async function waitForGeneratedResult(path) {
  while (true) {
    const result = await requestJson(path);

    if (result.status === 'completed') {
      return result.data; // Markdown string
    }

    if (result.status === 'failed' || result.status === 'expired') {
      throw new Error(result.msg);
    }

    await new Promise((resolve) => setTimeout(resolve, 3000));
  }
}

await requestJson(`/update-meta/screenshot/${analysisId}`, {
  method: 'PUT',
  body: JSON.stringify({
    description: 'Homepage hero',
    goal: 'Increase sign-ups',
  }),
});

await requestJson(`/analyses/summary/${analysisId}`, { method: 'POST' });

const summaryMarkdown = await waitForGeneratedResult(
  `/analyses/summary/${analysisId}`
);
```

To generate recommendations instead, use `/analyses/recommendations/${analysisId}` for both the `POST` trigger and the polling path.

## Result expiry

Summary and recommendation tasks become unavailable one hour after they were started, after which the request returns `410 Gone`. If this happens, re-trigger generation with a `POST` and poll again.

## Errors

The generated-content endpoints use a `{ "success": false, "msg": "..." }` error shape:

* **`404` `analysis_not_found`** — no analysis with that id for your account.
* **`404` `summary_not_found`** (or `recommendations_not_found`) — nothing generated yet; trigger one with `POST` first.
* **`410` `analysis_expired`** — the task is older than one hour.
* **`500` generation failed** — re-trigger with `POST`.
