Build

Deals

Full CRUD operations, generated memos, document and vault access, and Deal Room checklist reads for deals in the Lev API.

Updated September 2026
GET/api/external/v2/deals
GET/api/external/v2/deals/{deal_id}Structured deal detail
GET/api/external/v2/deals/{deal_id}/vaultsA deal's vaults — Resources and Deal Rooms
GET/api/external/v2/deals/{deal_id}/documentsUploaded files across a deal's vaults
GET/api/external/v2/deals/{deal_id}/documents/{document_id}Metadata for one uploaded file
GET/api/external/v2/deals/{deal_id}/documents/{document_id}/downloadShort-lived signed file link
GET/api/external/v2/deals/{deal_id}/checklistsDeal Room checklists with task status and attached file references
GET/api/external/v2/deals/{deal_id}/memosA deal's memos (published + draft)
GET/api/external/v2/deals/{deal_id}/memos/{memo_uuid}One memo with signed PDF links
GET/api/external/v2/deals/{deal_id}/notesNotes logged on a deal
POST/api/external/v2/deals/{deal_id}/notes
PATCH/api/external/v2/deals/{deal_id}/notes/{note_id}
DELETE/api/external/v2/deals/{deal_id}/notes/{note_id}
GET/api/external/v2/checklist-tasks/{task_id}/notesComments on a checklist task
POST/api/external/v2/checklist-tasks/{task_id}/notes
PATCH/api/external/v2/checklist-tasks/{task_id}/notes/{note_id}
DELETE/api/external/v2/checklist-tasks/{task_id}/notes/{note_id}
POST/api/external/v2/checklist-tasks
PATCH/api/external/v2/checklist-tasks/{task_id}
POST/api/external/v2/checklist-tasks/{task_id}/complete

Overview

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

EndpointDescription
GET /dealsList deals with filtering and pagination
GET /deals/{id}Get a single deal
GET /deals/{id}/vaultsList a deal's vaults — its Resources area and any shared Deal Rooms
GET /deals/{id}/documentsList 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}/downloadFetch a short-lived signed download link for one uploaded document
GET /deals/{id}/checklistsList each Deal Room's checklist — sections, tasks, document types, and task-linked files
GET /deals/{id}/memosList 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}/notesList notes logged on a deal
POST /deals/{id}/notesAdd 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}/notesList comments on a checklist task (borrower-portal comments)
POST /checklist-tasks/{id}/notesAdd 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-tasksCreate 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}/completeMark a checklist task complete (also approves its attached files)
POST /dealsCreate 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
limitinteger
Results per page (1–200, default 50)
cursorstring
Cursor for next page (mutually exclusive with sort)
offsetinteger
Offset for pagination (requires sort)
sortstring
Sort fields: title, loan_amount, created_at, updated_at. Prefix with - for descending
fieldsstring
Comma-separated fields to include (id always included)
includestring
Sub-resources to embed: financials, properties, team, pipelines, sponsor_contacts, fees. include=fees requires an admin key on this collection
filter[loan_type]LoanType
Filter by loan type enum name, such as heavy_bridge or permanent
filter[transaction_type]TransactionType
Filter by transaction type enum name, such as acquisition or refinance
filter[business_plan]BusinessPlanType
Filter by business plan enum name
filter[archived]boolean
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
Minimum loan amount
filter[loan_amount][lte]number
Maximum loan amount
filter[created_at][gte]string
Created after (ISO 8601)
filter[created_at][lte]string
Created before (ISO 8601)
Archived state is independent of pipeline status

Archiving a deal sets its archived and archived_at fields. It does not rewrite the deal's pipeline status, so an archived deal can still report a live-looking stage such as Reaching Out To Lenders. To separate active deals from archived ones, read the archived field or filter with filter[archived]. Do not infer archived state from a stalled pipeline stage.

The deal owner is a person, not the owning account

owner_account_id is the account (tenant) the deal belongs to. The person who owns the deal is owner_user — the deal's primary deal principal, meaning the team member whose is_primary is true and whose deal_role is deal_principal. is_primary on its own does not identify the owner, because that flag also marks the primary row of other roles, such as primary_lender_contact. When more than one member qualifies, the lowest user_id wins, so the API names the same person the Lev app does. owner_user keys on user_id, which joins against team[].user_id — not team[].id, the team assignment ID. Lev resolves it from the deal's full team, not from the embedded ?include=team list (at most 50 members per deal), so the value is the same with or without that include.

Sponsor contacts resolve through the deal's sponsor company

Lev has no deal-to-contact link. So ?include=sponsor_contacts resolves the deal's first sponsor company, then embeds that company's connected sponsor contacts. The company is the one sponsor_private_company_id names, so the two fields can never disagree, and each entry repeats it on company_id. A deal with more than one sponsor association resolves through the first one only.

Contacts are scoped to the deal's owning account and to yours, and a contact must satisfy both. A deal shared to you from another account therefore embeds [], even when that deal has sponsor contacts of its own. Compare the deal's owner_account_id against your own account before you read an empty list as "no contacts on file".

When the include is requested the key is always present, and always an array. Entries are ordered is_primary first, then by ascending id, so a caller that wants a single sponsor contact can take element 0. is_primary has no uniqueness constraint, so a company can return zero, one, or several primary contacts. That is why the field is a list. At most 25 contacts are embedded per deal.

include=fees refuses on this collection rather than answering partially

?include=fees returns 403 here unless the API key belongs to an account admin. A collection has no single deal, so the primary-owner half of the fee gate has nothing to resolve against, and Lev refuses the whole request rather than answering a money question with a silent null on every row the key does not own. Retrying will not help. Ask an account admin to mint the key, or read one deal at a time with GET /deals/{deal_id}?include=fees, which also admits the deal's primary owner.

An admin key succeeds, and then fees is embedded only on the deals the key's own account owns. A deal shared to you from another account reports fees: null on the same page, because admin standing is scoped to one account and a parent-account admin is not an admin of the child. See Fees Object for the payload and the full gate.

curl -X GET "https://api.lev.com/api/external/v2/deals?limit=10&include=financials&sort=-created_at" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "...",
  "timestamp": "2026-03-20T15:30:45Z",
  "data": [
    {
      "id": 101,
      "title": "123 Main St Acquisition",
      "loan_amount": 5000000.0,
      "loan_type": "heavy_bridge",
      "transaction_type": "acquisition",
      "business_plan": "value_add",
      "description": "Mixed-use acquisition in downtown",
      "estimated_close_date": "2026-06-01",
      "close_date": null,
      "owner_account_id": 56,
      "owner_user": {
        "user_id": 789,
        "first_name": "Alex",
        "last_name": "Rivera"
      },
      "created_at": "2026-01-15T10:00:00Z",
      "updated_at": "2026-03-10T14:30:00Z",
      "archived": false,
      "archived_at": null,
      "financials": {
        "id": 201,
        "noi": 450000.0,
        "purchase_price": 6500000.0,
        "appraised_value": 7000000.0,
        "ltv": 0.714,
        "dscr": 1.25
      }
    }
  ],
  "pagination": {
    "total": 42,
    "limit": 10,
    "offset": 0,
    "has_more": true
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
400bad_request
cursor and sort cannot be combined; use offset pagination when sorting— Both cursor and sort query parameters are provided

Get Deal

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

Get a single deal by ID

Path parameters
deal_idintegerrequired
The deal ID
Query parameters
includestring
Sub-resources to embed: financials, properties, team, pipelines, sponsor_contacts, fees. include=fees requires an admin of the deal's owning account, or the deal's primary owner
fieldsstring
Comma-separated fields to include

Response (200):

{
  "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "timestamp": "2026-03-20T15:30:45Z",
  "data": {
    "id": 101,
    "title": "123 Main St Acquisition",
    "loan_amount": 5000000.0,
    "loan_type": "heavy_bridge",
    "transaction_type": "acquisition",
    "business_plan": "value_add",
    "description": "Mixed-use acquisition in downtown Chicago",
    "estimated_close_date": "2026-06-01",
    "close_date": null,
    "owner_account_id": 56,
    "owner_user": {
      "user_id": 789,
      "first_name": "Alex",
      "last_name": "Rivera"
    },
    "created_at": "2026-01-15T10:00:00Z",
    "updated_at": "2026-03-10T14:30:00Z",
    "archived": false,
    "archived_at": null
  }
}

Request the sponsor contacts explicitly to embed them:

curl -X GET "https://api.lev.com/api/external/v2/deals/101?include=sponsor_contacts" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200), sponsor_contacts fragment:

