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

# Approvals API: governance workflow

> Create and manage governance approvals that authorize cap table actions — submit, step through reviews, and lock before they gate token minting.

Approvals are governance records that capture the decision chain required before a cap table action is finalized. Every approval moves through a defined lifecycle: it starts as a **Draft**, is submitted for review (**Submitted**), advances as each step is decided (**Approved** once all steps pass), and is finally locked (**Locked**) to make it immutable. An approval must be in the **Locked** state before it can gate token minting through the Securities table.

You can attach multiple sequential steps to an approval — for example, a legal review step followed by a board vote step. Each step carries its own `PENDING` → `APPROVED` / `REJECTED` status. The parent approval transitions to `Approved` only when every step resolves to `APPROVED`.

***

## List approvals

Returns a cursor-paginated list of approval requests for your organization. Filter by `status` or `entityType` to narrow results.

**`GET /api/v1/approvals`**

| Query param  | Type    | Description                                                                  |
| ------------ | ------- | ---------------------------------------------------------------------------- |
| `status`     | string  | Filter by status: `Draft`, `Submitted`, `Approved`, `Rejected`, or `Locked`. |
| `entityType` | string  | Filter by entity type (e.g. `Stock Issuance`, `Option Grant`).               |
| `limit`      | integer | Page size. Defaults to 25, maximum 100.                                      |
| `cursor`     | string  | Opaque cursor from the previous page's `nextCursor`.                         |

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://launchboard.xyz/api/v1/approvals?status=Approved" \
    -H "Authorization: Bearer pg_live_your_key"
  ```
</CodeGroup>

**Response**

```json theme={null}
{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440050",
      "orgId": "550e8400-e29b-41d4-a716-446655440000",
      "title": "Series A — common stock issuance to Alice Chen",
      "entityType": "Stock Issuance",
      "state": "Approved",
      "dueDate": "2024-03-31",
      "notes": "Board approved at March meeting.",
      "lockedAt": null,
      "createdAt": "2024-03-01T09:00:00.000Z",
      "updatedAt": "2024-03-15T16:00:00.000Z"
    }
  ],
  "nextCursor": null
}
```

***

## Create an approval

Creates a new approval request in **Draft** state. You can optionally provide initial steps in the same request.

**`POST /api/v1/approvals`**

Requires role: **EDITOR**. Idempotent with an `Idempotency-Key` header.

<ParamField body="title" type="string" required>
  Human-readable title for the approval (e.g. `"Q1 option grants — engineering cohort"`). Maximum 200 characters.
</ParamField>

<ParamField body="entityType" type="string" required>
  The type of equity event this approval covers. One of: `Stock Issuance`, `Option Grant`, `RSU Grant`, `Warrant Issuance`, `Convertible Issuance`, `Stock Transfer`, `Exercise`, `Conversion`, `Cancellation`, `Repurchase`, `Valuation`, `Equity Plan`, `Stock Split`, `Other`.
</ParamField>

<ParamField body="dueDate" type="string">
  Deadline for completing this approval (`YYYY-MM-DD`).
</ParamField>

<ParamField body="notes" type="string">
  Free-text context or instructions. Maximum 2000 characters.
</ParamField>

<ParamField body="steps" type="array">
  Initial steps to create alongside the approval. Each element requires a `name` (string, max 100 chars). Steps can also be added later via `POST /approvals/{id}/steps`.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://launchboard.xyz/api/v1/approvals \
    -H "Authorization: Bearer pg_live_your_key" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: approval-series-a-issuance-2024" \
    -d '{
      "title": "Series A — common stock issuance to Alice Chen",
      "entityType": "Stock Issuance",
      "dueDate": "2024-03-31",
      "notes": "Requires legal review and board vote.",
      "steps": [
        { "name": "Legal review" },
        { "name": "Board vote" }
      ]
    }'
  ```
</CodeGroup>

**Response — `201 Created`**

```json theme={null}
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440050",
    "orgId": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Series A — common stock issuance to Alice Chen",
    "state": "Draft",
    "lockedAt": null,
    "createdAt": "2024-03-01T09:00:00.000Z",
    "updatedAt": "2024-03-01T09:00:00.000Z"
  }
}
```

***

## Get an approval

Retrieves a single approval by ID.

**`GET /api/v1/approvals/{id}`**

