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

# Vesting terms and milestones API reference

> Create reusable vesting term templates, define cliff and schedule parameters, and track event-based milestone completion for equity grants.

Launchboard models vesting in two complementary resources. **Vesting terms** define a reusable vesting schedule — the allocation method, vesting conditions, cliff, and total duration — that you attach to a security at grant time via `vestingTermsId`. **Vesting milestones** track discrete events tied to an individual security (for example, "product launch" or "Series A close") that trigger a percentage of the grant to vest when you mark them complete. The two resources can be used independently or together depending on the grant structure.

## Vesting terms endpoints

| Method   | Path                         | Description                           |
| -------- | ---------------------------- | ------------------------------------- |
| `GET`    | `/api/v1/vesting-terms`      | List vesting terms (cursor-paginated) |
| `POST`   | `/api/v1/vesting-terms`      | Create vesting terms                  |
| `GET`    | `/api/v1/vesting-terms/{id}` | Get a single vesting terms record     |
| `PATCH`  | `/api/v1/vesting-terms/{id}` | Partially update vesting terms        |
| `DELETE` | `/api/v1/vesting-terms/{id}` | Delete vesting terms                  |

## Vesting milestones endpoints

| Method   | Path                                       | Description                                |
| -------- | ------------------------------------------ | ------------------------------------------ |
| `GET`    | `/api/v1/vesting-milestones`               | List vesting milestones (cursor-paginated) |
| `POST`   | `/api/v1/vesting-milestones`               | Create a vesting milestone                 |
| `GET`    | `/api/v1/vesting-milestones/{id}`          | Get a single vesting milestone             |
| `PATCH`  | `/api/v1/vesting-milestones/{id}`          | Partially update a vesting milestone       |
| `DELETE` | `/api/v1/vesting-milestones/{id}`          | Delete a vesting milestone                 |
| `POST`   | `/api/v1/vesting-milestones/{id}/complete` | Mark a milestone as completed              |

***

## List vesting terms

Returns a cursor-paginated list of all vesting term records for your organization.

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

### Query parameters

<ParamField path="query.limit" type="number" default="25">
  Maximum results per page. Capped at 100.
</ParamField>

<ParamField path="query.cursor" type="string">
  Opaque cursor from the previous response's `nextCursor` field.
</ParamField>

### Response

<ResponseField name="items" type="object[]">
  Array of vesting terms objects.

  <Expandable title="Vesting terms fields">
    <ResponseField name="id" type="string">UUID of the vesting terms record.</ResponseField>
    <ResponseField name="orgId" type="string">UUID of the owning organization.</ResponseField>
    <ResponseField name="name" type="string">Human-readable name (e.g., `"4-year / 1-year cliff"`).</ResponseField>
    <ResponseField name="description" type="string | null">Longer description of the vesting schedule.</ResponseField>

    <ResponseField name="allocationType" type="string">
      How share quantities are rounded across periods. One of `CUMULATIVE_ROUNDING`, `CUMULATIVE_ROUND_DOWN`, `FRONT_LOADED`, `BACK_LOADED`, `FRONT_LOADED_TO_SINGLE_TRANCHE`, `BACK_LOADED_TO_SINGLE_TRANCHE`, or `FRACTIONAL`.
    </ResponseField>

    <ResponseField name="periodLengthUnits" type="string">Unit of time for each vesting period (e.g., `"MONTHS"`).</ResponseField>
    <ResponseField name="periodLength" type="number">Length of each period in the above units.</ResponseField>
    <ResponseField name="totalPeriods" type="number">Total number of vesting periods.</ResponseField>
    <ResponseField name="cliffPeriods" type="number">Number of periods before the first vesting event.</ResponseField>
    <ResponseField name="createdAt" type="string">ISO-8601 timestamp with timezone offset.</ResponseField>
    <ResponseField name="updatedAt" type="string">ISO-8601 timestamp with timezone offset.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="nextCursor" type="string | null">
  Cursor for the next page. `null` on the last page.
</ResponseField>

***

## Create vesting terms

Creates a reusable vesting schedule. Once saved, reference the record by its `id` in `vestingTermsId` when issuing securities.

<CodeGroup>
  ```bash 4-year / 1-year cliff (monthly) theme={null}
  curl -X POST https://launchboard.xyz/api/v1/vesting-terms \
    -H "Authorization: Bearer pg_live_your_key" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: create-4yr1yr-cliff" \
    -d '{
      "name": "4-year / 1-year cliff",
      "description": "Standard employee vesting: 25% after 12 months, then monthly over 36 months.",
      "allocationMethod": "CUMULATIVE_ROUNDING",
      "triggerType": "TIME_BASED",
      "totalVestingMonths": 48,
      "cliffMonths": 12,
      "vestingConditions": [
        {
          "id": "cond-cliff",
          "trigger": {
            "type": "TIME_BASED",
            "periodLength": 12,
            "periodType": "MONTHS"
          },
          "portion": { "numerator": 1, "denominator": 4 }
        },
        {
          "id": "cond-monthly",
          "trigger": {
            "type": "TIME_BASED",
            "periodLength": 1,
            "periodType": "MONTHS"
          },
          "portion": { "numerator": 1, "denominator": 36 },
          "cliffConditionId": "cond-cliff"
        }
      ],
      "schedule": {
        "totalMonths": 48,
        "cliffMonths": 12,
        "cliffPercent": 25,
        "vestingFrequency": "MONTHLY"
      }
    }'
  ```