{
  "sponsor_private_company_id": 456,
  "sponsor_contacts": [
    {
      "id": 123,
      "company_id": 456,
      "first_name": "Jordan",
      "last_name": "Chen",
      "full_name": "Jordan Chen",
      "title": "Managing Partner",
      "department": null,
      "email": "jordan@example.com",
      "phones": [
        {
          "id": "1",
          "type": "mobile",
          "raw": "(312) 555-0142",
          "country": "US",
          "e164": "+13125550142",
          "extension": null
        }
      ],
      "linkedin_url": null,
      "is_primary": true,
      "created_at": "2026-01-15T10:00:00Z",
      "updated_at": "2026-03-10T14:30:00Z"
    }
  ]
}

Fees are requested the same way. Unlike financials, they are refused outright rather than returned as null when the key may not read them:

curl -X GET "https://api.lev.com/api/external/v2/deals/101?include=fees" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200), fees fragment:

{
  "fees": {
    "currency": "USD",
    "gross_fee_amount": 87500.0,
    "items": [
      {
        "party": "sponsor",
        "rate_percent": 1.5,
        "basis": "loan_amount",
        "flat_amount": null,
        "amount": 75000.0,
        "created_at": "2026-01-15T10:00:00Z",
        "updated_at": "2026-03-10T14:30:00Z"
      },
      {
        "party": "lender",
        "rate_percent": null,
        "basis": "loan_amount",
        "flat_amount": 12500.0,
        "amount": 12500.0,
        "created_at": "2026-02-02T09:15:00Z",
        "updated_at": "2026-02-02T09:15:00Z"
      }
    ]
  }
}

The first row is a rate: 1.5% of the deal's loan_amount of 5000000.0. The second replaces its rate with a flat_amount. gross_fee_amount is their sum, and is null the moment any one row cannot be resolved — see Fees Object.

401unauthorized
Authentication required— Missing or invalid Authorization header
403forbidden
include=fees on this deal requires an API key belonging to an admin of the account that owns the deal, or to the deal's primary owner.— ?include=fees on a deal the key can read, with a key that is neither an admin of the deal's owning account nor the deal's primary owner
404not_found
Deal not found— The deal_id doesn't exist or isn't accessible to the authenticated user

Deal Index

Every deal carries an index of source-backed facts — canonical values for metrics like NOI, occupancy, and loan terms, each traceable to the document it came from. Those endpoints have their own section.

Deal Index — what the index holds, and how to read, trace, correct, and record a deal's facts.

List Vaults

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

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

Lists the vaults on a deal. Every deal has one private Resources vault for its own working files, plus zero or more shared vaults — the Deal Rooms the borrower shares with other parties. Use this to see how a deal's documents are organized into rooms before browsing files with List Documents.

Each vault reports its document_count. Results are ordered Resources first, then the deal's primary Deal Room (is_default: true), then any other shared vaults.

Path parameters
deal_idintegerrequired
The deal ID
curl "https://api.lev.com/api/external/v2/deals/101/vaults" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "5f3c2a1e-9b7d-4c2a-8f1e-2d6b0a4c7e91",
  "timestamp": "2026-06-09T17:59:05Z",
  "data": [
    {
      "id": 41,
      "type": "resources",
      "title": "Deal Resources",
      "is_default": false,
      "document_count": 3
    },
    {
      "id": 42,
      "type": "shared",
      "title": "Deal Room",
      "is_default": true,
      "document_count": 2
    },
    {
      "id": 57,
      "type": "shared",
      "title": "Closing",
      "is_default": false,
      "document_count": 4
    }
  ],
  "pagination": {
    "total": 3,
    "limit": 3,
    "offset": 0,
    "has_more": false
  }
}

The type is the structural category — resources or shared — and title carries the human name. Only the default shared vault is the deal's primary Deal Room; other shared vaults are user-named for their own purpose (a per-lender room, Closing, and so on), so present the title, not the raw type. Pass a vault's id as the vault_id query parameter on List Documents to browse just that one. See the Vault Object for the full field reference.

401unauthorized
Authentication required— Missing or invalid Authorization header
403forbidden
Missing required scope: documents:read— The credential does not include the documents:read scope
404not_found
Deal not found— The deal_id doesn't exist or isn't accessible to the authenticated user

List Documents

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

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

Lists files uploaded to the deal — across its private Resources vault and any shared Deal Rooms. This is the deal-document browse surface for rent rolls, appraisals, budgets, closing documents, and other uploaded files. It is separate from generated memos and deal books, which are returned by List Memos.

By default the listing spans every vault on the deal, and each document is tagged with the vault it lives in. A file shared into more than one vault appears once per vault. Pass vault_id to drill into a single vault — get the ids from List Vaults.

The list returns metadata only. It does not include signed download URLs. Fetch a single document with Get Document, then use Download Document when you need a fresh file link.

Explicit filters only

This endpoint supports search, extension, folder_id, and vault_id. It intentionally does not accept generic filter[...] parameters.

Path parameters
deal_idintegerrequired
The deal ID
Query parameters
limitinteger
Documents per page. Defaults to 50 and is clamped to 1-200.
cursorstring
Cursor for the next page.
searchstring
Case-insensitive substring match on the document file name.
extensionstring
Filter to one file extension, e.g. `pdf` or `xlsx`. Omit the leading dot.
folder_idinteger
Only documents directly inside this folder. Omit to span the whole vault.
vault_idinteger
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.
fieldsstring
Comma-separated fields to include. `id` and `vault` are always included.
curl "https://api.lev.com/api/external/v2/deals/101/documents?limit=20&extension=pdf" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "7b61eb1f-33e8-4b10-8891-90c6e6f5e768",
  "timestamp": "2026-06-08T15:45:12Z",
  "data": [
    {
      "id": 17,
      "file_name": "rent-roll.pdf",
      "extension": "pdf",
      "size_bytes": 98765,
      "folder_path": "Financials/2024",
      "uploaded_at": "2026-06-01T10:00:00",
      "vault": { "id": 41, "type": "resources", "title": "Deal Resources" }
    },
    {
      "id": 21,
      "file_name": "appraisal.pdf",
      "extension": "pdf",
      "size_bytes": 1452200,
      "folder_path": null,
      "uploaded_at": "2026-06-03T14:18:27",
      "vault": { "id": 42, "type": "shared", "title": "Deal Room" }
    }
  ],
  "pagination": {
    "total": 2,
    "limit": 20,
    "cursor": null,
    "has_more": false,
    "next_cursor": null
  }
}

