# Deals

> Full CRUD operations, indexed deal-data search, generated memos, document and vault access, and Deal Room checklist reads for deals in the Lev API.

Source: https://www.lev.com/docs/build/deals

Last updated: July 2026

---
## Overview

The deals endpoints support full CRUD operations with filtering, sorting, pagination, sparse fieldsets, and optional sub-resource includes.

| Endpoint | Description |
|----------|-------------|
| `GET /deals` | List deals with filtering and pagination |
| `GET /deals/{id}` | Get a single deal |
| `POST /deals/{id}/index/search` | Search canonical indexed facts and source provenance for a deal |
| `POST /deals/{id}/index/observations` | List candidate observations (per-document extractions) behind a fact |
| `PATCH /deals/{id}/index/facts` | Update a single canonical fact value (direct replacement or promote an observation) |
| `GET /deals/{id}/index/metric-definitions` | List the metrics that can be recorded on a deal |
| `GET /deals/{id}/index/entities` | List the entities (deal, properties, sponsor) you can attach facts to |
| `POST /deals/{id}/index/facts` | Record one or more user-supplied metric values |
| `GET /deals/{id}/vaults` | List a deal's vaults — its Resources area and any shared Deal Rooms |
| `GET /deals/{id}/documents` | List uploaded documents across all of a deal's vaults |
| `GET /deals/{id}/documents/{document_id}` | Fetch one uploaded document's metadata |
| `GET /deals/{id}/documents/{document_id}/download` | Fetch a short-lived signed download link for one uploaded document |
| `GET /deals/{id}/checklists` | List each Deal Room's checklist — sections, tasks, document types, and task-linked files |
| `GET /deals/{id}/memos` | List a deal's memos (deal books) — published and draft |
| `GET /deals/{id}/memos/{memo_uuid}` | Fetch one memo with signed PDF download links |
| `GET /deals/{id}/notes` | List notes logged on a deal |
| `POST /deals/{id}/notes` | Add a note to a deal |
| `PATCH /deals/{id}/notes/{note_id}` | Edit a deal note |
| `DELETE /deals/{id}/notes/{note_id}` | Delete a deal note |
| `GET /checklist-tasks/{id}/notes` | List comments on a checklist task (borrower-portal comments) |
| `POST /checklist-tasks/{id}/notes` | Add a comment to a checklist task |
| `PATCH /checklist-tasks/{id}/notes/{note_id}` | Edit a checklist task comment |
| `DELETE /checklist-tasks/{id}/notes/{note_id}` | Delete a checklist task comment |
| `POST /checklist-tasks` | Create a checklist task — a document request or to-do on a deal |
| `PATCH /checklist-tasks/{id}` | Update a checklist task — retitle, re-status, reassign, set a due date, or reopen |
| `POST /checklist-tasks/{id}/complete` | Mark a checklist task complete (also approves its attached files) |
| `POST /deals` | Create a new deal, optionally with deal-level financials |
| `PATCH /deals/{id}` | Update deal fields and writable deal-level financials |
| `DELETE /deals/{id}` | Archive (soft-delete) a deal |

## List Deals

### `GET /api/external/v2/deals`

List deals with pagination, filtering, and sorting

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `limit` | `integer` | No | Results per page (1–200, default 50) |
| `cursor` | `string` | No | Cursor for next page (mutually exclusive with sort) |
| `offset` | `integer` | No | Offset for pagination (requires sort) |
| `sort` | `string` | No | Sort fields: title, loan_amount, created_at, updated_at. Prefix with - for descending |
| `fields` | `string` | No | Comma-separated fields to include (id always included) |
| `include` | `string` | No | Sub-resources to embed: financials, properties, team, pipelines |
| `filter[loan_type]` | `LoanType` | No | Filter by loan type enum name, such as heavy_bridge or permanent |
| `filter[transaction_type]` | `TransactionType` | No | Filter by transaction type enum name, such as acquisition or refinance |
| `filter[business_plan]` | `BusinessPlanType` | No | Filter by business plan enum name |
| `filter[archived]` | `boolean` | No | Filter by archived state. Omit to return both active and archived deals; pass true for only archived, false for only active |
| `filter[loan_amount][gte]` | `number` | No | Minimum loan amount |
| `filter[loan_amount][lte]` | `number` | No | Maximum loan amount |
| `filter[created_at][gte]` | `string` | No | Created after (ISO 8601) |
| `filter[created_at][lte]` | `string` | No | Created before (ISO 8601) |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **400** (bad_request): cursor and sort cannot be combined; use offset pagination when sorting

