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

# Annual Report Fee

> Quote what a state charges for a company's annual report, before anything is filed.

<Info>
  This endpoint is **read-only**. It quotes a price; it neither starts a filing, stores anything, nor charges the customer.
</Info>

Most states price an annual report from data doola already holds, so the amount comes back on the first call and there is nothing to collect. A small number price it from figures only the customer knows. For those, the first call tells you which fields to gather; the second call, carrying them, returns the amount.

Two fields carry the shape of the answer, and they answer different questions:

* **`filingRequired`** — is there an annual report to file in this state at all?
* **`needsInput`** — can the fee be quoted yet, or are figures still needed?

Read both before reading `amount`. An amount of `0` on its own is ambiguous: some states file the report free of charge, and some file no report at all.

## Quoting the fee

```
POST /v1/partner/companies/{companyId}/compliance/annual-report/fee
```

The body is optional in full. A bare `POST` with no body asks what the state needs.

| Body member  | Meaning                                                                                                                                                                                       |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fiscalYear` | Year the quote is for, `2000`–`2100`. Defaults to the current year. Echoed back so you can tag what you store; the state fee does not vary by year today.                                     |
| `answers`    | Figures for the fields a prior call named in `inputFieldIds`, each field at most once. **Omit** to be told which fields are needed. Send an **empty list** to declare that they are all zero. |

Omitting `answers` and sending `[]` are different requests, not two spellings of the same one:

| You send             | You are saying                     | You get back                         |
| -------------------- | ---------------------------------- | ------------------------------------ |
| no body, or `{}`     | "What does this state price from?" | The amount, or the fields to collect |
| `{"answers": []}`    | "Every figure is zero."            | The amount                           |
| `{"answers": [...]}` | "Here are the figures."            | The amount                           |

## When the amount comes straight back

Most calls end here. The state prices from data doola already holds, so the quote is settled on the first request.

```json theme={null}
{
  "doolaCompanyId": "2Ns8vJqZ1lQwErTyUiOpAsDfGhJ",
  "fiscalYear": 2026,
  "state": "FL",
  "entityType": "LLC",
  "filingRequired": true,
  "pricingType": "FIXED",
  "needsInput": false,
  "inputFieldIds": null,
  "amount": 138.75,
  "currency": "USD",
  "formula": null,
  "message": "Fixed state fee"
}
```

Some states compute the amount rather than reading it off a table — Delaware from the company's authorized shares, for instance. Those still answer immediately, and `formula` shows the working. **A computed fee is not a request for input**; only `needsInput` tells you which case you are in.

## When figures are needed first

```json theme={null}
{
  "doolaCompanyId": "2Ns8vJqZ1lQwErTyUiOpAsDfGhJ",
  "fiscalYear": 2026,
  "state": "WY",
  "entityType": "LLC",
  "filingRequired": true,
  "pricingType": "DYNAMIC",
  "needsInput": true,
  "inputFieldIds": ["Q16", "Q17", "Q18", "Q19", "Q20", "Q21", "Q22", "Q23", "Q24",
                    "Q25", "Q26", "Q27", "Q28", "Q29", "Q30", "Q31", "Q32"],
  "amount": null,
  "currency": "USD",
  "formula": null,
  "message": null
}
```

That is Wyoming's asset worksheet: a yes/no gate followed by the lines it opens up. Collect those fields — their labels, types and conditional logic are in [Annual report requirements](/api/compliance/annual-report-requirements) — then call again:

```json theme={null}
{
  "fiscalYear": 2026,
  "answers": [
    { "fieldId": "Q16", "value": "Yes" },
    { "fieldId": "Q17", "value": "1000000" }
  ]
}
```

| Response field   | Meaning                                                                                 |
| ---------------- | --------------------------------------------------------------------------------------- |
| `doolaCompanyId` | The company the fee was quoted for.                                                     |
| `fiscalYear`     | The year you asked for, or the current year when you did not send one.                  |
| `state`          | US state the report is filed in.                                                        |
| `entityType`     | `LLC` or `CCorp`. The quote applies to this entity type only.                           |
| `filingRequired` | Whether the state requires an annual report at all. See below.                          |
| `pricingType`    | `FIXED`, `DYNAMIC`, `FREE` or `NO_REPORT` — how the state arrives at the fee.           |
| `needsInput`     | `true` when figures are still needed. Decides which of the fields below carry anything. |
| `inputFieldIds`  | Fields to collect, `null` unless `needsInput`.                                          |
| `amount`         | The state fee, `null` when `needsInput`.                                                |
| `currency`       | Always `USD`.                                                                           |
| `formula`        | How a computed amount was arrived at, `null` for a fee read off a table.                |
| `message`        | Plain-language note about the fee, `null` when `needsInput`.                            |

## Zero is two different situations

Some states file an annual report and charge nothing for it. Others file no annual report at all. Both come back with `amount: 0`, and only `filingRequired` tells them apart.

|                         | `filingRequired` | `pricingType`        | `amount` | What to do                          |
| ----------------------- | ---------------- | -------------------- | -------- | ----------------------------------- |
| Most states             | `true`           | `FIXED` or `DYNAMIC` | the fee  | Collect the fee, file the report    |
| Filed at no charge      | `true`           | `FREE`               | `0`      | File the report, charge nothing     |
| No report in this state | **`false`**      | `NO_REPORT`          | `0`      | Nothing to file, nothing to collect |

<Warning>
  Do not branch on `amount === 0`. Branch on `filingRequired`. Treating a `NO_REPORT` state as free-but-filable would have you gather answers and attempt a filing that cannot be made.
</Warning>

`filingRequired: false` is a normal answer, not an error — as of today it covers Alabama, New Mexico, Ohio and South Carolina, which between them hold a meaningful share of doola companies. [Annual report requirements](/api/compliance/annual-report-requirements) reports the same `filingRequired: false` for those companies, with an empty `fields` list.

## Sending answers

Answers are keyed by `fieldId`, and each field may appear **at most once**. A repeated `fieldId` is rejected with `400` rather than resolved: two figures for one field is a request with no correct answer, and quietly picking one would be a guess about money.

A `fieldId` the state does not price from is **ignored**. This matters because the requirements list mixes the state's questions with company details, some of them numeric, so posting the whole list back is a natural mistake. Send what `inputFieldIds` names and nothing more, but a stray field will not corrupt the quote.

`value` is always a string, whatever the field's `type`. A blank or unparseable figure counts as zero rather than failing the quote, so a half-filled worksheet still returns a price.

## Errors

| HTTP                        | When                                                                                                         |
| --------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `400 E_VALIDATION_FAILED`   | `fiscalYear` is outside `2000`–`2100`, an answer is missing its `fieldId`, or a `fieldId` is answered twice. |
| `400 E_MALFORMED_JSON`      | The body is not parseable JSON.                                                                              |
| `404 E_COMPANY_NOT_FOUND`   | The company does not exist, or is not owned by your partner account.                                         |
| `422 E_STATE_NOT_SUPPORTED` | doola holds no annual-report data for the company's state and entity type.                                   |

<Note>
  `422` should not occur for a company created through this API. Every US state and DC is covered for both `LLC` and `CCorp`, so it is declared for completeness — a state added before its pricing lands, or an entity type outside this API's reach. A state that simply files no annual report is **not** a `422`: it answers `200` with `filingRequired: false`.
</Note>

This endpoint and [Annual report requirements](/api/compliance/annual-report-requirements) answer `422` on the same condition, and report the same `filingRequired`, because both read the same source.

Ownership is enforced the same way across the Partner API: a company that exists but isn't yours returns `404`, never `403`, so existence cannot be probed across partners. See [Errors](/api/errors).