Each document carries a vault tag — the Vault Object (id, type, title) for the vault it lives in. An accessible deal with no documents returns an empty list, not a 404. Documents that are rejected during review are excluded from list, detail, and download responses.

401unauthorized
Authentication required— Missing or invalid Authorization header
403forbidden
Missing required scope: documents:read— The credential does not include the documents:read scope
404not_found
Deal not found— The deal_id doesn't exist or isn't accessible to the authenticated user
404not_found
Vault not found— A vault_id is provided that doesn't belong to this deal
422validation_error
This endpoint does not accept filter parameters— A generic filter[...] query parameter is provided

Get Document

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

Fetch metadata for one uploaded document

Returns one uploaded document's metadata, including the vault it lives in. The document_id is the id returned by List Documents. The document is resolved across all of the requested deal's vaults — its Resources vault and any Deal Rooms.

Path parameters
deal_idintegerrequired
The deal ID
document_idintegerrequired
The document ID from the list endpoint
Query parameters
fieldsstring
Comma-separated fields to include. `id` is always included.
curl "https://api.lev.com/api/external/v2/deals/101/documents/17" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "ae1df8ee-c7c9-4ed0-82d2-19c4ec042ac4",
  "timestamp": "2026-06-08T15:46:18Z",
  "data": {
    "id": 17,
    "file_name": "rent-roll.pdf",
    "extension": "pdf",
    "size_bytes": 98765,
    "folder_path": "Financials/2024",
    "uploaded_at": "2026-06-01T10:00:00",
    "vault": { "id": 41, "type": "resources", "title": "Deal Resources" }
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
403forbidden
Missing required scope: documents:read— The credential does not include the documents:read scope
404not_found
Document not found— The document_id doesn't exist on the deal, is rejected, or isn't readable by the authenticated user

Download Document

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

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

Returns a signed S3 download URL for one uploaded document. The document is resolved across all of the deal's vaults, and the API re-checks that it belongs to the requested deal before signing, so a document from another deal returns 404 even if the account owns both deals.

Signed URLs expire after about 15 minutes. Request a fresh download link when the user is ready to open the file, and store document IDs rather than signed URLs in your system.

Path parameters
deal_idintegerrequired
The deal ID
document_idintegerrequired
The document ID from the list endpoint
curl "https://api.lev.com/api/external/v2/deals/101/documents/17/download" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "9fe1bfe1-b5c5-4a5f-8c7d-2766cf67a607",
  "timestamp": "2026-06-08T15:47:08Z",
  "data": {
    "file_name": "rent-roll.pdf",
    "download_url": "https://signed-s3-url.example/rent-roll.pdf",
    "expires_in": 900
  }
}
Signed document links expire

download_url is a short-lived bearer link to the file bytes. Fetch it when you need a fresh link, present it as a clickable file-name link, and avoid displaying the raw URL in chat or UI surfaces.

401unauthorized
Authentication required— Missing or invalid Authorization header
403forbidden
Missing required scope: documents:read— The credential does not include the documents:read scope
404not_found
Document not found— The document_id doesn't exist on the deal, is rejected, or isn't readable by the authenticated user

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

Lists the checklists on a deal: the items to collect, review, or complete in each Deal Room. Checklists live on the deal's shared vaults. Each Deal Room can carry one checklist, and a deal with several Deal Rooms can have several checklists. The deal's private Resources vault does not have a checklist. Results are ordered with the primary Deal Room's checklist first.

Use the task-level document_types array to see what type of document a request expects. Use the task-level files array to see which uploaded files are already linked to that task. A task with no files can still be complete if a user marked it complete manually, so use both files and the completion fields (status, is_completed) when reporting what is satisfied or outstanding.

Path parameters
deal_idintegerrequired
The deal ID
Query parameters
vault_idinteger
Only return the checklist for one shared Deal Room. Omit to return every Deal Room checklist on the deal.
curl "https://api.lev.com/api/external/v2/deals/101/checklists" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "fa8d1a1e-4e9a-4c82-b68e-44e8db5b3a0c",
  "timestamp": "2026-06-15T18:04:22Z",
  "data": [
    {
      "id": 11,
      "vault": {
        "id": 42,
        "type": "shared",
        "title": "Deal Room"
      },
      "sections": [
        {
          "id": 21,
          "name": "Financials",
          "position": 1,
          "start_date": "2026-06-01",
          "end_date": null,
          "tasks": [
            {
              "id": 301,
              "title": "Rent roll",
              "description": "Trailing 12 months",
              "status": "reviewing",
              "is_completed": false,
              "position": 1,
              "due_date": "2026-07-01",
              "role": "borrower",
              "assignee": {
                "id": 9,
                "first_name": "Ada",
                "last_name": "Lovelace",
                "email": "ada@example.com"
              },
              "assigned_team": {
                "id": 8256,
                "type": "account",
                "name": "Borrower Team"
              },
              "collaborators": [
                {
                  "id": null,
                  "email": "outside@example.com"
                }
              ],
              "document_types": [
                {
                  "id": 15,
                  "name": "Rent roll"
                }
              ],
              "files": [
                {
                  "vault_resource_id": 6001,
                  "document_id": 901,
                  "name": "Rent roll.xlsx",
                  "origin": "auto_match"
                }
              ],
              "subtasks": []
            }
          ]
        }
      ],
      "tasks": []
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 1,
    "offset": 0,
    "has_more": false
  }
}

Sections and tasks come back in display order (position ascending), matching the checklist as it appears in Lev. See the Checklist Object, Checklist Section Object, and Checklist Task Object for the full field reference.

401unauthorized
Authentication required— Missing or invalid Authorization header
403forbidden
Missing required scope: checklists:read— The credential does not include the checklists:read scope
404not_found
Deal not found— The deal_id doesn't exist or isn't accessible to the authenticated user

List Memos

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

List a deal's generated memos (deal books)

Lists the memos — AI-generated deal books — for a deal. Returns both published memos and unpublished drafts, so you can support preview-your-own-work flows. Each row carries status (published or draft) and published_at to disambiguate, plus updated_at (last-modified) and created_by ({id, name} — who made the memo). Published memos come first (most recently published), then drafts (most recently updated).

PDF download links are omitted here — fetch a single memo with Get Memo for signed pdf_url links. Each row's pdf_ready flag tells you whether a downloadable PDF has rendered yet.

By default the listing spans every vault on the deal, and each memo is tagged with the vaults it lives in. A memo can sit in more than one vault. Pass vault_id to return only the memos in a single vault — get the ids from List Vaults.

Narrow the list server-side with search (a case-insensitive title substring) and the filter[title] (exact title), filter[status], filter[memo_type], and filter[pdf_ready] parameters. Filters compose, and pagination reflects the filtered set. There is no sort control — the published-first ordering is fixed.

