/api/external/v2/placements/api/external/v2/placements/{placement_id}/api/external/v2/placements/api/external/v2/placements/{placement_id}/api/external/v2/placements/{placement_id}/notesNotes logged on a placement/api/external/v2/placements/{placement_id}/notes/api/external/v2/placements/{placement_id}/notes/{note_id}/api/external/v2/placements/{placement_id}/notes/{note_id}Overview
Placements connect deals to lenders. Each placement has a status that tracks the lender's progression (e.g. sent, lender_reviewing, terms_received, closed).
| Endpoint | Description |
|---|---|
GET /placements | List placements with filtering and pagination |
GET /placements/{id} | Get a single placement |
POST /placements | Create a placement against a deal |
PATCH /placements/{id} | Update a placement (use status="archived" to remove) |
GET /placements/{id}/notes | List notes logged on a placement |
POST /placements/{id}/notes | Add a note to a placement |
PATCH /placements/{id}/notes/{note_id} | Edit a placement note |
DELETE /placements/{id}/notes/{note_id} | Delete a placement note |
Placement records themselves do not have a DELETE verb. To remove a placement, send PATCH with status="archived". deal_id, private_company_id, and contact_id are set at create time and cannot be reassigned — archive the placement and create a new one to change linkage.
Placement responses include lender_name and contact_name for display. When you can see the real lender identity, lender_name uses the deal owner's lender name and falls back to the canonical lender name. On masked non-owner reads, lender_name uses the same generic borrower-portal label style as Lender A, while private_company_id, contact_id, and contact_name are null.
List Placements
/api/external/v2/placementsList placements with pagination
limitintegercursorstringResponse (200):
{
"request_id": "c5d6e7f8-a9b0-1234-8901-345678901234",
"timestamp": "2026-03-20T15:30:45Z",
"data": [
{
"id": 310,
"deal_id": 101,
"private_company_id": 12,
"lender_name": "JPMorgan Chase",
"contact_id": 78,
"contact_name": "Dana Lender",
"status": "lender_reviewing",
"lender_status": "lead_qualification",
"lev_score": 87.5,
"score": 4.0,
"description": "Strong fit — lender actively lending on multifamily in this submarket",
"outreach_date": "2026-02-01",
"outreach_source": "direct",
"last_communication_date": "2026-03-15T10:00:00Z",
"lender_first_response_date": "2026-02-03T09:30:00Z",
"visibility": "shared",
"created_at": "2026-02-01T08:00:00Z",
"updated_at": "2026-03-15T10:00:00Z"
}
],
"pagination": {
"total": 8,
"limit": 50,
"has_more": false,
"next_cursor": null
}
}unauthorizedbad_requestGet Placement
/api/external/v2/placements/{placement_id}Get a single placement by ID
placement_idintegerrequiredResponse (200):
{
"request_id": "d6e7f8a9-b0c1-2345-9012-456789012345",
"timestamp": "2026-03-20T15:30:45Z",
"data": {
"id": 310,
"deal_id": 101,
"private_company_id": 12,
"lender_name": "JPMorgan Chase",
"contact_id": 78,
"contact_name": "Dana Lender",
"status": "lender_reviewing",
"lender_status": "lead_qualification",
"lev_score": 87.5,
"score": 4.0,
"description": "Strong fit — lender actively lending on multifamily in this submarket",
"outreach_date": "2026-02-01",
"outreach_source": "direct",
"last_communication_date": "2026-03-15T10:00:00Z",
"lender_first_response_date": "2026-02-03T09:30:00Z",
"visibility": "shared",
"created_at": "2026-02-01T08:00:00Z",
"updated_at": "2026-03-15T10:00:00Z"
}
}unauthorizednot_foundCreate Placement
/api/external/v2/placementsCreate a placement against a deal
Supports the Idempotency-Key header to prevent duplicate creation. Requires the placements:write scope.
When you omit contact_id, Lev runs an asynchronous AI match to recommend the best contact at the lender (private_company_id). The placement is created and returned right away with no contact attached. Poll Get Placement — once matching completes, the placement carries a recommended contact_id whose contact_name stays masked (null) until you reveal it with Unlock Contact, which charges credits. Pass an explicit contact_id to skip matching and attach a known contact directly.
deal_idintegerrequiredprivate_company_idintegerrequiredcontact_idintegerstatusstringvisibilitystringdescriptionstringscorenumberoutreach_datestringlender_statusstringStatus enum values
new, sent, lender_reviewing, terms_received, term_sheet_received, executed_ts_in_closing, closed, unresponsive, willing_to_negotiate, lender_passed, carve_out, carve_out_closed. archived is reserved for Update Placement and cannot be used on create.
Lender status enum values
origination, new, lead_qualification, quotation, negotiation, offer, term_sheet, good_faith_deposit, diligence, in_closing, closed, archived.
curl -X POST "https://api.lev.com/api/external/v2/placements" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"deal_id": 101,
"private_company_id": 12,
"contact_id": 78,
"status": "sent",
"description": "Strong fit — initial outreach via direct intro"
}'Response (201):
{
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2026-05-19T15:30:45Z",
"data": {
"id": 311,
"deal_id": 101,
"private_company_id": 12,
"lender_name": "JPMorgan Chase",
"contact_id": 78,
"contact_name": "Dana Lender",
"status": "sent",
"lender_status": null,
"lev_score": null,
"score": null,
"description": "Strong fit — initial outreach via direct intro",
"outreach_date": null,
"outreach_source": null,
"last_communication_date": null,
"lender_first_response_date": null,
"visibility": "hidden",
"created_at": "2026-05-19T15:30:45Z",
"updated_at": "2026-05-19T15:30:45Z"
}
}bad_requestbad_requestunauthorizednot_foundnot_foundnot_foundvalidation_errorUpdate Placement
/api/external/v2/placements/{placement_id}Update a placement (partial update)
Supports the Idempotency-Key header. Requires the placements:write scope. Send only the fields you want to change — at least one body field is required.
placement_idintegerrequiredstatusstringvisibilitystringdescriptionstringscorenumberoutreach_datestringlender_statusstringdeal_id, private_company_id, and contact_id are not editable. To move a placement to a different lender or contact, archive this placement (status: "archived") and create a new one.
Archiving: sending status: "archived" clears contact_id, sets visibility: "hidden", and removes the placement from list and detail reads. Other fields sent alongside (e.g. a final description) still apply.
curl -X PATCH "https://api.lev.com/api/external/v2/placements/311" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "lender_reviewing",
"description": "Lender confirmed receipt and is reviewing rent roll"
}'Response (200):
{
"request_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2026-05-19T15:30:45Z",
"data": {
"id": 311,
"deal_id": 101,
"private_company_id": 12,
"lender_name": "JPMorgan Chase",
"contact_id": 78,
"contact_name": "Dana Lender",
"status": "lender_reviewing",
"lender_status": null,
"lev_score": null,
"score": null,
"description": "Lender confirmed receipt and is reviewing rent roll",
"outreach_date": null,
"outreach_source": null,
"last_communication_date": "2026-05-19T15:30:45Z",
"lender_first_response_date": null,
"visibility": "hidden",
"created_at": "2026-05-19T15:30:45Z",
"updated_at": "2026-05-19T15:30:45Z"
}
}bad_requestunauthorizednot_foundvalidation_errorList Placement Notes
/api/external/v2/placements/{placement_id}/notesList notes logged on a placement
Lists the free-text notes logged on a placement — lender conversations, outreach updates, and other human-written commentary on this lender's progress. Notes are returned oldest first; walk the next_cursor in the response to reach the most recent. Internal or private notes are never returned.
Notes are commentary, distinct from the placement's structured fields like status, score, and lender_status.
Updating a placement's description (via Update Placement) also records it as a note. Those notes appear in this list alongside notes you add directly — they share the same shape, so a description change and a posted note are indistinguishable in the response.
placement_idintegerrequiredlimitintegercursorstringcurl "https://api.lev.com/api/external/v2/placements/310/notes" \
-H "Authorization: Bearer YOUR_API_KEY"Response (200):
{
"request_id": "43f2c567-57e4-4fac-8044-b22e01ec562c",
"timestamp": "2026-06-08T17:25:28Z",
"data": [
{
"id": 8120,
"text": "Lender reviewed the package — comfortable at 65% LTV, wants an 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 Placement Note
/api/external/v2/placements/{placement_id}/notesAdd a note to a placement
Logs a free-text note on a placement. 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 placements:write scope.
placement_idintegerrequiredtextstringrequiredcurl -X POST "https://api.lev.com/api/external/v2/placements/310/notes" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{"text": "Lender reviewed the package and wants an updated rent roll."}'Response (201):
{
"request_id": "d0e1f2a3-b4c5-6789-3456-890123456789",
"timestamp": "2026-06-08T15:30:45Z",
"data": {
"id": 8120,
"text": "Lender reviewed the package and wants 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 Placement Note
/api/external/v2/placements/{placement_id}/notes/{note_id}Edit a placement note
Replaces the text of a note logged on a placement. Requires the placements:write scope. Use the id returned by List Placement Notes or Create Placement Note as note_id.
Only notes the authenticated user created and can still write are editable. Notes on another placement, hidden or private notes, and notes the user does not own return 404 Not Found.
Supports the Idempotency-Key header to make retries safe.
placement_idintegerrequirednote_idintegerrequiredtextstringrequiredcurl -X PATCH "https://api.lev.com/api/external/v2/placements/310/notes/8120" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{"text": "Lender reviewed the package — comfortable at 65% LTV and wants an updated rent roll."}'Response (200):
{
"request_id": "90d7bfc2-ff5e-4b4e-9c77-7987957fd6a7",
"timestamp": "2026-06-19T15:30:45Z",
"data": {
"id": 8120,
"text": "Lender reviewed the package — comfortable at 65% LTV and wants an updated rent roll.",
"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 Placement Note
/api/external/v2/placements/{placement_id}/notes/{note_id}Permanently delete a placement note
Permanently deletes a note logged on a placement. Requires the placements:write scope. Use the id returned by List Placement Notes or Create Placement Note as note_id.
Only notes the authenticated user created and can still write are deletable. Notes on another placement, hidden or private notes, and notes the user does not own return 404 Not Found.
placement_idintegerrequirednote_idintegerrequiredcurl -X DELETE "https://api.lev.com/api/external/v2/placements/310/notes/8120" \
-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_foundPlacement Object
| Field | Type | Description |
|---|---|---|
id | integer | Placement identifier |
deal_id | integer | Associated deal ID |
private_company_id | integer|null | Lender private company ID. Null on masked non-owner reads |
lender_name | string|null | Display lender name. Masked non-owner reads use borrower-portal labels such as Lender A |
contact_id | integer|null | Lender contact ID. Null on masked non-owner reads |
contact_name | string|null | Lender contact name. Null on masked non-owner reads, and null for an AI-recommended contact until you reveal it via Unlock Contact — contact_id is still present so you can unlock it |
status | string|null | Current placement status |
lender_status | string|null | Lender-side status |
description | string|null | Placement description |
lev_score | number|null | AI-generated match score |
score | number|null | Manual score |
outreach_date | string|null | Date of initial outreach |
outreach_source | string|null | Source of outreach |
last_communication_date | string|null | Date of last communication |
lender_first_response_date | string|null | Date of lender's first response |
visibility | string|null | Placement visibility |
created_at | string|null | Creation timestamp |
updated_at | string|null | Last update timestamp |