</CodeGroup>

### Request body

<ParamField body="name" type="string" required>
  Human-readable name. Maximum 100 characters.
</ParamField>

<ParamField body="description" type="string" required>
  Description of the vesting schedule. Used in the Launchboard UI and grant notices.
</ParamField>

<ParamField body="allocationMethod" type="string" default="CUMULATIVE_ROUNDING">
  Rounding strategy for share quantities across vesting periods. One of: `CUMULATIVE_ROUNDING`, `CUMULATIVE_ROUND_DOWN`, `FRONT_LOADED`, `BACK_LOADED`, `FRONT_LOADED_TO_SINGLE_TRANCHE`, `BACK_LOADED_TO_SINGLE_TRANCHE`, `FRACTIONAL`.
</ParamField>

<ParamField body="triggerType" type="string" default="TIME_BASED">
  Whether vesting is driven by time, events, or a combination: `TIME_BASED`, `EVENT_BASED`, or `HYBRID`.
</ParamField>

<ParamField body="vestingConditions" type="object[]" required>
  Array of vesting condition objects. At least one is required.

  <Expandable title="Vesting condition fields">
    <ParamField body="vestingConditions[].id" type="string" required>A UUID you assign to identify this condition (used to link cliff and continuation conditions).</ParamField>

    <ParamField body="vestingConditions[].trigger" type="object" required>
      Trigger definition.

      <Expandable title="trigger fields">
        <ParamField body="trigger.type" type="string" required>`TIME_BASED` or `EVENT_BASED`.</ParamField>
        <ParamField body="trigger.periodLength" type="number">Length of the time period (used when `type` is `TIME_BASED`).</ParamField>
        <ParamField body="trigger.periodType" type="string">Time unit: `DAYS`, `MONTHS`, or `YEARS`.</ParamField>
        <ParamField body="trigger.eventDescription" type="string">Description of the event (used when `type` is `EVENT_BASED`).</ParamField>
      </Expandable>
    </ParamField>

    <ParamField body="vestingConditions[].portion" type="object" required>
      Fractional share of the grant that vests on this condition.

      <Expandable title="portion fields">
        <ParamField body="portion.numerator" type="number" required>Non-negative integer numerator.</ParamField>
        <ParamField body="portion.denominator" type="number" required>Positive integer denominator.</ParamField>
      </Expandable>
    </ParamField>

    <ParamField body="vestingConditions[].cliffConditionId" type="string">UUID of the cliff condition that must be satisfied before this condition begins.</ParamField>
    <ParamField body="vestingConditions[].nextConditionId" type="string">UUID of the condition that repeats after this one (for recurring monthly/quarterly conditions).</ParamField>
  </Expandable>
</ParamField>

<ParamField body="schedule" type="object">
  Simplified schedule summary for display purposes.

  <Expandable title="schedule fields">
    <ParamField body="schedule.totalMonths" type="number" required>Total vesting duration in months.</ParamField>
    <ParamField body="schedule.cliffMonths" type="number" required>Cliff duration in months.</ParamField>
    <ParamField body="schedule.cliffPercent" type="number" required>Percentage that vests at the cliff (0–100).</ParamField>
    <ParamField body="schedule.vestingFrequency" type="string" required>Post-cliff frequency: `MONTHLY`, `QUARTERLY`, or `ANNUALLY`.</ParamField>
    <ParamField body="schedule.accelerationTriggers" type="object[]">Optional acceleration events.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="totalVestingMonths" type="number">
  Total vesting duration in months. Convenience field that mirrors `schedule.totalMonths`.
</ParamField>

<ParamField body="cliffMonths" type="number">
  Cliff duration in months. Convenience field that mirrors `schedule.cliffMonths`.
</ParamField>

<ParamField body="comments" type="string">
  Free-text notes.
</ParamField>

### Response

Returns `201 Created` with the full vesting terms object and a `Location` header.

***

## Get vesting terms

<CodeGroup>
  ```bash cURL theme={null}
  curl https://launchboard.xyz/api/v1/vesting-terms/abc123 \
    -H "Authorization: Bearer pg_live_your_key"
  ```
</CodeGroup>

Returns the vesting terms object.

***

## Update vesting terms

Partial update — include only the fields you want to change.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://launchboard.xyz/api/v1/vesting-terms/abc123 \
    -H "Authorization: Bearer pg_live_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "4-year / 1-year cliff (revised)",
      "comments": "Updated allocation method to FRACTIONAL per legal review."
    }'
  ```
</CodeGroup>

All fields from the create body are accepted; all are optional for `PATCH`. Returns `200 OK` with the updated object.

***

## Delete vesting terms

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE https://launchboard.xyz/api/v1/vesting-terms/abc123 \
    -H "Authorization: Bearer pg_live_your_key"
  ```
