/api/external/v2/deals/api/external/v2/deals/{deal_id}Structured deal detail/api/external/v2/deals/{deal_id}/vaultsA deal's vaults — Resources and Deal Rooms/api/external/v2/deals/{deal_id}/documentsUploaded files across a deal's vaults/api/external/v2/deals/{deal_id}/documents/{document_id}Metadata for one uploaded file/api/external/v2/deals/{deal_id}/documents/{document_id}/downloadShort-lived signed file link/api/external/v2/deals/{deal_id}/checklistsDeal Room checklists with task status and attached file references/api/external/v2/deals/{deal_id}/memosA deal's memos (published + draft)/api/external/v2/deals/{deal_id}/memos/{memo_uuid}One memo with signed PDF links/api/external/v2/deals/{deal_id}/notesNotes logged on a deal/api/external/v2/deals/{deal_id}/notes/api/external/v2/deals/{deal_id}/notes/{note_id}/api/external/v2/deals/{deal_id}/notes/{note_id}/api/external/v2/checklist-tasks/{task_id}/notesComments on a checklist task/api/external/v2/checklist-tasks/{task_id}/notes/api/external/v2/checklist-tasks/{task_id}/notes/{note_id}/api/external/v2/checklist-tasks/{task_id}/notes/{note_id}/api/external/v2/checklist-tasks/api/external/v2/checklist-tasks/{task_id}/api/external/v2/checklist-tasks/{task_id}/completeOverview
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 |
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
/api/external/v2/dealsList deals with pagination, filtering, and sorting
limitintegercursorstringoffsetintegersortstringfieldsstringincludestringfilter[loan_type]LoanTypefilter[transaction_type]TransactionTypefilter[business_plan]BusinessPlanTypefilter[archived]booleanfilter[loan_amount][gte]numberfilter[loan_amount][lte]numberfilter[created_at][gte]stringfilter[created_at][lte]stringArchiving 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.
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.
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 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
}
}unauthorizedbad_requestGet Deal
/api/external/v2/deals/{deal_id}Get a single deal by ID
deal_idintegerrequiredincludestringfieldsstringResponse (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.
unauthorizedforbiddennot_foundDeal 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
/api/external/v2/deals/{deal_id}/vaultsList 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.
deal_idintegerrequiredcurl "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.
unauthorizedforbiddennot_foundList Documents
/api/external/v2/deals/{deal_id}/documentsList 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.
This endpoint supports search, extension, folder_id, and vault_id. It intentionally does not accept generic filter[...] parameters.
deal_idintegerrequiredlimitintegercursorstringsearchstringextensionstringfolder_idintegervault_idintegerfieldsstringcurl "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.
unauthorizedforbiddennot_foundnot_foundvalidation_errorGet Document
/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.
deal_idintegerrequireddocument_idintegerrequiredfieldsstringcurl "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" }
}
}unauthorizedforbiddennot_foundDownload Document
/api/external/v2/deals/{deal_id}/documents/{document_id}/downloadFetch 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.
deal_idintegerrequireddocument_idintegerrequiredcurl "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
}
}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.
unauthorizedforbiddennot_foundList Checklists
/api/external/v2/deals/{deal_id}/checklistsList 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.
deal_idintegerrequiredvault_idintegercurl "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.
unauthorizedforbiddennot_foundList Memos
/api/external/v2/deals/{deal_id}/memosList 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.
deal_idintegerrequiredlimitintegeroffsetintegersearchstringfilter[title]stringfilter[status]stringfilter[memo_type]stringfilter[pdf_ready]booleanvault_idintegercurl "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: [].
unauthorizednot_foundGet Memo
/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.
deal_idintegerrequiredmemo_uuidstringrequiredqualitystringcurl "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…" }
]
}
}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.
validation_errorunauthorizednot_foundList Deal Notes
/api/external/v2/deals/{deal_id}/notesList 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.
deal_idintegerrequiredlimitintegercursorstringcurl "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
}
}unauthorizednot_foundCreate Deal Note
/api/external/v2/deals/{deal_id}/notesAdd 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.
deal_idintegerrequiredtextstringrequiredcurl -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"
}
}unauthorizednot_foundvalidation_errorUpdate Deal Note
/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.
deal_idintegerrequirednote_idintegerrequiredtextstringrequiredcurl -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"
}
}unauthorizednot_foundbad_requestvalidation_errorDelete Deal Note
/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.
deal_idintegerrequirednote_idintegerrequiredcurl -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
}
}unauthorizednot_foundList Checklist Task Notes
/api/external/v2/checklist-tasks/{task_id}/notesList 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.
task_idintegerrequiredlimitintegercursorstringcurl "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
}
}unauthorizednot_foundCreate Checklist Task Note
/api/external/v2/checklist-tasks/{task_id}/notesAdd 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.
task_idintegerrequiredtextstringrequiredcurl -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"
}
}unauthorizednot_foundvalidation_errorUpdate Checklist Task Note
/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.
task_idintegerrequirednote_idintegerrequiredtextstringrequiredcurl -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"
}
}unauthorizednot_foundbad_requestvalidation_errorDelete Checklist Task Note
/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.
task_idintegerrequirednote_idintegerrequiredcurl -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
}
}unauthorizednot_foundCreate Checklist Task
/api/external/v2/checklist-tasksAdd 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.
titlestringrequiredsection_idintegerchecklist_idintegerparent_task_idintegerdescriptionstringstatusstringassigned_user_idintegerdue_datestringdocument_type_idsarraycurl -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.
unauthorizedforbiddenvalidation_errornot_foundvalidation_errorUpdate Checklist Task
/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.
task_idintegerrequiredtitlestringdescriptionstring|nullstatusstringassigned_user_idinteger|nulldue_datestring|nullis_completedbooleandocument_type_idsarray|nullcurl -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": []
}
}unauthorizedforbiddenvalidation_errornot_foundvalidation_errorComplete Checklist Task
/api/external/v2/checklist-tasks/{task_id}/completeMark 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.
task_idintegerrequiredcurl -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": []
}
}unauthorizedforbiddennot_foundCreate Deal
/api/external/v2/dealsCreate a new deal
Supports the Idempotency-Key header to prevent duplicate creation.
titlestringrequiredloan_amountnumberloan_typeLoanTypetransaction_typeTransactionTypebusiness_planBusinessPlanTypedescriptionstringestimated_close_datestringpipeline_idsinteger[]deal_financialsDealFinancialsWritecurl -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
}
}unauthorizedvalidation_errorUpdate Deal
/api/external/v2/deals/{deal_id}Update a deal (partial update)
deal_idintegerrequiredAll 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.
titlestringloan_amountnumberloan_typeLoanTypetransaction_typeTransactionTypebusiness_planBusinessPlanTypedescriptionstringestimated_close_datestringdeal_financialsDealFinancialsWritecurl -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
}
}unauthorizednot_foundDelete Deal
/api/external/v2/deals/{deal_id}Archive (soft-delete) a deal
deal_idintegerrequiredResponse (200):
{
"request_id": "...",
"timestamp": "2026-03-20T15:30:45Z",
"data": {
"deleted": true
}
}unauthorizednot_foundDocument 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 (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).
| Field | Type | Description |
|---|---|---|
id | integer | Unique vault identifier. Pass as vault_id on 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; omitted from the embedded document vault tag. |
document_count | integer | Number 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.
| 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. |
tasks | ChecklistTask[] | Root-level tasks that sit outside any section, in display order. See the 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
| 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 (tenant) ID. Not the person who owns the deal — read owner_user for that |
sponsor_private_company_id | integer|null | The 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_user | DealOwnerUser|null | The 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_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) |
properties | array|null | Included when ?include=properties (see Deal Properties) |
team | array|null | Included when ?include=team (see 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) |
sponsor_contacts | SponsorContact[]|null | Included 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) |
fees | DealFees|null | Included 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) |
Sponsor Contact 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.
| Field | Type | Description |
|---|---|---|
id | integer | Unique contact identifier. Stable across syncs, and usable against Get Contact |
company_id | integer | The sponsor private company the contact was resolved through. Always equal to the deal's sponsor_private_company_id value |
first_name | string|null | Given name |
last_name | string|null | Family name |
full_name | string|null | Given and family name joined. null rather than an empty string when neither name part is on file |
title | string|null | Job title, such as Managing Partner |
department | string|null | Department |
email | string|null | Primary email address. Secondary addresses are not embedded |
phones | Phone[]|null | Phone numbers on the contact. [] means none on file. null means withheld. The two are distinct states, so do not collapse them |
linkedin_url | string|null | LinkedIn profile URL |
is_primary | boolean | Whether the contact is flagged primary at its company. Not unique: a company can have zero, one, or several primary contacts |
created_at | string|null | When the contact was created (ISO 8601) |
updated_at | string|null | When 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.
| Field | Type | Description |
|---|---|---|
currency | string | Always USD. Lev stores no currency on a fee, so this states the assumption rather than leaving it implied |
gross_fee_amount | number|null | Sum 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 |
items | DealFeeItem[] | 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.
| Field | Type | Description |
|---|---|---|
party | DealFeeParty | Who 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_percent | number|null | Rate in percent, not a fraction: 1.5 means 1.5%, so divide by 100 before multiplying. null on a flat-fee row |
basis | DealFeeBasis | What 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_amount | number|null | Fixed fee that replaces the rate entirely. null on a rate row |
amount | number|null | The 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_at | string|null | When the fee row was created (ISO 8601) |
updated_at | string|null | When the fee row was last updated (ISO 8601) |