## Get Deal

### `GET /api/external/v2/deals/{deal_id}`

Get a single deal by ID

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `include` | `string` | No | Sub-resources to embed: financials, properties, team, pipelines |
| `fields` | `string` | No | Comma-separated fields to include |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found

## Search Index

### `POST /api/external/v2/deals/{deal_id}/index/search`

Search canonical indexed deal facts by natural-language context

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `context` | `string` | Yes | Natural-language search context (1-4000 characters). Be specific about the metric or topic you want. |
| `min_score` | `number` | No | Minimum similarity threshold from 0 to 1. Defaults to 0.3. |
| `limit` | `integer` | No | Maximum ranked facts to return. Defaults to 20 and is clamped to 1-200. |
| `include_signed_urls` | `boolean` | No | When true, document-backed source records may include short-lived signed URLs for inspection or download. |

Response: `200`

#### Error Responses

- **422** (validation_error): context must not be blank
- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found
- **503** (service_unavailable): Deal data search is temporarily unavailable. Try again shortly.

## Index Observations

### `POST /api/external/v2/deals/{deal_id}/index/observations`

List per-document candidate values behind a deal fact

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `sot_id` | `string` | No | Prefixed SOT id from a search result (e.g. d:5362, a:123:property:690). At least one of sot_id or metric_id is required. |
| `metric_id` | `integer` | No | Numeric metric definition id (> 0). Use when fetching every observation for a metric across entities. |
| `entity_ref` | `string` | No | Optional entity scoping (e.g. property:690). Intended for use with metric_id; combining with sot_id has no additional effect. |
| `page` | `integer` | No | 1-based page number. Defaults to 1. |
| `limit` | `integer` | No | Observations per page. Defaults to 20 and is clamped to 1-200. |
| `include_signed_urls` | `boolean` | No | When true, document-backed source records may include short-lived signed URLs for inspection or download. |

Response: `200`

#### Error Responses

- **422** (validation_error): Provide at least one of sot_id or metric_id
- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found
- **503** (service_unavailable): Deal observations are temporarily unavailable. Please try again shortly.

## Update Index Fact

### `PATCH /api/external/v2/deals/{deal_id}/index/facts`

Update a single canonical deal fact value

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `sot_id` | `string` | Yes | Prefixed SOT id from a search result (e.g. d:5362, a:123:property:690). Identifies the fact to update. |
| `value` | `string` | No | Free-text replacement value. Provide exactly one of value or observation_id. |
| `observation_id` | `integer` | No | Observation id (> 0) to promote as the new canonical value. Returned by /index/observations. |

Response: `200`

#### Error Responses

- **422** (validation_error): Provide exactly one of value or observation_id; Metric is not editable
- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found
- **503** (service_unavailable): Deal fact update is temporarily unavailable. Please try again shortly.

## Metric Definitions

### `GET /api/external/v2/deals/{deal_id}/index/metric-definitions`

List the metric definitions that can be recorded on a deal

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `metric_label` | `string` | No | Case-insensitive search term to filter by metric label (for example, noi). |
| `category_id` | `integer` | No | Filter to a single metric category id (greater than 0). |
| `page` | `integer` | No | 1-based page number. Defaults to 1. |
| `limit` | `integer` | No | Metric definitions per page. Defaults to 100 and is clamped to 1-200. |

Response: `200`

#### Error Responses

- **422** (validation_error): limit must be an integer
- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found
- **503** (service_unavailable): Deal metric definitions are temporarily unavailable. Please try again shortly.

## Index Entities

### `GET /api/external/v2/deals/{deal_id}/index/entities`

List the entities you can attach recorded facts to

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found
- **503** (service_unavailable): Deal entities are temporarily unavailable. Please try again shortly.

## Insert Index Facts

### `POST /api/external/v2/deals/{deal_id}/index/facts`

Record one or more user-supplied metric values on a deal

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `values` | `array` | Yes | One or more metric values to record. Must contain at least one item. |
| `values[].metric_id` | `integer` | Yes | Metric definition id (greater than 0) from /index/metric-definitions. |
| `values[].value` | `string` | Yes | The value to record. Must not be blank. |
| `values[].entity_ref` | `string` | No | Entity to attach the value to, from /index/entities (for example, property:690). Must match the metric's entity type. |
| `values[].group_ref` | `string` | No | Groups several submitted values under the same not-yet-created entity. Use only when no entity_ref is available. |