Path parameters
deal_idintegerrequired
The deal ID
Query parameters
limitinteger
Memos per page. Defaults to 50 and is clamped to 1-200.
offsetinteger
0-based index of the first memo to return. Defaults to 0.
searchstring
Case-insensitive substring match on the memo title.
filter[title]string
Exact match on the full memo title. Use search for a substring match instead.
filter[status]string
Filter by publish state: draft or published.
filter[memo_type]string
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
Filter to memos whose downloadable PDF has rendered (true) or not yet (false).
vault_idinteger
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.
curl "https://api.lev.com/api/external/v2/deals/101/memos" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "43f2c567-57e4-4fac-8044-b22e01ec562c",
  "timestamp": "2026-06-02T17:25:28Z",
  "data": [
    {
      "uuid": "f26c0055-b2d3-4ca1-b25e-0ce0bacd0d61",
      "title": "Debt Financing Offering Memorandum",
      "memo_type": "debt_financing_om",
      "status": "published",
      "published_at": "2026-05-27T20:21:26",
      "updated_at": "2026-05-28T14:03:11",
      "created_by": { "id": 4012, "name": "Jordan Avery" },
      "pdf_generation_status": "completed",
      "pdf_ready": true,
      "memo_url": "https://memo.lev.com/acme/deals/101/memos/f26c0055-b2d3-4ca1-b25e-0ce0bacd0d61",
      "vaults": [
        { "id": 42, "type": "shared", "title": "Deal Room" },
        { "id": 57, "type": "shared", "title": "Closing" }
      ]
    },
    {
      "uuid": "8452c954-eedc-44ab-afbd-d86a8b65d007",
      "title": "Investment Sales Offering Memorandum (draft)",
      "memo_type": "investment_sales",
      "status": "draft",
      "published_at": null,
      "updated_at": "2026-06-01T09:12:40",
      "created_by": { "id": 4012, "name": "Jordan Avery" },
      "pdf_generation_status": null,
      "pdf_ready": false,
      "memo_url": "https://memo.lev.com/acme/deals/101/memos/8452c954-eedc-44ab-afbd-d86a8b65d007",
      "vaults": []
    }
  ],
  "pagination": {
    "total": 2,
    "limit": 50,
    "offset": 0,
    "has_more": false
  }
}

Each memo carries a vaults array — the Vault Object (id, type, title) for every vault it belongs to. The set is scoped to the vaults your credential can see: a vault you can't access is left out, and a memo in no vault you can see returns vaults: [].

401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Deal not found— The deal_id doesn't exist or isn't accessible to the authenticated user

Get Memo

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

Fetch one memo with signed PDF download links

Returns one memo — published or draft — with signed PDF links. pdf_url points at the variant named by quality (default original); pdf_versions lists every rendered variant.

pdf_url is null and pdf_versions is empty when the memo has no rendered variant — common for drafts that have never been previewed (pdf_ready: false).

The vaults array tags the memo with each Vault Object it belongs to — a memo can be in more than one. The set is scoped to the vaults your credential can see, so it is empty when the memo is in no vault you can access.

Path parameters
deal_idintegerrequired
The deal ID
memo_uuidstringrequired
The memo's UUID, from the `uuid` field on a List Memos row.
Query parameters
qualitystring
Which rendered variant `pdf_url` points at: `original` (default), `high`, `medium`, or `low`. An unknown value returns 400.
curl "https://api.lev.com/api/external/v2/deals/101/memos/f26c0055-b2d3-4ca1-b25e-0ce0bacd0d61?quality=original" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "69628f7c-3577-407a-9816-fd90942381f9",
  "timestamp": "2026-06-02T17:25:56Z",
  "data": {
    "uuid": "f26c0055-b2d3-4ca1-b25e-0ce0bacd0d61",
    "title": "Debt Financing Offering Memorandum",
    "memo_type": "debt_financing_om",
    "status": "published",
    "published_at": "2026-05-27T20:21:26",
    "updated_at": "2026-05-28T14:03:11",
    "created_by": { "id": 4012, "name": "Jordan Avery" },
    "pdf_generation_status": "completed",
    "pdf_ready": true,
    "memo_url": "https://memo.lev.com/acme/deals/101/memos/f26c0055-b2d3-4ca1-b25e-0ce0bacd0d61",
    "vaults": [
      { "id": 42, "type": "shared", "title": "Deal Room" },
      { "id": 57, "type": "shared", "title": "Closing" }
    ],
    "pdf_url": "https://…signed-s3-url…",
    "pdf_versions": [
      { "quality": "original", "status": "completed", "pdf_url": "https://…signed-s3-url…" },
      { "quality": "high", "status": "completed", "pdf_url": "https://…signed-s3-url…" },
      { "quality": "medium", "status": "completed", "pdf_url": "https://…signed-s3-url…" },
      { "quality": "low", "status": "completed", "pdf_url": "https://…signed-s3-url…" }
    ]
  }
}
Signed PDF links expire

pdf_url (and each pdf_versions[].pdf_url) is a short-lived signed link. Fetch the memo when you need a fresh link rather than storing it, and don't display the raw URL.

400validation_error
Invalid quality: bogus. Allowed: original, high, medium, low— The quality query parameter isn't one of the allowed values
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Memo not found— The memo_uuid doesn't exist on the deal, or isn't readable by the authenticated user

List Deal Notes

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

List notes logged on a deal

Lists the free-text notes logged on a deal — call summaries, lender conversations, and other human-written commentary. Notes are returned oldest first; walk the next_cursor in the response to reach the most recent. Internal or private notes are never returned.

For underwriting facts and where a value came from, use the deal index (Search Index) — notes are commentary, not indexed facts.

