# Working with review results

Once a certificate is processed, you can retrieve detailed results to understand what passed, what failed, and why. This guide covers how to fetch results and handle each status type in your application.

## Fetching results

Results are available once processing completes (status is no longer `pending` or `processing`). Use the `expand` parameter to control the level of detail returned.

### Without expansion – Basic status only

```bash
curl https://api.1099policy.com/api/v1/files/certificates/cert_aBcDeFgHiJ \
  -u YOUR_SECRET_KEY:
```

```json
{
  "id": "cert_aBcDeFgHiJ",
  "contractor": "cn_abc123",
  "status": "flagged",
  "created": 1706745600,
  "updated": 1706745620,
  "filename": "acme-corp-coi-2024.pdf",
  "pdf_url": "https://...",
  "review_results": null
}
```

### Abbreviated results – Summary counts

```bash
curl "https://api.1099policy.com/api/v1/files/certificates/cert_aBcDeFgHiJ?expand[]=review_results" \
  -u YOUR_SECRET_KEY:
```

```json
{
  "id": "cert_aBcDeFgHiJ",
  "contractor": "cn_abc123",
  "status": "flagged",
  "created": 1706745600,
  "updated": 1706745620,
  "filename": "acme-corp-coi-2024.pdf",
  "pdf_url": "https://...",
  "review_results": {
    "id": "ca_xYzAbCdEfG",
    "status": "flagged",
    "summary": {
      "total_rules": 5,
      "passed": 4,
      "failed": 1
    },
    "created": 1706745610
  }
}
```

### Full results – Complete parsed data and audit details

```bash
curl "https://api.1099policy.com/api/v1/files/certificates/cert_aBcDeFgHiJ?expand[]=review_results.full" \
  -u YOUR_SECRET_KEY:
```

```json
{
  "id": "cert_aBcDeFgHiJ",
  "contractor": "cn_abc123",
  "status": "flagged",
  "created": 1706745600,
  "updated": 1706745620,
  "filename": "acme-corp-coi-2024.pdf",
  "pdf_url": "https://...",
  "review_results": {
    "id": "ca_xYzAbCdEfG",
    "status": "flagged",
    "parsed_certificate_json": {
      "coverages": {
        "commercial_general_liability": {
          "policy_expiration_date": "03/15/2025",
          "policy_number_string": "CGL-2024-001",
          "limits": {
            "general_aggregate_dollars": 2000000,
            "each_occurrence_dollars": 1000000,
            "products_completed_operations_dollars": 1000000
          }
        },
        "workers_compensation": {
          "policy_expiration_date": "06/30/2024",
          "policy_number_string": "WC-789456"
        }
      },
      "insured": {
        "name": "Acme Corporation LLC"
      },
      "description_of_operations": {
        "signature": true,
        "additional_insured_names": ["Client Corp Inc."]
      }
    },
    "audit_results": [
      {
        "id": "car_aB1cD2eF3g",
        "rule_path": "coverages.commercial_general_liability.limits.general_aggregate_dollars",
        "rule_name": null,
        "result": "pass",
        "message": "Coverage for commercial general liability is sufficient. The limit for general aggregate is $2,000,000, which meets the required $1,000,000.",
        "manually_approved": false,
        "created": 1706745611
      },
      {
        "id": "car_oP7qR8sT9u",
        "rule_path": "coverages.workers_compensation.policy_expiration_date",
        "rule_name": null,
        "result": "fail",
        "message": "Coverage for workers compensation has expired 'policy expiration date'. Policy expired on 06/30/2024.",
        "manually_approved": false,
        "created": 1706745611
      },
      {
        "id": "car_vW0xY1zA2b",
        "rule_path": "description_of_operations.additional_insured_names",
        "rule_name": "Additional Insured Check",
        "result": "pass",
        "message": "Rule for additional insureds was checked",
        "manually_approved": false,
        "created": 1706745612
      }
    ],
    "created": 1706745610,
    "updated": 1706745615
  }
}
```

### When to use each expansion level

| Use case | Expansion | Why |
| --- | --- | --- |
| Dashboard status indicator | None | Just need pass/fail status |
| Summary counts for reporting | `review_results` | Shows total/passed/failed without payload overhead |
| Displaying failure reasons to users | `review_results.full` | Need `audit_results` with messages |
| Debugging parsing issues | `review_results.full` | Need `parsed_certificate_json` to see extracted data |

## Understanding parsed_certificate_json

The `parsed_certificate_json` object contains everything the AI extracted from the PDF. Use this to:

