> ## 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.

# Required actions

> Respond when doola needs something from you before a company's formation can continue.

Most of a formation runs without you. Occasionally it cannot: the state rejects every company name you submitted, or a document has to be signed again. When that happens doola raises a **required action** on the company, tells you over a webhook, and waits.

A required action is the one thing in the Partner API that blocks on you. Until it is resolved, that company's formation does not progress — so treat these events as work items, not notifications.

This page covers what every required action has in common: how you are told, how to read them, and how to recover the ones you missed. Each kind has its own page for the part that differs — how you actually clear it.

<CardGroup cols={2}>
  <Card title="New company names needed" icon="pen-to-square" href="/api/required-actions/name-options">
    `FORMATION_NAME_OPTIONS_EXHAUSTED` — the state rejected every name you submitted. You send replacements.
  </Card>

  <Card title="SS-4 signature needed again" icon="file-signature" href="/api/required-actions/ss4-reset">
    `FORMATION_SIGNATURE_SS4_RESET` — a name change voided a signed SS-4. Your founder signs again.
  </Card>
</CardGroup>

<Note>
  A closed action is never reopened. If the same thing is needed again, doola raises a **new** action with a fresh `requiredActionId`. Key your records on `requiredActionId`, not on the company plus the code.
</Note>

## Anatomy

Every action carries a `requiredActionId`, an `actionCode` that says what is needed, and a `reason` written for a person to read. These are the three you build against:

| Field               | Notes                                                                                 |
| ------------------- | ------------------------------------------------------------------------------------- |
| `requiredActionId`  | Identifier for the action. Resolve and read against it                                |
| `actionCode`        | `FORMATION_NAME_OPTIONS_EXHAUSTED` or `FORMATION_SIGNATURE_SS4_RESET`. Branch on this |
| `reason`            | Explanation to render to your user verbatim. Never parse it                           |
| `status` and `open` | Where the action has got to; see [Statuses](#statuses)                                |

Full field list: [Required action](/api/domain-objects#required-action).

For name rejections the `reason` usually names the exact names the state turned down. When doola could not capture them it falls back to a fixed sentence, `All submitted company name options were rejected by the state. Submit new options.`

## Statuses

An action's `status` is the last thing that happened to it. `open` tells you whether it still needs attention.

| Status            | Set by | Meaning                                                                                                 |
| ----------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `created`         | doola  | The action was raised                                                                                   |
| `delivered`       | doola  | The webhook reached your endpoint. The action is still open and still needs your answer                 |
| `delivery_failed` | doola  | doola could not reach your endpoint after all retries — you will only find this action by listing       |
| `submitted`       | you    | Your resolution was received and is being processed                                                     |
| `resolved`        | doola  | The resolution was accepted; the action is closed                                                       |
| `rejected`        | doola  | The resolution was not accepted, for example the state rejected the new names too; the action is closed |

`resolved` and `rejected` are terminal, and `open` is then `false`. Every other status means the action is still open.

<Warning>
  `submitted` is not done. It means doola has your answer and is acting on it; for name options that means [refiling with the state](/api/required-actions/name-options#the-flow). Wait for the action to close before telling your user the matter is settled.
</Warning>

## How doola tells you

Each code has its own webhook event, and both carry the same payload shape.

| Event                           | Raised for                                                               |
| ------------------------------- | ------------------------------------------------------------------------ |
| `company_name_options_required` | [`FORMATION_NAME_OPTIONS_EXHAUSTED`](/api/required-actions/name-options) |
| `signature_ss4_reset`           | [`FORMATION_SIGNATURE_SS4_RESET`](/api/required-actions/ss4-reset)       |

```json theme={null}
{
  "eventId": "31pLdpvMh4OfO90moxLgLT8AuQr",
  "eventName": "company_name_options_required",
  "eventPayload": {
    "doolaCompanyId": "31pLD0Tq2lm2FsgRBoHv4x3BpzZ",
    "requiredActionId": "31wR7kM2nP5qT8vXyZaBcDeFgHj",
    "actionCode": "FORMATION_NAME_OPTIONS_EXHAUSTED",
    "actionName": "New company names needed",
    "reason": "The state rejected “Acme Holdings LLC” and “Acme Labs LLC”. Submit new company name options to continue the formation."
  },
  "timestamp": 1756216157000
}
```

Delivery follows the standard [webhook contract](/api/webhooks#delivery-and-retries), so treat it as at least once and dedupe on `eventId`. An action doola never managed to deliver is still open and still blocking — see [Reconciling](#reconciling).

## Reading actions

### Everything still open

One call, across every company you own. This is what a dashboard or a startup check should read.

```bash theme={null}
curl "https://api.test.doola.com/v1/partner/required-actions?page=0&size=20" \
  -H "Authorization: dk_test_your_api_key_here"
```

```json theme={null}
{
  "payload": {
    "content": [
      {
        "requiredActionId": "31wR7kM2nP5qT8vXyZaBcDeFgHj",
        "doolaCompanyId": "31pLD0Tq2lm2FsgRBoHv4x3BpzZ",
        "actionCode": "FORMATION_NAME_OPTIONS_EXHAUSTED",
        "actionName": "New company names needed",
        "status": "delivered",
        "reason": "The state rejected “Acme Holdings LLC”. Submit new company name options to continue the formation.",
        "open": true,
        "updatedAt": "2026-08-13T17:33:12.751020"
      }
    ],
    "page": 0,
    "size": 20,
    "total": 1,
    "totalPages": 1
  },
  "error": null
}
```

Closed actions are excluded. `total` is the tenant-wide open count whatever page size you ask for, so `size=1` is a cheap "is anything waiting on me?" check. `size` is capped at 100 — a larger value is clamped rather than rejected.

### One company

```bash theme={null}
curl https://api.test.doola.com/v1/partner/companies/{companyId}/required-actions \
  -H "Authorization: dk_test_your_api_key_here"
```

```json theme={null}
{
  "payload": [
    {
      "requiredActionId": "31wR7kM2nP5qT8vXyZaBcDeFgHj",
      "doolaCompanyId": "31pLD0Tq2lm2FsgRBoHv4x3BpzZ",
      "actionCode": "FORMATION_NAME_OPTIONS_EXHAUSTED",
      "actionName": "New company names needed",
      "status": "resolved",
      "reason": "The state rejected “Acme Holdings LLC”. Submit new company name options to continue the formation.",
      "open": false,
      "updatedAt": "2026-08-14T09:02:44.118220"
    }
  ],
  "error": null
}
```

A plain array under `payload`, unpaged, most recently changed first. Unlike the open list this one keeps `resolved` and `rejected` entries, which is what lets you tell "nothing was ever needed" apart from "something was needed and I missed it".

### One action, with history

```bash theme={null}
curl https://api.test.doola.com/v1/partner/companies/{companyId}/required-actions/{requiredActionId} \
  -H "Authorization: dk_test_your_api_key_here"
```

Adds a `history` array: every step the action has been through, oldest first, including whether doola reached your endpoint and what you submitted.

```json theme={null}
{
  "payload": {
    "requiredActionId": "31wR7kM2nP5qT8vXyZaBcDeFgHj",
    "doolaCompanyId": "31pLD0Tq2lm2FsgRBoHv4x3BpzZ",
    "actionCode": "FORMATION_NAME_OPTIONS_EXHAUSTED",
    "actionName": "New company names needed",
    "status": "submitted",
    "reason": "The state rejected “Acme Holdings LLC”. Submit new company name options to continue the formation.",
    "open": true,
    "updatedAt": "2026-08-13T17:33:32.624556",
    "history": [
      { "status": "created", "createdAt": "2026-08-13T17:33:12.502357" },
      { "status": "delivered", "createdAt": "2026-08-13T17:33:12.751020" },
      {
        "status": "submitted",
        "submittedPayload": {
          "actionCode": "FORMATION_NAME_OPTIONS_EXHAUSTED",
          "nameOptions": [{ "name": "Acme Ventures", "entityTypeEnding": "LLC" }]
        },
        "createdAt": "2026-08-13T17:33:32.624556"
      }
    ]
  },
  "error": null
}
```

<Note>
  `submittedPayload` appears on `submitted` steps only. It is a record of what you sent at that moment, not the company's current state — read the company for that.
</Note>

## Reconciling

Webhooks can be missed — downtime on your side, or an endpoint doola disabled after repeated failures. An action left in `delivery_failed` is still open and still blocking that company, and no further webhook announces it.

Guard against that with one call at startup and on a schedule:

```bash theme={null}
curl "https://api.test.doola.com/v1/partner/required-actions?size=100" \
  -H "Authorization: dk_test_your_api_key_here"
```

Anything there you have no record of is work you missed. Compare `total` against what you received and page with `page=1`, `page=2`, and so on if you ever hold more than 100 open actions at once; for most tenants this is a single request no matter how many companies you have.

## Testing

The [sandbox playground](/api/sandbox-playground) cannot raise a required action today. To exercise the flow end to end in sandbox, ask doola to raise one on a sandbox company at [engineering@doola.com](mailto:engineering@doola.com) — the webhook, the reads, and the resolution endpoint then behave exactly as they do in production. The reads are safe to build against meanwhile: a company with nothing outstanding returns an empty list.

## Errors

Branch on `error.code`, never on the message. The 400-level codes below come from [submitting a resolution](/api/required-actions/name-options#submitting-new-names); the 404s are returned by the reads as well.

| HTTP | Code                               | Meaning                                                                                                                                                                                                       |
| ---- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400  | `E_VALIDATION_FAILED`              | Body shape is wrong: `actionCode` missing, or `nameOptions` missing, empty, more than three, or carrying an unrecognised `entityTypeEnding`. The `error.fields.nameOptions` detail names the rule that failed |
| 400  | `E_REQUIRED_ACTION_CODE_MISMATCH`  | `actionCode` does not match the action you are resolving — you likely mixed up two open actions                                                                                                               |
| 400  | `E_NAME_OPTIONS_INVALID`           | A recognised ending that does not belong to the company's entity type, for example a CCorp ending on an LLC                                                                                                   |
| 404  | `E_REQUIRED_ACTION_NOT_FOUND`      | No action with this ID on this company, including an action that belongs to another partner                                                                                                                   |
| 404  | `E_COMPANY_NOT_FOUND`              | No company with this ID belongs to your tenant                                                                                                                                                                |
| 409  | `E_REQUIRED_ACTION_CLOSED`         | The action is already resolved or rejected. A closed action never reopens; wait for the new one                                                                                                               |
| 422  | `E_REQUIRED_ACTION_NOT_RESOLVABLE` | This action is resolved by another flow, such as an [SS-4 re-signature](/api/required-actions/ss4-reset)                                                                                                      |

Checks run in a fixed order — body shape, company scope, action scope, `actionCode` match, resolvability, then whether the action is still open — so a request with more than one problem reports the first failure in that list. A closed SS-4 reset therefore answers `422`, not `409`.

Anything that is not yours is a `404`, never a `403` — a `403` would confirm the ID exists, which is enough to enumerate another partner's companies.

See [Errors](/api/errors) for the response envelope and the global codes that any endpoint can return.
