Build

Placements

Create, update, and view lender placements in the Lev API.

Updated June 2026
GET/api/external/v2/placements
GET/api/external/v2/placements/{placement_id}
POST/api/external/v2/placements
PATCH/api/external/v2/placements/{placement_id}
GET/api/external/v2/placements/{placement_id}/notesNotes logged on a placement
POST/api/external/v2/placements/{placement_id}/notes
PATCH/api/external/v2/placements/{placement_id}/notes/{note_id}
DELETE/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).

EndpointDescription
GET /placementsList placements with filtering and pagination
GET /placements/{id}Get a single placement
POST /placementsCreate a placement against a deal
PATCH /placements/{id}Update a placement (use status="archived" to remove)
GET /placements/{id}/notesList notes logged on a placement
POST /placements/{id}/notesAdd 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

GET/api/external/v2/placements

List placements with pagination

Query parameters
limitinteger
Results per page (1–200, default 50)
cursorstring
Cursor for next page

Response (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
  }
}
401unauthorized
Authentication requiredMissing or invalid Authorization header
400bad_request
cursor and sort cannot be combined; use offset pagination when sortingBoth cursor and sort params provided

Get Placement

GET/api/external/v2/placements/{placement_id}

Get a single placement by ID

Path parameters
placement_idintegerrequired
The placement ID

Response (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"
  }
}
401unauthorized
Authentication requiredMissing or invalid Authorization header
404not_found
Placement not foundThe ID doesn't exist or isn't accessible to the authenticated user

Create Placement

POST/api/external/v2/placements

Create a placement against a deal

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

AI lender-contact matching

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.

Request body
deal_idintegerrequired
ID of the deal this placement belongs to
private_company_idintegerrequired
ID of the lender private company (use `GET /companies` to find candidates)
contact_idinteger
ID of the lender contact for this outreach. Must belong to the lender at `private_company_id`. Omit it and Lev runs an async AI match to recommend a contact at that lender; the recommended contact's name stays masked until you unlock it
statusstring
Placement status enum name. Defaults to `new`. Cannot be `archived` on create
visibilitystring
Placement visibility (`hidden`, `masked`, `shared`)
descriptionstring
Free-text note (e.g. lender-specific feedback)
scorenumber
Manual relevance score (0–100)
outreach_datestring
ISO 8601 timestamp the lender was first contacted
lender_statusstring
Lender-side status enum name

Status 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"
  }
}
400bad_request
Cannot create a placement with status='archived'; create an active placement and PATCH to archiveRequest body includes `status: "archived"`
400bad_request
A placement already exists for this deal, contact, and lender; use PATCH to update itAn active placement at this `(deal_id, contact_id)` pair already exists
401unauthorized
Authentication requiredMissing or invalid Authorization header
404not_found
Deal not found`deal_id` doesn't exist or isn't accessible to the authenticated user
404not_found
Private company not found`private_company_id` doesn't exist in the deal owner's account scope
404not_found
Contact not found`contact_id` doesn't exist, isn't a lender contact, or doesn't belong to the specified lender
422validation_error
status: input is not a valid enum memberUnknown enum value sent for `status`, `visibility`, or `lender_status`

Update Placement

PATCH/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.

Path parameters
placement_idintegerrequired
The placement ID
Request body
statusstring
Placement status enum name. Use `archived` to remove the placement
visibilitystring
Placement visibility (`hidden`, `masked`, `shared`)
descriptionstring
Free-text note. Changing it also appends a note to the placement's notes (List Placement Notes)
scorenumber
Manual relevance score (0–100)
outreach_datestring
ISO 8601 timestamp the lender was first contacted
lender_statusstring
Lender-side status enum name

deal_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"
  }
}
400bad_request
Request body must contain at least one field to updateEmpty request body
401unauthorized
Authentication requiredMissing or invalid Authorization header
404not_found
Placement not found`placement_id` doesn't exist or isn't accessible to the authenticated user
422validation_error
status: input is not a valid enum memberUnknown enum value sent for `status`, `visibility`, or `lender_status`

List Placement Notes

GET/api/external/v2/placements/{placement_id}/notes

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

Description updates appear here

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.

Path parameters
placement_idintegerrequired
The placement ID
Query parameters
limitinteger
Results per page (1–200, default 50)
cursorstring
Cursor for next page
curl "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
  }
}
401unauthorized
Authentication requiredMissing or invalid Authorization header
404not_found
Placement not foundThe placement_id doesn't exist or isn't accessible to the authenticated user

Create Placement Note

POST/api/external/v2/placements/{placement_id}/notes

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

Path parameters
placement_idintegerrequired
The placement ID
Request body
textstringrequired
Note text. Must be non-empty.
curl -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"
  }
}
401unauthorized
Authentication requiredMissing or invalid Authorization header
404not_found
Placement not foundThe placement_id doesn't exist or isn't accessible to the authenticated user
422validation_error
text must not be blankThe request omits text or sends an empty string

Update Placement Note

PATCH/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.

Path parameters
placement_idintegerrequired
The placement 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/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"
  }
}
401unauthorized
Authentication requiredMissing or invalid Authorization header
404not_found
Placement or note not foundThe placement_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 emptyThe supplied text becomes empty after sanitization
422validation_error
text must not be blankThe request omits text or sends an empty string

Delete Placement Note

DELETE/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.

Path parameters
placement_idintegerrequired
The placement ID
note_idintegerrequired
The note ID
curl -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
  }
}
401unauthorized
Authentication requiredMissing or invalid Authorization header
404not_found
Placement or note not foundThe placement_id or note_id doesn't exist, doesn't match, isn't externally visible, or isn't writable by the authenticated user

Placement Object

FieldTypeDescription
idintegerPlacement identifier
deal_idintegerAssociated deal ID
private_company_idinteger|nullLender private company ID. Null on masked non-owner reads
lender_namestring|nullDisplay lender name. Masked non-owner reads use borrower-portal labels such as Lender A
contact_idinteger|nullLender contact ID. Null on masked non-owner reads
contact_namestring|nullLender contact name. Null on masked non-owner reads, and null for an AI-recommended contact until you reveal it via Unlock Contactcontact_id is still present so you can unlock it
statusstring|nullCurrent placement status
lender_statusstring|nullLender-side status
descriptionstring|nullPlacement description
lev_scorenumber|nullAI-generated match score
scorenumber|nullManual score
outreach_datestring|nullDate of initial outreach
outreach_sourcestring|nullSource of outreach
last_communication_datestring|nullDate of last communication
lender_first_response_datestring|nullDate of lender's first response
visibilitystring|nullPlacement visibility
created_atstring|nullCreation timestamp
updated_atstring|nullLast update timestamp
More in this section