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

# Report lifecycle

> From `202 Accepted` to a completed report — the four states a report passes through and what to expect at each.

Reports are async. `POST /v1/analyze` returns immediately with `202 Accepted` and a `report_id`. The actual analysis runs in the background and typically completes in **60–120 seconds**.

Here's the full state diagram:

```
AUTHORIZED  →  PROCESSING  →  COMPLETED
                     ↓
                   FAILED
```

## States

| State | What it means | Fields available |
| - | - | - |
| `authorized` | Report accepted, waiting for the worker to pick it up | `report_id`, `mode`, `created_at`, `poll_url` |
| `processing` | Worker is running the analysis | Same as `authorized` |
| `completed` | Analysis finished successfully | Full report body — see below |
| `failed` | Analysis hit an unrecoverable error | `report_id`, `status`, `failed_reason` |

> The API returns lowercase status strings (`"completed"`, `"failed"`). The internal database uses uppercase (`COMPLETED`, `FAILED`) — you'll only see the lowercase form.

## What you get back when it's done

A completed report includes:

```json theme={null}
{
  "report_id": "rpt_01HY2Z...",
  "status": "completed",
  "mode": "off_grid",
  "property": {
    "address": "123 Canyon Rd, Taos, NM",
    "state": "NM",
    "county": "Taos",
    "lat": 36.4072,
    "lng": -105.5734,
    "acreage": 5.2,
    "asking_price": 65000
  },
  "scores": {
    "overall": 78,
    "sub_scores": { "solar": 92, "water": 64, "septic": 71, "building_codes": 80, "access": 83 }
  },
  "confidence": {
    "solar": "high", "water": "medium", "septic": "medium", "building_codes": "high", "access": "high"
  },
  "summary": "Strong off-grid fundamentals — top-tier solar, accessible groundwater. Material risk is county-level RV/septic rules pending verification.",
  "details": {
    "solar": { "score": 92, "summary": "...", "details": ["..."] },
    "water": { "score": 64, "summary": "...", "details": ["..."] },
    "buildability": {
      "score": 71, "summary": "...", "details": ["..."],
      "rv_living_allowed": "conditional",
      "composting_toilet_allowed": "conditional",
      "permit_difficulty": "moderate"
    },
    "access": { "score": 83, "summary": "...", "details": ["..."] }
  },
  "considerations": [
    "Verify county regulations with planning department",
    "Get well drilling quotes from local contractors"
  ],
  "estimated_costs": {
    "solar": { "low": 18000, "high": 30000, "notes": "..." },
    "well": { "low": 7500, "high": 25000, "notes": "..." },
    "septic": { "low": 5000, "high": 22000, "notes": "..." },
    "road": { "low": 0, "high": 3000, "notes": "..." },
    "permits": { "low": 1000, "high": 3000, "notes": "..." },
    "total_low": 31500,
    "total_high": 83000
  },
  "sources": [
    { "name": "NREL PVWatts v8", "url": "https://developer.nrel.gov/..." }
  ],
  "metadata": { /* whatever you sent on the request */ },
  "generated_at": "2026-04-28T22:00:00.000Z"
}
```

The exact sub-score keys depend on the [analysis mode](/concepts/analysis-modes).
The `details` object carries the per-topic narrative (summary + detail
bullets per topic, plus regulatory extras for `buildability`). The
`considerations` array lists actionable follow-ups. `estimated_costs`
gives cost ranges per development category.

## What you get back when it fails

```json theme={null}
{
  "report_id": "rpt_01HY2Z...",
  "status": "failed",
  "mode": "off_grid",
  "failed_reason": "Geocoding lookup failed for address."
}
```

Common failure reasons:

* Address could not be geocoded (typo, ambiguous, or outside the US)
* Upstream data source (NREL, FEMA, USGS) timed out or returned an error
* AI analysis exceeded retry budget

Failed reports **don't refund** automatically — but if you encounter repeated failures on a valid address, contact support with the `report_id` and we'll credit the report.

## Webhook vs. polling

Two ways to find out when a report is done:

### Webhooks (recommended)

Configure a `webhook_url` on your customer (dashboard) or per-request. AcreLens POSTs the full completed report to your endpoint, signed with HMAC-SHA256. You don't poll. You don't burn rate-limit headroom. See [the webhooks guide](/guides/webhooks).

### Polling

Hit the `poll_url` returned in the initial response. While the report is `authorized` or `processing`, you'll get back the basic envelope. Once it flips to `completed` or `failed`, you'll get the full body.

If you must poll, **wait 30 seconds before the first poll** and back off exponentially after that. Hammering the endpoint will hit `429 rate_limit_exceeded`.

## Format

The API returns structured JSON only — no baked-in PDF, CSV, or other
deliverables. Render in your own UI, generate a branded PDF on your end,
ask an AI to summarize it, push to Notion, drop in Slack — bring your own
format. JSON is the deliverable.

## Idempotency and retries

Use an `Idempotency-Key` header on every `POST /v1/analyze` to make retries safe. Same key + same body returns the original `report_id` without creating a new report or charging again. See [the idempotency guide](/guides/idempotency).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.