# Contacts

> Manage lender and sponsor contacts in the Lev API.

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

Last updated: June 2026

---
## Overview

| Endpoint | Description |
|----------|-------------|
| `GET /contacts` | List contacts with pagination |
| `GET /contacts/{id}` | Get a single contact |
| `POST /contacts` | Create a new contact |
| `PATCH /contacts/{id}` | Update a contact |
| `POST /contacts/{id}/actions/unlock` | Reveal an AI-recommended lender contact (charges credits) |
| `GET /contacts/{id}/notes` | List notes logged on a contact |
| `POST /contacts/{id}/notes` | Add a note to a contact |
| `PATCH /contacts/{id}/notes/{note_id}` | Edit a contact note |
| `DELETE /contacts/{id}/notes/{note_id}` | Delete a contact note |

Contacts are scoped to your account. Only connected contacts (active relationships) are returned by default.

## List Contacts

### `GET /api/external/v2/contacts`

List contacts with pagination

#### Query Parameters

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

Response: `200`

#### Error Responses

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

## Get Contact

### `GET /api/external/v2/contacts/{contact_id}`

Get a single contact by ID

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_id` | `integer` | Yes | The contact ID |

Response: `200`

#### Error Responses

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

## Create Contact

### `POST /api/external/v2/contacts`

Create a new contact

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_type` | `string` | Yes | Contact type: lender_contact or sponsor |
| `company_id` | `integer` | Yes | Associated company ID |
| `first_name` | `string` | No | First name |
| `last_name` | `string` | No | Last name |
| `email` | `string` | No | Email address |
| `title` | `string` | No | Job title |
| `department` | `string` | No | Department |
| `address` | `string` | No | Street address |
| `address2` | `string` | No | Apartment, suite, or unit line |
| `city` | `string` | No | City |
| `state` | `string` | No | State |
| `zip` | `string` | No | ZIP code |
| `linkedin_url` | `string` | No | LinkedIn profile URL |
| `photo_url` | `string` | No | URL to a profile photo |
| `phones` | `PhoneIn[]` | No | Phone numbers. Each object: { raw, type?, country?, extension? }. raw accepts any common format and is normalized to E.164 server-side (unparseable numbers return 422). type is mobile, office, or fax (default mobile). country is a 2-letter ISO region used to parse numbers without a + prefix (default US). |
| `is_primary` | `boolean` | No | Whether this is the primary contact at the company |

Response: `201`

#### Error Responses

- **401** (unauthorized): Authentication required
- **403** (forbidden): User not authorized to create contact
- **422** (validation_error): contact_type is required

## Update Contact

### `PATCH /api/external/v2/contacts/{contact_id}`

Update a contact

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_id` | `integer` | Yes | The contact ID |

#### Request Body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `first_name` | `string` | No | First name |
| `last_name` | `string` | No | Last name |
| `email` | `string` | No | Email address |
| `title` | `string` | No | Job title |
| `department` | `string` | No | Department |
| `address` | `string` | No | Street address |
| `address2` | `string` | No | Apartment, suite, or unit line |
| `city` | `string` | No | City |
| `state` | `string` | No | State |
| `zip` | `string` | No | ZIP code |
| `linkedin_url` | `string` | No | LinkedIn profile URL |
| `photo_url` | `string` | No | URL to a profile photo |
| `phones` | `PhoneIn[]` | No | Replaces the contact's entire phone list — send [] to clear all numbers. Same object shape as Create Contact. |
| `is_primary` | `boolean` | No | Set to true to make this the primary contact at the company. Promoting one contact demotes the previous primary. |

Response: `200`

#### Error Responses

- **401** (unauthorized): Authentication required
- **404** (not_found): Contact not found
- **409** (conflict): Changing a contact's email is not supported yet

## Unlock Contact

### `POST /api/external/v2/contacts/{contact_id}/actions/unlock`

Reveal an AI-recommended lender contact — charges credits; requires the ai:actions scope

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_id` | `integer` | Yes | The contact to reveal — the `contact_id` returned on the placement |

Response: `200` the revealed [Contact Object](#contact-object), now carrying the contact's name and other identity fields.

#### Error Responses

- **401** (unauthorized): Authentication required
- **402** (insufficient_credits): Insufficient credits to unlock this contact
- **403** (forbidden): Missing required scope: ai:actions
- **404** (not_found): Contact not found

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

## List Contact Notes

### `GET /api/external/v2/contacts/{contact_id}/notes`

List notes logged on a contact

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_id` | `integer` | Yes | The contact ID |

#### Query Parameters

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

Response: `200`

#### Error Responses

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

## Create Contact Note

### `POST /api/external/v2/contacts/{contact_id}/notes`

Add a note to a contact

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_id` | `integer` | Yes | The contact ID |

#### Request Body

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

Response: `201`

#### Error Responses

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

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

## Update Contact Note

### `PATCH /api/external/v2/contacts/{contact_id}/notes/{note_id}`

Edit a contact note

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_id` | `integer` | Yes | The contact ID |
| `note_id` | `integer` | Yes | The note ID |

#### Request Body

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

Response: `200`

#### Error Responses

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

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

## Delete Contact Note

### `DELETE /api/external/v2/contacts/{contact_id}/notes/{note_id}`

Permanently delete a contact note

#### Path Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contact_id` | `integer` | Yes | The contact ID |
| `note_id` | `integer` | Yes | The note ID |

Response: `200`

#### Error Responses

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

## Contact Object

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Contact identifier |
| `contact_type` | string\|null | First entry from `contact_types`, kept as a string for backwards compatibility. `lender_contact` or `sponsor`. |
| `contact_types` | string[] | All contact types assigned to the contact (a contact can be both a lender and a sponsor contact). Values: `lender_contact`, `sponsor`. |
| `first_name` | string\|null | First name |
| `last_name` | string\|null | Last name |
| `email` | string\|null | Email address |
| `title` | string\|null | Job title |
| `department` | string\|null | Department |
| `address` | string\|null | Street address |
| `address2` | string\|null | Apartment, suite, or unit line |
| `city` | string\|null | City |
| `state` | string\|null | State |
| `zip` | string\|null | ZIP code |
| `linkedin_url` | string\|null | LinkedIn profile URL |
| `photo_url` | string\|null | URL to a profile photo |
| `phones` | Phone[] | Phone numbers (`[]` when none). Each: `{id, type, raw, country, e164, extension}` — `e164` is the normalized canonical number (always server-computed from `raw`); `type` is `mobile`, `office`, or `fax`; fields may be null on records created before phone support. |
| `is_primary` | boolean | Whether this is the primary contact |
| `owner_account_id` | integer\|null | Owning account ID |
| `is_connected` | boolean | Whether this is a connected contact |
| `created_at` | string\|null | Creation timestamp |
| `updated_at` | string\|null | Last update timestamp |