- Debug why a rule failed (was the data extracted correctly?)
- Display certificate details in your UI
- Verify the AI parsed the document as expected

Key sections:

| Field | Contains |
| --- | --- |
| `insured` | The contractor/company named as insured |
| `certificate_holder` | Your organization's info as listed on the certificate |
| `coverages` | Coverage details organized by type |
| `description_of_operations` | Free-text description field, including additional insured names and signature presence |

### Coverage structure example

```json
{
  "coverages": {
    "commercial_general_liability": {
      "limits": {
        "each_occurrence_dollars": 1000000,
        "general_aggregate_dollars": 2000000,
        "products_completed_operations_dollars": 1000000
      },
      "policy_number_string": "CGL-2024-001",
      "policy_expiration_date": "03/15/2025"
    }
  }
}
```

## Understanding audit_results

The `audit_results` array contains one entry for each rule evaluated:

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Unique identifier (prefixed with `car_`) |
| `rule_path` | string or null | The data path that was checked (e.g., `coverages.commercial_general_liability.limits.each_occurrence_dollars`) |
| `rule_name` | string or null | Human-readable rule name, when available |
| `result` | string or null | `pass` or `fail` |
| `message` | string or null | Explanation of the result |
| `manually_approved` | boolean | Whether a compliance reviewer manually approved a failed rule |
| `created` | integer | Unix timestamp of when the evaluation occurred |

Null rule_name
`rule_name` may be `null` for standard coverage limit rules. Use `rule_path` to identify which rule was evaluated, or display the `message` which always describes the result.

### Example: Identifying failed rules

```python
response = requests.get(
    f'https://api.1099policy.com/api/v1/files/certificates/{cert_id}?expand[]=review_results.full',
    auth=('YOUR_SECRET_KEY', '')
)
cert = response.json()

if cert['review_results']:
    failed_rules = [
        r for r in cert['review_results']['audit_results']
        if r['result'] == 'fail' and not r['manually_approved']
    ]

for rule in failed_rules:
        # Use message for display since rule_name may be null
        print(f"Failed: {rule['message']}")
```

## Handling each status

Build your integration to handle all possible statuses:

```python
def handle_certificate_result(certificate):
    status = certificate['status']

if status == 'approved':
        # All requirements met - contractor is cleared
        approve_contractor(certificate['contractor'])

elif status == 'flagged':
        # Some rules failed - needs review
        # Fetch full results to show what failed
        results = fetch_full_results(certificate['id'])
        queue_for_compliance_review(certificate, results)

elif status == 'denied':
        # Manually denied by compliance reviewer
        notify_contractor_denied(certificate['contractor'])

elif status == 'error':
        # Processing failed - see Edge Cases guide
        handle_processing_error(certificate)

elif status in ['pending', 'processing']:
        # Still processing - wait for webhook
        pass
```

## Status-specific guidance

| Status | Recommended action |
| --- | --- |
| `approved` | Proceed with contractor onboarding or activation |
| `flagged` | Present failed rules to compliance team for manual review. They can approve individual rules or request a new certificate. |
| `denied` | Notify contractor. They'll need to obtain different coverage and upload a new certificate. |
| `error` | Typically a file issue. See [Edge Cases & Troubleshooting](/content/docs/working-with-review-results#/index.html) for common causes and solutions. |

## Manually approved rules

When a compliance reviewer manually approves a failed rule, the `manually_approved` field is set to `true`. The rule still shows `result: "fail"` but is treated as passing for overall status calculation.

In your UI, you may want to distinguish these:

```python
for rule in audit_results:
    if rule['result'] == 'pass':
        display_as_passed(rule)
    elif rule['manually_approved']: 
        display_as_manually_approved(rule)  # Show with different styling
    else:
        display_as_failed(rule)
```

## Voided audit results

In rare cases, audit results may be voided (e.g., if evaluated against an incorrect requirement). Voided results are excluded from the API response entirely—they don't appear in `audit_results` and aren't counted in the `summary` totals. There's no need to handle them in your integration.

## Missing review results

If `review_results` is `null`:

| Scenario | Explanation |
| --- | --- |
| Status is `pending` or `processing` | Processing hasn't completed yet |
| Status is `error` | Processing failed before results could be generated |
| Expansion parameter not included | Add `?expand[]=review_results` to your request |

Always check `status` before attempting to access `review_results`.

**Next:** Set up [webhooks](/content/docs/working-with-review-results#/index.html) to receive real-time notifications when processing completes.
