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

# Validation

# Validation

Run validation checks against your synthetic datasets. Lucitra scores data across four dimensions — coverage, physics plausibility, distribution quality, and sim-to-real gap — each producing a score from 0 to 100.

## Validation Types

<CardGroup cols={2}>
  <Card title="Coverage" icon="grid">
    Checks whether your dataset adequately covers required scenarios, object classes, edge cases, and environmental conditions.
  </Card>

  <Card title="Physics" icon="atom">
    Detects physically impossible artifacts — intersecting geometry, impossible lighting, unrealistic materials, and gravity violations.
  </Card>

  <Card title="Sim-to-Real" icon="arrows-left-right">
    Quantifies domain gap by comparing your synthetic data against a real-world reference dataset.
  </Card>

  <Card title="Full" icon="check-double">
    Runs all validation checks in a single pass. Requires a reference dataset for the sim-to-real component.
  </Card>
</CardGroup>

## Run a Validation

All validation endpoints accept a dataset ID and return immediately with a `run_id` for polling. The actual validation runs asynchronously.

<Tabs>
  <Tab title="Coverage">
    Evaluate scenario and class coverage without a reference dataset.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.lucitra.io/v1/validate/coverage \
        -H "Authorization: Bearer luci_your_api_key" \
        -H "Content-Type: application/json" \
        -d '{
          "dataset_id": "ds_7kx9m2",
          "options": {
            "min_class_samples": 50,
            "required_conditions": ["rain", "night", "fog"]
          }
        }'
      ```

      ```python Python theme={null}
      resp = requests.post(
          "https://api.lucitra.io/v1/validate/coverage",
          headers={"Authorization": "Bearer luci_your_api_key"},
          json={
              "dataset_id": "ds_7kx9m2",
              "options": {
                  "min_class_samples": 50,
                  "required_conditions": ["rain", "night", "fog"],
              },
          },
      )
      run = resp.json()
      ```
    </CodeGroup>

    <ParamField body="dataset_id" type="string" required>
      The dataset to validate.
    </ParamField>

    <ParamField body="options" type="object">
      Validation-specific configuration. For coverage, you can set `min_class_samples` and `required_conditions`.
    </ParamField>
  </Tab>

  <Tab title="Physics">
    Check for physically implausible artifacts in the dataset.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.lucitra.io/v1/validate/physics \
        -H "Authorization: Bearer luci_your_api_key" \
        -H "Content-Type: application/json" \
        -d '{
          "dataset_id": "ds_7kx9m2",
          "options": {
            "check_gravity": true,
            "check_intersections": true,
            "check_lighting": true
          }
        }'
      ```

      ```python Python theme={null}
      resp = requests.post(
          "https://api.lucitra.io/v1/validate/physics",
          headers={"Authorization": "Bearer luci_your_api_key"},
          json={
              "dataset_id": "ds_7kx9m2",
              "options": {
                  "check_gravity": True,
                  "check_intersections": True,
                  "check_lighting": True,
              },
          },
      )
      run = resp.json()
      ```
    </CodeGroup>

    <ParamField body="dataset_id" type="string" required>
      The dataset to validate.
    </ParamField>

    <ParamField body="options" type="object">
      Toggle individual physics checks: `check_gravity`, `check_intersections`, `check_lighting`.
    </ParamField>
  </Tab>

  <Tab title="Sim-to-Real">
    Compare synthetic data against a real-world reference dataset to measure domain gap.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.lucitra.io/v1/validate/sim-to-real \
        -H "Authorization: Bearer luci_your_api_key" \
        -H "Content-Type: application/json" \
        -d '{
          "dataset_id": "ds_7kx9m2",
          "reference_dataset_id": "ds_real_abc",
          "options": {
            "feature_extractor": "resnet50",
            "distance_metric": "fid"
          }
        }'
      ```

      ```python Python theme={null}
      resp = requests.post(
          "https://api.lucitra.io/v1/validate/sim-to-real",
          headers={"Authorization": "Bearer luci_your_api_key"},
          json={
              "dataset_id": "ds_7kx9m2",
              "reference_dataset_id": "ds_real_abc",
              "options": {
                  "feature_extractor": "resnet50",
                  "distance_metric": "fid",
              },
          },
      )
      run = resp.json()
      ```
    </CodeGroup>

    <ParamField body="dataset_id" type="string" required>
      The synthetic dataset to validate.
    </ParamField>

    <ParamField body="reference_dataset_id" type="string" required>
      A real-world dataset to compare against.
    </ParamField>

    <ParamField body="options" type="object">
      Configure `feature_extractor` and `distance_metric` for the comparison.
    </ParamField>
  </Tab>

  <Tab title="Full">
    Run all four validation dimensions in a single pass.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST https://api.lucitra.io/v1/validate/full \
        -H "Authorization: Bearer luci_your_api_key" \
        -H "Content-Type: application/json" \
        -d '{
          "dataset_id": "ds_7kx9m2",
          "reference_dataset_id": "ds_real_abc",
          "options": {}
        }'
      ```

      ```python Python theme={null}
      resp = requests.post(
          "https://api.lucitra.io/v1/validate/full",
          headers={"Authorization": "Bearer luci_your_api_key"},
          json={
              "dataset_id": "ds_7kx9m2",
              "reference_dataset_id": "ds_real_abc",
              "options": {},
          },
      )
      run = resp.json()
      ```
    </CodeGroup>

    <ParamField body="dataset_id" type="string" required>
      The synthetic dataset to validate.
    </ParamField>

    <ParamField body="reference_dataset_id" type="string" required>
      A real-world reference dataset. Required for the sim-to-real component.
    </ParamField>

    <ParamField body="options" type="object">
      Combined options for all validation types.
    </ParamField>
  </Tab>