</CodeGroup>

Returns `204 No Content` on success.

***

## List vesting milestones

Returns a cursor-paginated list of all vesting milestones for your organization.

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

### Query parameters

<ParamField path="query.limit" type="number" default="25">
  Maximum results per page. Capped at 100.
</ParamField>

<ParamField path="query.cursor" type="string">
  Opaque cursor from the previous response's `nextCursor` field.
</ParamField>

### Response

<ResponseField name="items" type="object[]">
  Array of vesting milestone objects.

  <Expandable title="Vesting milestone fields">
    <ResponseField name="id" type="string">UUID of the milestone.</ResponseField>
    <ResponseField name="orgId" type="string">UUID of the owning organization.</ResponseField>
    <ResponseField name="securityId" type="string">UUID of the security this milestone is attached to.</ResponseField>
    <ResponseField name="description" type="string">Description of the milestone event.</ResponseField>
    <ResponseField name="targetDate" type="string">Expected completion date, in `YYYY-MM-DD` format.</ResponseField>
    <ResponseField name="completedAt" type="string | null">Timestamp when the milestone was completed. `null` while still pending.</ResponseField>
    <ResponseField name="quantity" type="string">Number of shares that vest on completion, as a decimal string.</ResponseField>
    <ResponseField name="createdAt" type="string">ISO-8601 timestamp with timezone offset.</ResponseField>
    <ResponseField name="updatedAt" type="string">ISO-8601 timestamp with timezone offset.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="nextCursor" type="string | null">
  Cursor for the next page. `null` on the last page.
</ResponseField>

***

## Create a vesting milestone

Creates a new milestone for a security. The milestone starts in `PENDING` status.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://launchboard.xyz/api/v1/vesting-milestones \
    -H "Authorization: Bearer pg_live_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Series A close",
      "description": "25% of grant vests upon closing a Series A financing of at least $5M.",
      "targetDate": "2024-12-31"
    }'
  ```
</CodeGroup>

### Request body

<ParamField body="name" type="string" required>
  Short name for the milestone. Maximum 100 characters.
</ParamField>

<ParamField body="description" type="string">
  Detailed description of the milestone condition. Maximum 500 characters.
</ParamField>

<ParamField body="targetDate" type="string">
  Expected completion date in `YYYY-MM-DD` format.
</ParamField>

### Response

Returns `201 Created` with the full milestone object and a `Location` header.

***

## Get a vesting milestone

<CodeGroup>
  ```bash cURL theme={null}
  curl https://launchboard.xyz/api/v1/vesting-milestones/abc123 \
    -H "Authorization: Bearer pg_live_your_key"
  ```
</CodeGroup>

Returns the milestone object.

***

## Update a vesting milestone

Partial update for pending milestones. Use this to adjust the target date or description before the milestone is completed.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://launchboard.xyz/api/v1/vesting-milestones/abc123 \
    -H "Authorization: Bearer pg_live_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "targetDate": "2025-03-31",
      "description": "25% vests on Series A close (updated deadline)."
    }'
  ```
</CodeGroup>

All fields from the create body are accepted; all are optional for `PATCH`. Returns `200 OK` with the updated milestone object.

***

## Delete a vesting milestone

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE https://launchboard.xyz/api/v1/vesting-milestones/abc123 \
    -H "Authorization: Bearer pg_live_your_key"
  ```
</CodeGroup>

Returns `204 No Content` on success.

***

## Mark a milestone as completed

Transitions a `PENDING` milestone to `COMPLETED` and records a `completedAt` timestamp. Optionally attach an evidence note describing how the milestone was satisfied.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://launchboard.xyz/api/v1/vesting-milestones/abc123/complete \
    -H "Authorization: Bearer pg_live_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "evidenceNote": "Series A closed on 2024-11-15. Wire confirmation #TXN-88420."
    }'
  ```
</CodeGroup>

### Request body

<ParamField body="evidenceNote" type="string">
  Optional note documenting how the milestone was satisfied. Maximum 1000 characters.
</ParamField>

### Response

Returns `200 OK` with the updated milestone object. The `completedAt` field will be populated with the current timestamp and `status` will be `COMPLETED`.

***

## Common errors

| Status | When it occurs                                                                             |
| ------ | ------------------------------------------------------------------------------------------ |
| `400`  | Malformed JSON in the request body.                                                        |
| `401`  | Missing or invalid `Authorization` header.                                                 |
| `403`  | The API key role does not have `EDITOR` permission. Required for all write operations.     |
| `404`  | No record with the given `id` exists in your organization.                                 |
| `409`  | Attempting to complete a milestone that is already `COMPLETED`.                            |
| `422`  | Request body failed schema validation. The response includes a field-level `errors` array. |

<Note>
  Completing a milestone is an irreversible action. If you need to correct a milestone that was completed in error, contact support or delete the milestone and recreate it.
</Note>