Response: `200` `results` reports the outcome of each submitted value in request order; `metric_sot_values` aggregates the SOTs that were written. Each `results[].sot` has the same shape as a `metric_sot_values` entry.

#### Error Responses

- **422** (validation_error): values must contain at least one item; value must not be blank
- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found
- **503** (service_unavailable): Deal fact insert is temporarily unavailable. Please try again shortly.

## List Vaults

### `GET /api/external/v2/deals/{deal_id}/vaults`

List a deal's vaults — its private Resources area and any shared Deal Rooms

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **403** (forbidden): Missing required scope: documents:read
- **404** (not_found): Deal not found

## List Documents

### `GET /api/external/v2/deals/{deal_id}/documents`

List a deal's uploaded documents across all of its vaults

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `limit` | `integer` | No | Documents per page. Defaults to 50 and is clamped to 1-200. |
| `cursor` | `string` | No | Cursor for the next page. |
| `search` | `string` | No | Case-insensitive substring match on the document file name. |
| `extension` | `string` | No | Filter to one file extension, e.g. `pdf` or `xlsx`. Omit the leading dot. |
| `folder_id` | `integer` | No | Only documents directly inside this folder. Omit to span the whole vault. |
| `vault_id` | `integer` | No | Only documents in this vault (a Deal Room or Resources). Get ids from [List Vaults](#list-vaults). Omit to span every vault on the deal. |
| `fields` | `string` | No | Comma-separated fields to include. `id` and `vault` are always included. |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **403** (forbidden): Missing required scope: documents:read
- **404** (not_found): Deal not found; Vault not found
- **422** (validation_error): This endpoint does not accept filter parameters

## Get Document

### `GET /api/external/v2/deals/{deal_id}/documents/{document_id}`

Fetch metadata for one uploaded document

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |
| `document_id` | `integer` | Yes | The document ID from the list endpoint |

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `fields` | `string` | No | Comma-separated fields to include. `id` is always included. |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **403** (forbidden): Missing required scope: documents:read
- **404** (not_found): Document not found

## Download Document

### `GET /api/external/v2/deals/{deal_id}/documents/{document_id}/download`

Fetch a short-lived signed download link for one uploaded document

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |
| `document_id` | `integer` | Yes | The document ID from the list endpoint |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **403** (forbidden): Missing required scope: documents:read
- **404** (not_found): Document not found

## List Checklists

### `GET /api/external/v2/deals/{deal_id}/checklists`

List a deal's checklists — one per shared Deal Room, with sections, tasks, document types, and task-linked files

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `vault_id` | `integer` | No | Only return the checklist for one shared Deal Room. Omit to return every Deal Room checklist on the deal. |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **403** (forbidden): Missing required scope: checklists:read
- **404** (not_found): Deal not found

## List Memos

### `GET /api/external/v2/deals/{deal_id}/memos`

List a deal's generated memos (deal books)

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `limit` | `integer` | No | Memos per page. Defaults to 50 and is clamped to 1-200. |
| `offset` | `integer` | No | 0-based index of the first memo to return. Defaults to 0. |
| `search` | `string` | No | Case-insensitive substring match on the memo title. |
| `filter[title]` | `string` | No | Exact match on the full memo title. Use search for a substring match instead. |
| `filter[status]` | `string` | No | Filter by publish state: draft or published. |
| `filter[memo_type]` | `string` | No | Filter by memo type: debt_financing_om, credit_memo, investment_sales, debt_brokerage, equity_raise, broker_opinion_of_value, investment_committee, invoice, or other. |
| `filter[pdf_ready]` | `boolean` | No | Filter to memos whose downloadable PDF has rendered (true) or not yet (false). |
| `vault_id` | `integer` | No | Only memos in this vault (a Deal Room or Resources). Get ids from [List Vaults](#list-vaults). Omit to span every vault on the deal. |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found

## Get Memo

### `GET /api/external/v2/deals/{deal_id}/memos/{memo_uuid}`

Fetch one memo with signed PDF download links

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |
| `memo_uuid` | `string` | Yes | The memo's UUID, from the `uuid` field on a List Memos row. |

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `quality` | `string` | No | Which rendered variant `pdf_url` points at: `original` (default), `high`, `medium`, or `low`. An unknown value returns 400. |

Response: `200`

#### Error Responses

- **400** (validation_error): Invalid quality: bogus. Allowed: original, high, medium, low
- **401** (unauthorized): Authentication required
- **404** (not_found): Memo not found

## List Deal Notes

### `GET /api/external/v2/deals/{deal_id}/notes`

List notes logged on a deal

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `limit` | `integer` | No | Results per page (1–200, default 50) |
| `cursor` | `string` | No | Cursor for next page |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found

## Create Deal Note

### `POST /api/external/v2/deals/{deal_id}/notes`

Add a note to a deal

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `text` | `string` | Yes | Note text. Must be non-empty. |

Response: `201`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found
- **422** (validation_error): text must not be blank

- Supports idempotent writes with the `Idempotency-Key` header.

## Update Deal Note

### `PATCH /api/external/v2/deals/{deal_id}/notes/{note_id}`

Edit a deal note

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |
| `note_id` | `integer` | Yes | The note ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `text` | `string` | Yes | Replacement note text. Must be non-empty. |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Deal or note not found
- **400** (bad_request): text cannot be empty
- **422** (validation_error): text must not be blank

- Supports idempotent writes with the `Idempotency-Key` header.

## Delete Deal Note

### `DELETE /api/external/v2/deals/{deal_id}/notes/{note_id}`

Permanently delete a deal note

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |
| `note_id` | `integer` | Yes | The note ID |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Deal or note not found

## List Checklist Task Notes

### `GET /api/external/v2/checklist-tasks/{task_id}/notes`

List comments on a checklist task

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `task_id` | `integer` | Yes | The checklist task ID |

#### Query Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `limit` | `integer` | No | Results per page (1–200, default 50) |
| `cursor` | `string` | No | Cursor for next page |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Checklist task not found

## Create Checklist Task Note

### `POST /api/external/v2/checklist-tasks/{task_id}/notes`

Add a comment to a checklist task

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `task_id` | `integer` | Yes | The checklist task ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `text` | `string` | Yes | Comment text. Must be non-empty. |

Response: `201`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Checklist task not found
- **422** (validation_error): text must not be blank

- Supports idempotent writes with the `Idempotency-Key` header.

## Update Checklist Task Note

### `PATCH /api/external/v2/checklist-tasks/{task_id}/notes/{note_id}`

Edit a checklist task comment

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `task_id` | `integer` | Yes | The checklist task ID |
| `note_id` | `integer` | Yes | The note ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `text` | `string` | Yes | Replacement comment text. Must be non-empty. |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Checklist task or note not found
- **400** (bad_request): text cannot be empty
- **422** (validation_error): text must not be blank

- Supports idempotent writes with the `Idempotency-Key` header.

## Delete Checklist Task Note

### `DELETE /api/external/v2/checklist-tasks/{task_id}/notes/{note_id}`

Permanently delete a checklist task comment

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `task_id` | `integer` | Yes | The checklist task ID |
| `note_id` | `integer` | Yes | The note ID |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Checklist task or note not found

## Create Checklist Task

### `POST /api/external/v2/checklist-tasks`

Add a task to a deal's checklist

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `title` | `string` | Yes | Task title. Must be non-empty. |
| `section_id` | `integer` | No | Add the task inside this checklist section. Provide exactly one of section_id, checklist_id, or parent_task_id. |
| `checklist_id` | `integer` | No | Add the task at the checklist root, outside any section. Provide exactly one of section_id, checklist_id, or parent_task_id. |
| `parent_task_id` | `integer` | No | Add the task as a subtask of this task (one level deep). Provide exactly one of section_id, checklist_id, or parent_task_id. |
| `description` | `string` | No | Optional longer detail. |
| `status` | `string` | No | Initial review state: to_do, requested, reviewing, updates_needed, approved, or cancelled. Defaults to to_do. |
| `assigned_user_id` | `integer` | No | Lev user id to assign. Must be an active teammate in your account. |
| `due_date` | `string` | No | Due date (ISO 8601, YYYY-MM-DD). |
| `document_type_ids` | `array` | No | Expected document type ids for the request. |

Response: `201`

#### Error Responses

- **401** (unauthorized): Authentication required
- **403** (forbidden): Missing required scope: checklists:write
- **400** (validation_error): assigned_user_id must belong to an active user in your account
- **404** (not_found): Section, checklist, or parent task not found
- **422** (validation_error): Provide exactly one of section_id, checklist_id, or parent_task_id

- Supports idempotent writes with the `Idempotency-Key` header.

## Update Checklist Task

### `PATCH /api/external/v2/checklist-tasks/{task_id}`

Change a checklist task's fields

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `task_id` | `integer` | Yes | The checklist task ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `title` | `string` | No | New title. Must be non-empty if provided. |
| `description` | `string|null` | No | New description. Send `null` to clear. |
| `status` | `string` | No | New review state: to_do, requested, reviewing, updates_needed, approved, or cancelled. |
| `assigned_user_id` | `integer|null` | No | New assignee. Must be an active teammate in your account. Send `null` to unassign. |
| `due_date` | `string|null` | No | New due date (ISO 8601, YYYY-MM-DD). Send `null` to clear. |
| `is_completed` | `boolean` | No | Pass false to reopen a completed task. To mark a task complete use the complete endpoint. |
| `document_type_ids` | `array|null` | No | Replace the expected document type ids. Send `null` to clear. |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **403** (forbidden): Missing required scope: checklists:write
- **400** (validation_error): assigned_user_id must belong to an active user in your account
- **404** (not_found): Checklist task not found
- **422** (validation_error): is_completed accepts false only; to mark a task complete use the complete endpoint

- Supports idempotent writes with the `Idempotency-Key` header.

## Complete Checklist Task

### `POST /api/external/v2/checklist-tasks/{task_id}/complete`

Mark a checklist task complete

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `task_id` | `integer` | Yes | The checklist task ID |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **403** (forbidden): Missing required scope: checklists:write
- **404** (not_found): Checklist task not found

- Supports idempotent writes with the `Idempotency-Key` header.

## Create Deal

### `POST /api/external/v2/deals`

Create a new deal

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `title` | `string` | Yes | Deal title (1–255 characters) |
| `loan_amount` | `number` | No | Requested loan amount |
| `loan_type` | `LoanType` | No | Loan type enum name, such as construction, heavy_bridge, or permanent |
| `transaction_type` | `TransactionType` | No | Transaction type enum name, such as acquisition, refinance, or new_construction |
| `business_plan` | `BusinessPlanType` | No | Business plan enum name: stabilized, value_add, construction, or land |
| `description` | `string` | No | Deal description |
| `estimated_close_date` | `string` | No | Estimated close date (ISO 8601) |
| `pipeline_ids` | `integer[]` | No | Pipeline IDs to add the deal to |
| `deal_financials` | `DealFinancialsWrite` | No | Deal-level financial fields to create with the deal |

Response: `201`

#### Error Responses

- **401** (unauthorized): Authentication required
- **422** (validation_error): title is required

- Supports idempotent writes with the `Idempotency-Key` header.

## Update Deal

### `PATCH /api/external/v2/deals/{deal_id}`

Update a deal (partial update)

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `title` | `string` | No | Deal title (min 1 character) |
| `loan_amount` | `number` | No | Requested loan amount |
| `loan_type` | `LoanType` | No | Loan type enum name |
| `transaction_type` | `TransactionType` | No | Transaction type enum name |
| `business_plan` | `BusinessPlanType` | No | Business plan enum name |
| `description` | `string` | No | Deal description |
| `estimated_close_date` | `string` | No | Estimated close date (ISO 8601) |
| `deal_financials` | `DealFinancialsWrite` | No | Deal-level financial fields to update |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found

## Delete Deal

### `DELETE /api/external/v2/deals/{deal_id}`

Archive (soft-delete) a deal

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deal_id` | `integer` | Yes | The deal ID |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Deal not found

## Document Object

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Unique document identifier. Use this value in the document detail and download endpoints. |
| `file_name` | string\|null | Original file name. |
| `extension` | string\|null | File extension without the leading dot. |
| `size_bytes` | integer\|null | File size in bytes. May be null for older records whose size was not stored. |
| `folder_path` | string\|null | Slash-delimited path inside the vault, or null for files at the vault root. |
| `uploaded_at` | string\|null | Upload timestamp (ISO 8601). |
| `vault` | DocumentVaultRef | The vault this document lives in — the compact `id`, `type`, `title` subset of the [Vault Object](#vault-object) (omits the list-only `is_default` and `document_count`). Always present, even under a sparse `fields` request. |

## Vault Object

A vault is a container for a deal's documents. Returned by [List Vaults](#list-vaults), embedded as the `vault` tag on each [Document Object](#document-object), and listed in the `vaults` array on each memo ([List Memos](#list-memos), [Get Memo](#get-memo)).

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Unique vault identifier. Pass as `vault_id` on [List Documents](#list-documents) to browse just this vault. |
| `type` | string | Structural category: `resources` (the deal's single private working vault) or `shared` (a vault the borrower can share with other parties). Present the `title` to users, not this raw value. |
| `title` | string\|null | Human-readable vault name — `Deal Resources` for the private vault; shared vaults carry names like `Deal Room`, `Closing`, or a per-lender room. |
| `is_default` | boolean | `true` for the deal's primary Deal Room (its default shared vault). Returned by [List Vaults](#list-vaults); omitted from the embedded document `vault` tag. |
| `document_count` | integer | Number of documents in the vault. Returned by [List Vaults](#list-vaults); omitted from the embedded document `vault` tag. |

## Checklist Object

A checklist is the to-do and request list inside one Deal Room. Returned by [List Checklists](#list-checklists), one per shared vault.

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Unique checklist identifier |
| `vault` | ChecklistVaultRef | The Deal Room this checklist belongs to: `id`, `type`, and `title`. `type` is always `shared`; checklists do not live on the private Resources vault. |
| `sections` | ChecklistSection[] | Ordered sections, each carrying its own tasks. See the [Checklist Section Object](#checklist-section-object). |
| `tasks` | ChecklistTask[] | Root-level tasks that sit outside any section, in display order. See the [Checklist Task Object](#checklist-task-object). |

## Checklist Section Object

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Unique section identifier |
| `name` | string | Section name |
| `position` | integer | Display order inside the checklist |
| `start_date` | string\|null | Optional section start date (ISO 8601) |
| `end_date` | string\|null | Optional section end date (ISO 8601) |
| `tasks` | ChecklistTask[] | The section's tasks in display order. See the [Checklist Task Object](#checklist-task-object). |

## Checklist Task Object

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Unique task identifier |
| `title` | string | Task title |
| `description` | string\|null | Task description |
| `status` | string | Review state: `to_do`, `requested`, `reviewing`, `updates_needed`, `approved`, or `cancelled`. |
| `is_completed` | boolean | Separate done flag. A task can be approved but not marked complete. |
| `position` | integer | Display order inside its section or root task list |
| `due_date` | string\|null | Due date (ISO 8601) |
| `role` | string\|null | Role responsible for the task, such as borrower or lender |
| `assignee` | ChecklistAssignee\|null | The Lev user assigned to the task, or null when unassigned. |
| `assigned_team` | ChecklistAssignedTeam\|null | The account, lender, or borrower team assigned to the task. `name` can be null for stale or out-of-scope team references. |
| `collaborators` | ChecklistCollaborator[] | People looped into the task. Each carries `id` and `email`; `id` is null for external parties without a Lev account. |
| `document_types` | ChecklistDocumentType[] | Expected document types for the request, such as Appraisal or Rent roll. |
| `files` | ChecklistTaskFile[] | Uploaded files already linked to this task. Each file includes `document_id`, `vault_resource_id`, `name`, and `origin`. |
| `subtasks` | ChecklistTask[] | Nested subtasks, one level deep, in the same task shape. |

## Deal Object

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Unique deal identifier |
| `title` | string\|null | Deal title |
| `loan_amount` | number\|null | Requested loan amount |
| `loan_type` | LoanType\|null | Loan type enum name |
| `transaction_type` | TransactionType\|null | Transaction type enum name |
| `business_plan` | BusinessPlanType\|null | Business plan enum name |
| `description` | string\|null | Deal description |
| `estimated_close_date` | string\|null | Estimated close date (ISO 8601) |
| `close_date` | string\|null | Actual close date (ISO 8601) |
| `owner_account_id` | integer\|null | Owning account ID |
| `created_at` | string\|null | Creation timestamp (ISO 8601) |
| `updated_at` | string\|null | Last update timestamp (ISO 8601) |
| `archived` | boolean | Whether the deal has been archived. Archiving is separate from pipeline status, so an archived deal keeps its last stage |
| `archived_at` | string\|null | When the deal was archived (ISO 8601), or null when active |
| `financials` | object\|null | Included when `?include=financials` (see [Deal Financials](/build/deal-financials)) |
| `properties` | array\|null | Included when `?include=properties` (see [Deal Properties](/build/deal-properties)) |
| `team` | array\|null | Included when `?include=team` (see [Deal Team](/build/deal-team)) |
| `pipelines` | array\|null | Included when `?include=pipelines`. Up to 10 current pipeline rows per deal, each with `pipeline_id`, `status_name`, `updated_at` (see [Pipelines](/build/pipelines)) |