Requires role: **VIEWER**.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://launchboard.xyz/api/v1/approvals/550e8400-e29b-41d4-a716-446655440050 \
    -H "Authorization: Bearer pg_live_your_key"
  ```
</CodeGroup>

***

## Update an approval

Updates a **Draft** approval's metadata. You cannot update an approval that has been submitted or locked.

**`PATCH /api/v1/approvals/{id}`**

Requires role: **EDITOR**. Returns `409 Conflict` if the approval is not in `Draft` state.

<ParamField body="title" type="string">
  Updated title. Maximum 200 characters.
</ParamField>

<ParamField body="entityType" type="string">
  Updated entity type.
</ParamField>

<ParamField body="dueDate" type="string">
  Updated deadline (`YYYY-MM-DD`).
</ParamField>

<ParamField body="notes" type="string">
  Updated notes. Maximum 2000 characters.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://launchboard.xyz/api/v1/approvals/550e8400-e29b-41d4-a716-446655440050 \
    -H "Authorization: Bearer pg_live_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "dueDate": "2024-04-15",
      "notes": "Extended deadline — awaiting legal sign-off."
    }'
  ```
</CodeGroup>

***

## Delete an approval

Permanently deletes a **Draft** approval and all its steps. Returns `409 Conflict` if the approval has been submitted.

**`DELETE /api/v1/approvals/{id}`**

Requires role: **EDITOR**.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE https://launchboard.xyz/api/v1/approvals/550e8400-e29b-41d4-a716-446655440050 \
    -H "Authorization: Bearer pg_live_your_key"
  ```
</CodeGroup>

**Response — `204 No Content`**

***

## Submit for review

Moves a **Draft** approval to **Submitted**, signalling that it is ready for step decisions. Returns `409 Conflict` if the approval is already past Draft state.

**`POST /api/v1/approvals/{id}/submit`**

Requires role: **EDITOR**. No request body.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://launchboard.xyz/api/v1/approvals/550e8400-e29b-41d4-a716-446655440050/submit \
    -H "Authorization: Bearer pg_live_your_key"
  ```
</CodeGroup>

**Response — `200 OK`**

Returns the updated approval object with `state: "Submitted"`.

***

## Add a step

Appends a new approval step to an existing approval. Steps can only be added while the approval is in **Draft** or **Submitted** state.

**`POST /api/v1/approvals/{id}/steps`**

Requires role: **EDITOR**. Returns `409 Conflict` if the approval is locked.

<ParamField body="name" type="string" required>
  Display name for the step (e.g. `"CFO sign-off"`). Maximum 100 characters.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://launchboard.xyz/api/v1/approvals/550e8400-e29b-41d4-a716-446655440050/steps \
    -H "Authorization: Bearer pg_live_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "CFO sign-off"
    }'
  ```
</CodeGroup>

**Response — `201 Created`**

```json theme={null}
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440060",
    "approvalId": "550e8400-e29b-41d4-a716-446655440050",
    "status": "PENDING",
    "order": 3,
    "decidedAt": null,
    "comment": null,
    "createdAt": "2024-03-02T11:00:00.000Z"
  }
}
```

<ResponseField name="data.id" type="string (uuid)">
  Unique step ID. Reference this as `{stepId}` when updating or deleting the step.
</ResponseField>

<ResponseField name="data.status" type="string">
  `PENDING`, `APPROVED`, or `REJECTED`.
</ResponseField>

<ResponseField name="data.order" type="integer">
  1-based position of this step in the approval sequence.
</ResponseField>

***

## Lock an approval

Locks an **Approved** approval, making it immutable and eligible to gate token minting. Returns `409 Conflict` if the approval is not in `Approved` state (i.e. all steps must be approved first).

**`POST /api/v1/approvals/{id}/lock`**

Requires role: **EDITOR**. No request body.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://launchboard.xyz/api/v1/approvals/550e8400-e29b-41d4-a716-446655440050/lock \
    -H "Authorization: Bearer pg_live_your_key"
  ```
</CodeGroup>

**Response — `200 OK`**

Returns the updated approval object with `state: "Locked"` and `lockedAt` set to the current timestamp.

<Warning>
  Locking is irreversible. Once an approval is `Locked`, its state and steps cannot be modified. Create a new approval if you need to re-authorize the same action.
</Warning>

***

## Approval lifecycle

The diagram below shows the valid state transitions:

| From state  | Transition          | To state    |
| ----------- | ------------------- | ----------- |
| `Draft`     | `POST /{id}/submit` | `Submitted` |
| `Submitted` | All steps approved  | `Approved`  |
| `Submitted` | Any step rejected   | `Rejected`  |
| `Approved`  | `POST /{id}/lock`   | `Locked`    |

<Note>
  An approval must reach `Locked` status before it can authorize token minting in the Securities table. Approvals in `Draft`, `Submitted`, or `Approved` state are not yet eligible.
</Note>

***

## Error codes

| Status | When it occurs                                                                                                 |
| ------ | -------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing or invalid API key.                                                                                    |
| `403`  | Key role is below the required level.                                                                          |
| `404`  | Approval or step not found, or does not belong to your organization.                                           |
| `409`  | State transition is not permitted (e.g. trying to submit a `Locked` approval, or delete a non-Draft approval). |
| `422`  | Request body failed validation.                                                                                |