Path parameters
deal_idintegerrequired
The deal ID
Query parameters
limitinteger
Results per page (1–200, default 50)
cursorstring
Cursor for next page
curl "https://api.lev.com/api/external/v2/deals/101/notes" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "43f2c567-57e4-4fac-8044-b22e01ec562c",
  "timestamp": "2026-06-08T17:25:28Z",
  "data": [
    {
      "id": 7340,
      "text": "Lender call went well — they're comfortable at 65% LTV, want updated rent roll.",
      "created_by": {
        "id": 88,
        "name": "Dana Lender"
      },
      "created_at": "2026-06-08T10:00:00Z",
      "updated_at": "2026-06-08T10:00:00Z"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 50,
    "has_more": false,
    "next_cursor": null
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Deal not found— The deal_id doesn't exist or isn't accessible to the authenticated user

Create Deal Note

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

Add a note to a deal

Logs a free-text note on a deal. The note is attributed to the API key's user and appears alongside notes written in the Lev web app. Simple HTML formatting is preserved; scripts and other unsafe markup are stripped.

Supports the Idempotency-Key header to prevent duplicate creation. Requires the deals:write scope.

Path parameters
deal_idintegerrequired
The deal ID
Request body
textstringrequired
Note text. Must be non-empty.
curl -X POST "https://api.lev.com/api/external/v2/deals/101/notes" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{"text": "Lender call went well — they want an updated rent roll."}'

Response (201):

{
  "request_id": "d0e1f2a3-b4c5-6789-3456-890123456789",
  "timestamp": "2026-06-08T15:30:45Z",
  "data": {
    "id": 7340,
    "text": "Lender call went well — they want an updated rent roll.",
    "created_by": {
      "id": 88,
      "name": "Dana Lender"
    },
    "created_at": "2026-06-08T15:30:45Z",
    "updated_at": "2026-06-08T15:30:45Z"
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Deal not found— The deal_id doesn't exist or isn't accessible to the authenticated user
422validation_error
text must not be blank— The request omits text or sends an empty string

Update Deal Note

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

Edit a deal note

Replaces the text of a note logged on a deal. Requires the deals:write scope. Use the id returned by List Deal Notes or Create Deal Note as note_id.

Only notes the authenticated user created and can still write are editable. Notes on another deal, hidden or private notes, and notes the user does not own return 404 Not Found.

Supports the Idempotency-Key header to make retries safe.

Path parameters
deal_idintegerrequired
The deal ID
note_idintegerrequired
The note ID
Request body
textstringrequired
Replacement note text. Must be non-empty.
curl -X PATCH "https://api.lev.com/api/external/v2/deals/101/notes/7340" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{"text": "Lender call went well — they want an updated rent roll and trailing 12."}'

Response (200):

{
  "request_id": "90d7bfc2-ff5e-4b4e-9c77-7987957fd6a7",
  "timestamp": "2026-06-19T15:30:45Z",
  "data": {
    "id": 7340,
    "text": "Lender call went well — they want an updated rent roll and trailing 12.",
    "created_by": {
      "id": 88,
      "name": "Dana Lender"
    },
    "created_at": "2026-06-08T15:30:45Z",
    "updated_at": "2026-06-19T15:30:45Z"
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Deal or note not found— The deal_id or note_id doesn't exist, doesn't match, isn't externally visible, or isn't writable by the authenticated user
400bad_request
text cannot be empty— The supplied text becomes empty after sanitization
422validation_error
text must not be blank— The request omits text or sends an empty string

Delete Deal Note

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

Permanently delete a deal note

Permanently deletes a note logged on a deal. Requires the deals:write scope. Use the id returned by List Deal Notes or Create Deal Note as note_id.

Only notes the authenticated user created and can still write are deletable. Notes on another deal, hidden or private notes, and notes the user does not own return 404 Not Found.

Path parameters
deal_idintegerrequired
The deal ID
note_idintegerrequired
The note ID
curl -X DELETE "https://api.lev.com/api/external/v2/deals/101/notes/7340" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "2c23bb16-8a8c-4a20-8ec0-9af5f555c84c",
  "timestamp": "2026-06-19T15:30:45Z",
  "data": {
    "deleted": true
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Deal or note not found— The deal_id or note_id doesn't exist, doesn't match, isn't externally visible, or isn't writable by the authenticated user

List Checklist Task Notes

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

List comments on a checklist task

Lists the comments on a checklist task — the borrower-portal "comments" that appear in the task's activity feed. A checklist task is a single diligence item (for example, a document request) inside a Deal Room checklist. These are the same comments your team and the borrower post in the portal: reading here returns that thread, and posting is equivalent to commenting on the item in the Lev web app.

Comments are returned oldest first; walk the next_cursor in the response to reach the most recent. Internal or private notes are never returned.

Path parameters
task_idintegerrequired
The checklist task ID
Query parameters
limitinteger
Results per page (1–200, default 50)
cursorstring
Cursor for next page
curl "https://api.lev.com/api/external/v2/checklist-tasks/4821/notes" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "43f2c567-57e4-4fac-8044-b22e01ec562c",
  "timestamp": "2026-06-08T17:25:28Z",
  "data": [
    {
      "id": 9120,
      "text": "Uploaded the latest rent roll — let me know if you also need the prior year.",
      "created_by": {
        "id": 88,
        "name": "Dana Lender"
      },
      "created_at": "2026-06-08T10:00:00Z",
      "updated_at": "2026-06-08T10:00:00Z"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 50,
    "has_more": false,
    "next_cursor": null
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Checklist task not found— The task_id doesn't exist or isn't accessible to the authenticated user

Create Checklist Task Note

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

Add a comment to a checklist task

Posts a comment on a checklist task. The comment is attributed to the API key's user and is borrower-visible — it surfaces in the task's portal activity feed exactly like a comment typed there by you or the borrower. Simple HTML formatting is preserved; scripts and other unsafe markup are stripped.

Supports the Idempotency-Key header to prevent duplicate creation. Requires the checklists:write scope.

Path parameters
task_idintegerrequired
The checklist task ID
Request body
textstringrequired
Comment text. Must be non-empty.
curl -X POST "https://api.lev.com/api/external/v2/checklist-tasks/4821/notes" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{"text": "Uploaded the latest rent roll — let me know if you also need the prior year."}'

Response (201):

{
  "request_id": "d0e1f2a3-b4c5-6789-3456-890123456789",
  "timestamp": "2026-06-08T15:30:45Z",
  "data": {
    "id": 9120,
    "text": "Uploaded the latest rent roll — let me know if you also need the prior year.",
    "created_by": {
      "id": 88,
      "name": "Dana Lender"
    },
    "created_at": "2026-06-08T15:30:45Z",
    "updated_at": "2026-06-08T15:30:45Z"
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Checklist task not found— The task_id doesn't exist or isn't accessible to the authenticated user
422validation_error
text must not be blank— The request omits text or sends an empty string

Update Checklist Task Note

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

Edit a checklist task comment

Replaces the text of a comment on a checklist task. Requires the checklists:write scope. Use the id returned by List Checklist Task Notes or Create Checklist Task Note as note_id.

Only comments the authenticated user created and can still write are editable. Comments on another checklist task, hidden or private comments, and comments the user does not own return 404 Not Found.

Supports the Idempotency-Key header to make retries safe.

Path parameters
task_idintegerrequired
The checklist task ID
note_idintegerrequired
The note ID
Request body
textstringrequired
Replacement comment text. Must be non-empty.
curl -X PATCH "https://api.lev.com/api/external/v2/checklist-tasks/4821/notes/9120" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{"text": "Uploaded the latest rent roll and prior-year version."}'

Response (200):

{
  "request_id": "90d7bfc2-ff5e-4b4e-9c77-7987957fd6a7",
  "timestamp": "2026-06-19T15:30:45Z",
  "data": {
    "id": 9120,
    "text": "Uploaded the latest rent roll and prior-year version.",
    "created_by": {
      "id": 88,
      "name": "Dana Lender"
    },
    "created_at": "2026-06-08T15:30:45Z",
    "updated_at": "2026-06-19T15:30:45Z"
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Checklist task or note not found— The task_id or note_id doesn't exist, doesn't match, isn't externally visible, or isn't writable by the authenticated user
400bad_request
text cannot be empty— The supplied text becomes empty after sanitization
422validation_error
text must not be blank— The request omits text or sends an empty string

Delete Checklist Task Note

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

Permanently delete a checklist task comment

Permanently deletes a comment on a checklist task. Requires the checklists:write scope. Use the id returned by List Checklist Task Notes or Create Checklist Task Note as note_id.

Only comments the authenticated user created and can still write are deletable. Comments on another checklist task, hidden or private comments, and comments the user does not own return 404 Not Found.

Path parameters
task_idintegerrequired
The checklist task ID
note_idintegerrequired
The note ID
curl -X DELETE "https://api.lev.com/api/external/v2/checklist-tasks/4821/notes/9120" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "request_id": "2c23bb16-8a8c-4a20-8ec0-9af5f555c84c",
  "timestamp": "2026-06-19T15:30:45Z",
  "data": {
    "deleted": true
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Checklist task or note not found— The task_id or note_id doesn't exist, doesn't match, isn't externally visible, or isn't writable by the authenticated user

Create Checklist Task

POST/api/external/v2/checklist-tasks

Add a task to a deal's checklist

Creates a checklist task — a document request or to-do tracked on the deal, such as collecting the trailing-12 operating statements. Anchor the task under exactly one parent: provide one of section_id (inside a section), checklist_id (at the checklist root, outside any section), or parent_task_id (a subtask, one level deep). Get those ids from List Checklists.

Returns the created task in the same shape List Checklists returns. Requires the checklists:write scope. Supports the Idempotency-Key header to prevent duplicate creation.

Request body
titlestringrequired
Task title. Must be non-empty.
section_idinteger
Add the task inside this checklist section. Provide exactly one of section_id, checklist_id, or parent_task_id.
checklist_idinteger
Add the task at the checklist root, outside any section. Provide exactly one of section_id, checklist_id, or parent_task_id.
parent_task_idinteger
Add the task as a subtask of this task (one level deep). Provide exactly one of section_id, checklist_id, or parent_task_id.
descriptionstring
Optional longer detail.
statusstring
Initial review state: to_do, requested, reviewing, updates_needed, approved, or cancelled. Defaults to to_do.
assigned_user_idinteger
Lev user id to assign. Must be an active teammate in your account.
due_datestring
Due date (ISO 8601, YYYY-MM-DD).
document_type_idsarray
Expected document type ids for the request.
curl -X POST "https://api.lev.com/api/external/v2/checklist-tasks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{
    "title": "Trailing-12 operating statements",
    "section_id": 21,
    "description": "From the sponsor",
    "due_date": "2026-07-15",
    "document_type_ids": [15]
  }'

Response (201):

{
  "request_id": "b1c2d3e4-f5a6-7890-1234-567890abcdef",
  "timestamp": "2026-06-23T15:30:45Z",
  "data": {
    "id": 412,
    "title": "Trailing-12 operating statements",
    "description": "From the sponsor",
    "status": "to_do",
    "is_completed": false,
    "position": 4,
    "due_date": "2026-07-15",
    "role": null,
    "assignee": null,
    "assigned_team": null,
    "collaborators": [],
    "document_types": [
      {
        "id": 15,
        "name": "Operating statement"
      }
    ],
    "files": [],
    "subtasks": []
  }
}

See the Checklist Task Object for the full field reference.

401unauthorized
Authentication required— Missing or invalid Authorization header
403forbidden
Missing required scope: checklists:write— The credential does not include the checklists:write scope
400validation_error
assigned_user_id must belong to an active user in your account— The assigned_user_id is deactivated or belongs to another account
404not_found
Section, checklist, or parent task not found— The section_id, checklist_id, or parent_task_id doesn't exist or isn't accessible to the authenticated user
422validation_error
Provide exactly one of section_id, checklist_id, or parent_task_id— The request omits the anchor, sends more than one, or sends a blank title

Update Checklist Task

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

Change a checklist task's fields

Edits an existing checklist task — retitle, re-status, reassign, set a due date, or reopen it. Get the task_id from List Checklists. Sends only the fields you provide; omitted fields are left unchanged. Send null for description, assigned_user_id, due_date, or document_type_ids to clear them.

To mark a task complete use Complete Checklist Task — it also approves the task's attached files. Here is_completed accepts false only, to reopen a completed task. Requires the checklists:write scope. Supports the Idempotency-Key header to make retries safe.

Path parameters
task_idintegerrequired
The checklist task ID
Request body
titlestring
New title. Must be non-empty if provided.
descriptionstring|null
New description. Send `null` to clear.
statusstring
New review state: to_do, requested, reviewing, updates_needed, approved, or cancelled.
assigned_user_idinteger|null
New assignee. Must be an active teammate in your account. Send `null` to unassign.
due_datestring|null
New due date (ISO 8601, YYYY-MM-DD). Send `null` to clear.
is_completedboolean
Pass false to reopen a completed task. To mark a task complete use the complete endpoint.
document_type_idsarray|null
Replace the expected document type ids. Send `null` to clear.
curl -X PATCH "https://api.lev.com/api/external/v2/checklist-tasks/301" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{"due_date": "2026-07-15", "assigned_user_id": 88}'

Response (200):

{
  "request_id": "c2d3e4f5-a6b7-8901-2345-678901bcdef0",
  "timestamp": "2026-06-23T15:30:45Z",
  "data": {
    "id": 301,
    "title": "Rent roll",
    "description": "Trailing 12 months",
    "status": "reviewing",
    "is_completed": false,
    "position": 1,
    "due_date": "2026-07-15",
    "role": "borrower",
    "assignee": {
      "id": 88,
      "first_name": "Dana",
      "last_name": "Lender",
      "email": "dana@example.com"
    },
    "assigned_team": {
      "id": 8256,
      "type": "account",
      "name": "Borrower Team"
    },
    "collaborators": [],
    "document_types": [
      {
        "id": 15,
        "name": "Rent roll"
      }
    ],
    "files": [],
    "subtasks": []
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
403forbidden
Missing required scope: checklists:write— The credential does not include the checklists:write scope
400validation_error
assigned_user_id must belong to an active user in your account— The assigned_user_id is deactivated or belongs to another account
404not_found
Checklist task not found— The task_id doesn't exist or isn't accessible to the authenticated user
422validation_error
is_completed accepts false only; to mark a task complete use the complete endpoint— The request sets is_completed to true or sends a blank title

Complete Checklist Task

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

Mark a checklist task complete

Marks a checklist task done — the equivalent of checking it off in the deal's checklist. Completing also moves the task to approved status and approves any pending files already attached to it, matching the in-app action. Get the task_id from List Checklists.

The body is empty. The action is idempotent: completing an already-complete task leaves it complete. To reopen a completed task use Update Checklist Task with is_completed set to false. Requires the checklists:write scope. Supports the Idempotency-Key header to make retries safe.

Path parameters
task_idintegerrequired
The checklist task ID
curl -X POST "https://api.lev.com/api/external/v2/checklist-tasks/301/complete" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000"

Response (200):

{
  "request_id": "d3e4f5a6-b7c8-9012-3456-789012cdef01",
  "timestamp": "2026-06-23T15:30:45Z",
  "data": {
    "id": 301,
    "title": "Rent roll",
    "description": "Trailing 12 months",
    "status": "approved",
    "is_completed": true,
    "position": 1,
    "due_date": "2026-07-01",
    "role": "borrower",
    "assignee": {
      "id": 88,
      "first_name": "Dana",
      "last_name": "Lender",
      "email": "dana@example.com"
    },
    "assigned_team": {
      "id": 8256,
      "type": "account",
      "name": "Borrower Team"
    },
    "collaborators": [],
    "document_types": [
      {
        "id": 15,
        "name": "Rent roll"
      }
    ],
    "files": [
      {
        "vault_resource_id": 6001,
        "document_id": 901,
        "name": "Rent roll.xlsx",
        "origin": "manually_added"
      }
    ],
    "subtasks": []
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
403forbidden
Missing required scope: checklists:write— The credential does not include the checklists:write scope
404not_found
Checklist task not found— The task_id doesn't exist or isn't accessible to the authenticated user

Create Deal

POST/api/external/v2/deals

Create a new deal

Supports the Idempotency-Key header to prevent duplicate creation.

Request body
titlestringrequired
Deal title (1–255 characters)
loan_amountnumber
Requested loan amount
loan_typeLoanType
Loan type enum name, such as construction, heavy_bridge, or permanent
transaction_typeTransactionType
Transaction type enum name, such as acquisition, refinance, or new_construction
business_planBusinessPlanType
Business plan enum name: stabilized, value_add, construction, or land
descriptionstring
Deal description
estimated_close_datestring
Estimated close date (ISO 8601)
pipeline_idsinteger[]
Pipeline IDs to add the deal to
deal_financialsDealFinancialsWrite
Deal-level financial fields to create with the deal
curl -X POST "https://api.lev.com/api/external/v2/deals" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{
    "title": "456 Oak Ave Refinance",
    "loan_amount": 3500000,
    "loan_type": "permanent",
    "transaction_type": "refinance",
    "deal_financials": {
      "purchase_price": 5250000,
      "estimated_value": 5900000,
      "total_cost": 5500000
    }
  }'

Response (201):

{
  "request_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "timestamp": "2026-03-20T15:30:45Z",
  "data": {
    "id": 205,
    "title": "456 Oak Ave Refinance",
    "loan_amount": 3500000.0,
    "loan_type": "permanent",
    "transaction_type": "refinance",
    "business_plan": null,
    "description": null,
    "estimated_close_date": null,
    "close_date": null,
    "owner_account_id": 56,
    "owner_user": {
      "user_id": 789,
      "first_name": "Alex",
      "last_name": "Rivera"
    },
    "created_at": "2026-03-20T15:30:45Z",
    "updated_at": "2026-03-20T15:30:45Z",
    "archived": false,
    "archived_at": null
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
422validation_error
title is required— Missing the required title field in the request body

Update Deal

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

Update a deal (partial update)

Path parameters
deal_idintegerrequired
The deal ID

All request body fields are optional. Only provided fields are updated. For deal_financials, omitted nested fields are unchanged and explicit null clears an existing value.

Request body
titlestring
Deal title (min 1 character)
loan_amountnumber
Requested loan amount
loan_typeLoanType
Loan type enum name
transaction_typeTransactionType
Transaction type enum name
business_planBusinessPlanType
Business plan enum name
descriptionstring
Deal description
estimated_close_datestring
Estimated close date (ISO 8601)
deal_financialsDealFinancialsWrite
Deal-level financial fields to update
curl -X PATCH "https://api.lev.com/api/external/v2/deals/101" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "loan_amount": 7500000,
    "deal_financials": {
      "purchase_price": 6500000,
      "estimated_value": null
    }
  }'

Response (200):

{
  "request_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "timestamp": "2026-03-20T15:30:45Z",
  "data": {
    "id": 101,
    "title": "123 Main St Acquisition",
    "loan_amount": 7500000.0,
    "loan_type": "heavy_bridge",
    "transaction_type": "acquisition",
    "business_plan": "value_add",
    "description": "Mixed-use acquisition in downtown Chicago — updated loan amount",
    "estimated_close_date": "2026-07-15",
    "close_date": null,
    "owner_account_id": 56,
    "owner_user": {
      "user_id": 789,
      "first_name": "Alex",
      "last_name": "Rivera"
    },
    "created_at": "2026-01-15T10:00:00Z",
    "updated_at": "2026-03-20T15:30:45Z",
    "archived": false,
    "archived_at": null
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Deal not found— The deal_id doesn't exist or isn't accessible to the authenticated user

Delete Deal

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

Archive (soft-delete) a deal

Path parameters
deal_idintegerrequired
The deal ID

Response (200):

{
  "request_id": "...",
  "timestamp": "2026-03-20T15:30:45Z",
  "data": {
    "deleted": true
  }
}
401unauthorized
Authentication required— Missing or invalid Authorization header
404not_found
Deal not found— The deal_id doesn't exist or isn't accessible to the authenticated user

Document Object

FieldTypeDescription
idintegerUnique document identifier. Use this value in the document detail and download endpoints.
file_namestring|nullOriginal file name.
extensionstring|nullFile extension without the leading dot.
size_bytesinteger|nullFile size in bytes. May be null for older records whose size was not stored.
folder_pathstring|nullSlash-delimited path inside the vault, or null for files at the vault root.
uploaded_atstring|nullUpload timestamp (ISO 8601).
vaultDocumentVaultRefThe vault this document lives in — the compact id, type, title subset of the 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, embedded as the vault tag on each Document Object, and listed in the vaults array on each memo (List Memos, Get Memo).

FieldTypeDescription
idintegerUnique vault identifier. Pass as vault_id on List Documents to browse just this vault.
typestringStructural 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.
titlestring|nullHuman-readable vault name — Deal Resources for the private vault; shared vaults carry names like Deal Room, Closing, or a per-lender room.
is_defaultbooleantrue for the deal's primary Deal Room (its default shared vault). Returned by List Vaults; omitted from the embedded document vault tag.
document_countintegerNumber of documents in the vault. Returned by 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, one per shared vault.

FieldTypeDescription
idintegerUnique checklist identifier
vaultChecklistVaultRefThe Deal Room this checklist belongs to: id, type, and title. type is always shared; checklists do not live on the private Resources vault.
sectionsChecklistSection[]Ordered sections, each carrying its own tasks. See the Checklist Section Object.
tasksChecklistTask[]Root-level tasks that sit outside any section, in display order. See the Checklist Task Object.

Checklist Section Object

FieldTypeDescription
idintegerUnique section identifier
namestringSection name
positionintegerDisplay order inside the checklist
start_datestring|nullOptional section start date (ISO 8601)
end_datestring|nullOptional section end date (ISO 8601)
tasksChecklistTask[]The section's tasks in display order. See the Checklist Task Object.

Checklist Task Object

FieldTypeDescription
idintegerUnique task identifier
titlestringTask title
descriptionstring|nullTask description
statusstringReview state: to_do, requested, reviewing, updates_needed, approved, or cancelled.
is_completedbooleanSeparate done flag. A task can be approved but not marked complete.
positionintegerDisplay order inside its section or root task list
due_datestring|nullDue date (ISO 8601)
rolestring|nullRole responsible for the task, such as borrower or lender
assigneeChecklistAssignee|nullThe Lev user assigned to the task, or null when unassigned.
assigned_teamChecklistAssignedTeam|nullThe account, lender, or borrower team assigned to the task. name can be null for stale or out-of-scope team references.
collaboratorsChecklistCollaborator[]People looped into the task. Each carries id and email; id is null for external parties without a Lev account.
document_typesChecklistDocumentType[]Expected document types for the request, such as Appraisal or Rent roll.
filesChecklistTaskFile[]Uploaded files already linked to this task. Each file includes document_id, vault_resource_id, name, and origin.
subtasksChecklistTask[]Nested subtasks, one level deep, in the same task shape.

Deal Object

FieldTypeDescription
idintegerUnique deal identifier
titlestring|nullDeal title
loan_amountnumber|nullRequested loan amount
loan_typeLoanType|nullLoan type enum name
transaction_typeTransactionType|nullTransaction type enum name
business_planBusinessPlanType|nullBusiness plan enum name
descriptionstring|nullDeal description
estimated_close_datestring|nullEstimated close date (ISO 8601)
close_datestring|nullActual close date (ISO 8601)
owner_account_idinteger|nullOwning account (tenant) ID. Not the person who owns the deal — read owner_user for that
sponsor_private_company_idinteger|nullThe deal's sponsor company. Resolved from the deal's first sponsor association, so a deal with several sponsors reports only the first. null when the deal has no sponsor company
owner_userDealOwnerUser|nullThe person who owns the deal — its primary deal principal — as user_id, first_name, and last_name. null when the deal has no primary deal principal. Returned on list, get, create, and update responses, including under a sparse fields request, and identical whether or not ?include=team is requested
created_atstring|nullCreation timestamp (ISO 8601)
updated_atstring|nullLast update timestamp (ISO 8601)
archivedbooleanWhether the deal has been archived. Archiving is separate from pipeline status, so an archived deal keeps its last stage
archived_atstring|nullWhen the deal was archived (ISO 8601), or null when active
financialsobject|nullIncluded when ?include=financials (see Deal Financials)
propertiesarray|nullIncluded when ?include=properties (see Deal Properties)
teamarray|nullIncluded when ?include=team (see Deal Team)
pipelinesarray|nullIncluded when ?include=pipelines. Up to 10 current pipeline rows per deal, each with pipeline_id, status_name, updated_at (see Pipelines)
sponsor_contactsSponsorContact[]|nullIncluded when ?include=sponsor_contacts. Up to 25 connected sponsor contacts at the company named by sponsor_private_company_id, ordered is_primary first then by ascending id. [] when the deal has no sponsor company or that company has no connected sponsor contacts (see Sponsor Contact Object)
feesDealFees|nullIncluded when ?include=fees, which requires an admin of the deal's owning account or the deal's primary owner. null on a deal whose fees this key may not read; on the deals collection a non-admin key is refused outright instead (see Fees Object)

Returned only inside a deal's sponsor_contacts array. It is deliberately narrower than the Contact object. Postal address, photo, bio, secondary emails, and the contact-type and ownership fields are all left out, because a deal embed only needs to identify and reach the person. Read Contacts when you need the full record.

Contacts are scoped to the deal's owning account and to the caller's, and a contact must satisfy both. A deal shared to you from another account therefore embeds [], even when that deal has sponsor contacts of its own. Compare the deal's owner_account_id against your own account before you read an empty list as "no contacts on file".

Only contacts currently connected to the account appear here. Unlinking a sponsor contact in Lev, or deleting its company, removes it from this embed while the contact record itself survives. That contact stays readable by id through Get Contact, so presence in this list is a narrower test than existence.

Every identity field is nullable. Lev withholds all of them, phones included, on a contact the account has neither connected to nor paid for. Because this embed admits connected contacts only, that state does not arise through the include today. The published contract still allows it, so do not model these fields as non-null.

FieldTypeDescription
idintegerUnique contact identifier. Stable across syncs, and usable against Get Contact
company_idintegerThe sponsor private company the contact was resolved through. Always equal to the deal's sponsor_private_company_id value
first_namestring|nullGiven name
last_namestring|nullFamily name
full_namestring|nullGiven and family name joined. null rather than an empty string when neither name part is on file
titlestring|nullJob title, such as Managing Partner
departmentstring|nullDepartment
emailstring|nullPrimary email address. Secondary addresses are not embedded
phonesPhone[]|nullPhone numbers on the contact. [] means none on file. null means withheld. The two are distinct states, so do not collapse them
linkedin_urlstring|nullLinkedIn profile URL
is_primarybooleanWhether the contact is flagged primary at its company. Not unique: a company can have zero, one, or several primary contacts
created_atstring|nullWhen the contact was created (ISO 8601)
updated_atstring|nullWhen the contact was last updated (ISO 8601)

Fees Object

Returned inside a deal's fees key, and only when ?include=fees is both requested and authorized. It reports inbound fees: what the brokerage charges on the deal. Per-broker payouts, meaning the split of that fee among individual brokers, are deliberately not published here.

Reading fees takes more than access to the deal. On GET /deals/{deal_id} the key must belong to an admin of the account that owns the deal, or to that deal's primary owner — its primary deal principal. Anything else is a 403. The check runs after the 404 for a deal the key cannot see, so a refusal never reveals that a deal exists. On GET /deals the include is admin-only, and a non-admin key is refused for the whole request rather than served a page of nulls. An admin key gets the embed on its own account's deals and fees: null on any deal another account owns, because admin standing is scoped to one account: a parent-account admin is not an admin of the child.

Two consequences worth planning around. There is no self-serve path to a fee-capable key, because minting an API key already requires an admin — a consultant or integrator cannot create one for themselves, and cannot upgrade one they already hold. And a demotion from admin does not invalidate the key — it goes on reading deals — but it ends fee access loudly, not quietly: the collection starts refusing ?include=fees outright, and the detail route refuses every deal that person does not personally own. The one genuinely silent case is the account boundary above: a key that is still an admin reads fees: null on another account's deal.

gross_fee_amount is a total, not an estimate. It is null, never 0.0, whenever it cannot be stated exactly: any item's amount is null, items is empty, or the deal has more fee rows than the per-deal cap of 50. A deal sitting exactly on the cap keeps its total. A genuine zero fee is a list of rows that each resolve to 0.0 — neither a null nor an empty list.

At most 50 fee rows are embedded per deal, in a stable order, so a truncated list is a consistent slice rather than a random sample.

FieldTypeDescription
currencystringAlways USD. Lev stores no currency on a fee, so this states the assumption rather than leaving it implied
gross_fee_amountnumber|nullSum of every items[].amount. null when any one of them is null, when items is empty, or when the deal has more than 50 fee rows. Never 0.0 to mean unknown
itemsDealFeeItem[]The inbound fee rows, capped at 50 per deal and returned in a stable order. [] when the deal has no fee rows

Fee Item Object

One inbound fee row. A row is normally either a rate or a flat fee, but both columns can be set, and the flat amount then wins. Read amount rather than re-deriving it from rate_percent.

A fee row publishes no identifier of its own, so rows cannot be keyed or matched one-to-one across reads. Treat a deal's fee rows as a set and replace them wholesale rather than upserting them individually.

FieldTypeDescription
partyDealFeePartyWho the fee is charged to or paid through, such as sponsor or lender. Eleven values are published. referrer and employee are payout concepts and are unusual on an inbound row, but nothing in the schema pairs a direction with a party, so they are returned rather than filtered out. Treat an unlisted value as forward-compatible rather than invalid
rate_percentnumber|nullRate in percent, not a fraction: 1.5 means 1.5%, so divide by 100 before multiplying. null on a flat-fee row
basisDealFeeBasisWhat rate_percent is charged against. loan_amount is the only basis that resolves to a number. total_fee and net_fee are self-referential against the deal's own fee total, so a row on either always reports amount: null, which in turn nulls the deal's gross total
flat_amountnumber|nullFixed fee that replaces the rate entirely. null on a rate row
amountnumber|nullThe resolved fee in USD, and the field to read. null when it cannot be computed, such as a missing loan amount or a self-referential basis. Never 0.0 as a stand-in
created_atstring|nullWhen the fee row was created (ISO 8601)
updated_atstring|nullWhen the fee row was last updated (ISO 8601)
More in this section