</Tabs>

All validation endpoints return **202 Accepted**:

```json theme={null}
{
  "run_id": "run_3fa85f64",
  "status": "queued"
}
```

## Poll for Status

Validation runs progress through three statuses before completing.

```
queued → running → completed | failed
```

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.lucitra.io/v1/validate/run_3fa85f64/status" \
    -H "Authorization: Bearer luci_your_api_key"
  ```

  ```python Python theme={null}
  resp = requests.get(
      "https://api.lucitra.io/v1/validate/run_3fa85f64/status",
      headers={"Authorization": "Bearer luci_your_api_key"},
  )
  status = resp.json()
  ```
</CodeGroup>

<Accordion title="Response">
  ```json theme={null}
  {
    "run_id": "run_3fa85f64",
    "status": "running",
    "progress": 0.45,
    "estimated_time_remaining": 120,
    "report_id": null
  }
  ```

  <ResponseField name="run_id" type="string" required>
    The validation run identifier.
  </ResponseField>

  <ResponseField name="status" type="string" required>
    Current status: `queued`, `running`, `completed`, or `failed`.
  </ResponseField>

  <ResponseField name="progress" type="float" required>
    Completion fraction from 0.0 to 1.0.
  </ResponseField>

  <ResponseField name="estimated_time_remaining" type="integer">
    Estimated seconds until completion. Null when queued or failed.
  </ResponseField>

  <ResponseField name="report_id" type="string">
    The generated report ID. Only present when `status` is `completed`.
  </ResponseField>
</Accordion>

## Polling Pattern

Use this pattern to wait for a validation run to complete.

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

  def wait_for_validation(run_id, api_key, interval=5, timeout=600):
      """Poll until validation completes or times out."""
      url = f"https://api.lucitra.io/v1/validate/{run_id}/status"
      headers = {"Authorization": f"Bearer {api_key}"}
      elapsed = 0

      while elapsed < timeout:
          resp = requests.get(url, headers=headers)
          resp.raise_for_status()
          data = resp.json()

          if data["status"] == "completed":
              print(f"Validation complete. Report: {data['report_id']}")
              return data
          elif data["status"] == "failed":
              raise RuntimeError(f"Validation failed: {data}")

          print(f"Status: {data['status']} | Progress: {data['progress']:.0%}")
          time.sleep(interval)
          elapsed += interval

      raise TimeoutError(f"Validation {run_id} did not complete within {timeout}s")


  result = wait_for_validation("run_3fa85f64", "luci_your_api_key")
  ```

  ```bash Bash theme={null}
  #!/bin/bash
  RUN_ID="run_3fa85f64"
  API_KEY="luci_your_api_key"

  while true; do
    RESPONSE=$(curl -s "https://api.lucitra.io/v1/validate/${RUN_ID}/status" \
      -H "Authorization: Bearer ${API_KEY}")

    STATUS=$(echo "$RESPONSE" | jq -r '.status')
    PROGRESS=$(echo "$RESPONSE" | jq -r '.progress')

    echo "Status: ${STATUS} | Progress: ${PROGRESS}"

    if [ "$STATUS" = "completed" ]; then
      REPORT_ID=$(echo "$RESPONSE" | jq -r '.report_id')
      echo "Done. Report ID: ${REPORT_ID}"
      break
    elif [ "$STATUS" = "failed" ]; then
      echo "Validation failed."
      exit 1
    fi

    sleep 5
  done
  ```
</CodeGroup>

## Rate Limits

<Warning>
  Each plan includes a monthly validation run limit. If you exceed your quota, the API returns **429 Too Many Requests** with details about when your limit resets.

  ```json theme={null}
  {
    "error": "monthly_run_limit_exceeded",
    "limit": 100,
    "used": 100,
    "resets_at": "2026-04-01T00:00:00Z"
  }
  ```
</Warning>

<Info>
  Use [webhooks](/guides/webhooks) instead of polling for production workflows. They deliver real-time notifications when validation runs complete or fail.
</Info>
