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

# Evaluation

> Trigger an asynchronous compliance evaluation of a promotion version and read the results.

Evaluation is an asynchronous compliance review of one promotion version. You trigger it, then receive the result via [webhook](/developer/webhooks) or by polling. Each evaluation returns per-file results, each with a set of issues and their cited rules.

## Trigger evaluation

Evaluation runs asynchronously. The endpoint returns immediately with `202 Accepted`.

<CodeGroup>
  ```bash curl theme={null}
  curl -s -X POST \
    "$API_BASE/v1/promotions/<promotionId>/versions/<versionId>/evaluate" \
    -H "Authorization: Bearer $ADCLEAR_API_KEY" \
    -H "X-External-User-ID: $USER_ID"
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(
    `${API_BASE}/v1/promotions/${promotionId}/versions/${versionId}/evaluate`,
    { method: "POST", headers });
  const { evaluationId } = await res.json();
  ```

  ```python Python theme={null}
  res = requests.post(
      f"{API_BASE}/v1/promotions/{promotion_id}/versions/{version_id}/evaluate",
      headers=headers)
  evaluation_id = res.json()["evaluationId"]
  ```
</CodeGroup>

**Response (202 Accepted):**

```json theme={null}
{ "evaluationId": "eval-uuid-...", "status": "processing", "correlationId": "uuid-..." }
```

Typical processing time is 30 to 90 seconds, depending on content size and complexity.

## Poll evaluation status

Fetch the current status and results for a version. The response includes per-file results, so single-file promotions return an array of one.

```http theme={null}
GET https://public-api.adclear.ai/v1/promotions/{promotionId}/versions/{versionId}/evaluation
```

**Response (200 OK), succeeded:**

```json theme={null}
{
  "status": "succeeded",
  "results": [
    {
      "evaluationId": "eval-uuid-1",
      "uploadId": "upload-uuid-1",
      "fileName": "brochure.pdf",
      "status": "succeeded",
      "result": {
        "reviewRequired": true,
        "overallAssessment": "Requires review",
        "issues": [
          {
            "recommendation": "Consider adding a risk warning.",
            "rationale": "Financial promotions must include risk warnings per FCA guidelines.",
            "severity": "HIGH",
            "citedRules": [
              { "id": "rule-uuid-...", "name": "Risk Warning Requirement", "product": "Investment", "channel": "Digital" }
            ],
            "location": { "type": "text", "content": "Invest now for guaranteed returns", "pageNumber": 1 }
          }
        ]
      }
    }
  ],
  "correlationId": "uuid-..."
}
```

While processing, a file's `status` is `pending` and its `result` is absent. When a file fails, its `status` is `failed` and no `result` is returned. A `404` means no evaluation exists for the version yet; trigger one first.

**Aggregate status:**

| Aggregate `status` | Condition                                    |
| ------------------ | -------------------------------------------- |
| `processing`       | Any individual evaluation is still `pending` |
| `succeeded`        | All evaluations have `succeeded`             |
| `failed`           | At least one `failed`, none `pending`        |

## Recommended flow

1. **Trigger** the evaluation with `POST .../evaluate`.
2. **Receive the result via a [webhook](/developer/webhooks).** This is the recommended approach: Adclear pushes the result to your endpoint as soon as it's ready, with no polling.

Polling is a simpler option for low volume, or a fallback when a webhook is delayed: call `GET .../evaluation` every 5 to 10 seconds until `status` is `succeeded` or `failed`.

<Warning>
  Polling counts against the rate limit of **60 requests per minute per API key, shared across all `/v1/*` endpoints**. Polling one evaluation every 5 seconds uses 12 of those requests a minute; polling several at once will exhaust the budget and return `429`, including for your `create` and `evaluate` calls. Use webhooks once you evaluate at any volume. See [Errors & rate limits](/developer/errors).
</Warning>

### Polling example

<CodeGroup>
  ```bash curl theme={null}
  while true; do
    STATUS=$(curl -s "$API_BASE/v1/promotions/$PROMO/versions/$VERSION/evaluation" \
      -H "Authorization: Bearer $ADCLEAR_API_KEY" -H "X-External-User-ID: $USER_ID" | jq -r .status)
    echo "status: $STATUS"
    [ "$STATUS" = "succeeded" ] || [ "$STATUS" = "failed" ] && break
    sleep 5
  done
  ```

  ```typescript TypeScript theme={null}
  async function pollEvaluation(promotionId: string, versionId: string) {
    const url = `${API_BASE}/v1/promotions/${promotionId}/versions/${versionId}/evaluation`;
    for (let i = 0; i < 30; i++) {
      await new Promise((r) => setTimeout(r, 5000));
      const data = await (await fetch(url, { headers })).json();
      if (data.status === "succeeded") {
        for (const r of data.results) console.log(r.fileName, r.result.issues.length, "issue(s)");
        return data;
      }
      if (data.status === "failed") throw new Error("Evaluation failed");
    }
    throw new Error("Polling timed out");
  }
  ```

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

  def poll_evaluation(promotion_id, version_id):
      url = f"{API_BASE}/v1/promotions/{promotion_id}/versions/{version_id}/evaluation"
      for _ in range(30):  # ~150s at 5s intervals
          time.sleep(5)
          data = requests.get(url, headers=headers).json()
          if data["status"] == "succeeded":
              for r in data["results"]:
                  print(r["fileName"], len(r["result"]["issues"]), "issue(s)")
              return data
          if data["status"] == "failed":
              raise RuntimeError("Evaluation failed")
      raise TimeoutError("Polling timed out")
  ```
</CodeGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Evaluation stuck in processing" icon="clock">
    Typical evaluation time is 30 to 90 seconds. Continue polling every 5 to 10 seconds for up to 5 minutes. If it's still processing after that, re-trigger with `POST .../evaluate`. If it persists, contact support with the `correlationId` and `evaluationId`.

    <Tip>Configure a [webhook](/developer/webhooks) to receive results asynchronously and avoid timeout concerns entirely.</Tip>
  </Accordion>

  <Accordion title="Can I re-evaluate the same version?" icon="circle-question">
    Yes. Each `POST .../evaluate` creates a new evaluation, and `GET .../evaluation` always returns the most recent result for that version. Previous results are retained internally for audit.
  </Accordion>
</AccordionGroup>

For cross-cutting errors (401, 403, 429, 502), see [Errors & rate limits](/developer/errors).
