People
The people list screen, and the REST API endpoints for managing people within your organization.
The people list
People in the sidebar opens the list of everyone in your organization, at
/app/<your-org>/people. Each row shows:
| Column | What it holds |
|---|---|
| Name | The person's first and last name, next to their initials. Clicking it opens the person's details for editing. |
| Job title | Their role, or — when it is not set. |
| Company | The company they are linked to. Clicking it opens that company's record page. Reads — for a person with no company. |
| Their email address. | |
| Phone | Their phone number, or — when it is not set. |
A person has no page of their own — they open in a dialog, from this list and from a company's People tab alike. New person above the list creates one.
The dialog's Company field links the person to a company. Type to search your companies by name and pick one, choose No company to leave the person unlinked — an empty company is a normal record, not an incomplete one — or, when what you typed matches no existing company, choose Create "…" to create that company then and there and link the person to it. The new company is created with the name you typed and nothing else: it starts at the Lead lifecycle stage with the source Other, which you can change later on its record page. Opening the dialog from a company's People tab pre-fills this field with the company you came from.
Search the list with the box above it — it matches first name, last name and email. Narrow it with the Company filter, whose one option, No company, shows only the people who are not linked to any company. Sort by clicking the Name or Email header; the other three columns cannot be sorted.
Your search, filter, sort and page number all live in the address bar, so a filtered list is a link you can send to a colleague or bookmark.
Each row's ⋯ menu repeats Edit — the same dialog as the name — and adds
Delete. Nothing blocks deleting a person; the confirmation is the only step.
Endpoints
All endpoints require a valid API key in the Authorization header. See Authentication for details.
Base URL: /api/v1A person may be linked to a company through companyId, but the link is
optional: a person with no company is a normal record, not an incomplete one.
People carry no lifecycle stage — that belongs to the company alone.
List People
GET /api/v1/peopleQuery parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 25 | Number of results (1-100) |
offset | number | 0 | Pagination offset |
query | string | — | Search by first name, last name or email |
sortBy | string | "createdAt" | Sort field: firstName, lastName, email, createdAt, updatedAt |
sortOrder | string | "asc" | Sort direction: asc or desc |
companyId | string | — | Comma-separated company UUIDs |
hasCompany | string | — | true for people with a company, false for people without |
createdAt | string | — | Comma-separated date ranges (e.g., today,this-week) |
Date range values: today, this-week, this-month, older
These windows are cut at midnight UTC, not at midnight in any user's timezone — see Data Formats.
Omit hasCompany to get either; true and false are the only values it
takes, and anything else is a 422. It combines with companyId rather than
overriding it, so hasCompany=false alongside a companyId list is a
contradiction and matches nobody.
query is split into words, and a person matches when every word appears
in their first name, last name or email — in any order, case-insensitive — so
Ada Lovelace and lovelace ada both find Ada Lovelace. A word that matches
none of the three excludes the person; the company name is not searched.
query takes up to 200 characters, and a longer one is a 422. Words past the
eighth are ignored, and a query of only spaces is the same as none.
Returns { "data": [...], "total": number }.
Get Person
GET /api/v1/people/:idResponds 400 when the ID is not a UUID, and 404 when no person with that ID exists in your organization.
Create Person
POST /api/v1/people
Content-Type: application/json| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Yes | First name (1-100 characters) |
lastName | string | Yes | Last name (1-100 characters) |
email | string | Yes | Email address, stored lowercase (up to 255 chars) |
phone | string | No | Phone number (up to 50 characters) |
jobTitle | string | No | Job title (up to 200 characters) |
companyId | string | No | UUID of a company in your organization, or null |
Returns 201 with { "data": ... }.
Responds 400 when the body is not valid JSON, 422 when a field fails
validation, and 404 with { "error": "Company not found" } when companyId
does not exist or belongs to another organization.
Update Person
PATCH /api/v1/people/:id
Content-Type: application/jsonBody: same fields as Create (all optional). phone, jobTitle and companyId
accept null to clear them.
Responds 400 for an invalid ID or a body that is not valid JSON, and 422 when a
field fails validation. A 404 names the record that was missing:
{ "error": "Person not found" } or { "error": "Company not found" }.
Delete Person
DELETE /api/v1/people/:idReturns { "success": true } on success. Responds 400 when the ID is not a
UUID, and 404 when the person is not in your organization.
Response shape
{
"id": "3f8a1c22-...",
"organizationId": "9a2d...",
"companyId": "0c1f5b8e-...",
"firstName": "Ada",
"lastName": "Lovelace",
"email": "[email protected]",
"phone": null,
"jobTitle": "Head of Engineering",
"createdAt": "2026-09-01T14:54:01.000Z",
"updatedAt": "2026-09-01T14:54:01.000Z",
"company": { "id": "0c1f5b8e-...", "name": "Acme Inc." }
}companyId and company are both null for a person who is not linked to a
company.
createdAt and updatedAt are ISO 8601 in UTC. The API is not localized,
whatever language the app itself is showing you — see
Data Formats.
Examples
cURL
# List people
curl https://your-app.com/api/v1/people \
-H "Authorization: Bearer ba_a1b2c3d4e5f6..."
# List with filters
curl "https://your-app.com/api/v1/people?companyId=UUID_HERE&sortBy=lastName&sortOrder=asc&limit=10" \
-H "Authorization: Bearer ba_a1b2c3d4e5f6..."
# List people with no company
curl "https://your-app.com/api/v1/people?hasCompany=false" \
-H "Authorization: Bearer ba_a1b2c3d4e5f6..."
# Get a single person
curl https://your-app.com/api/v1/people/UUID_HERE \
-H "Authorization: Bearer ba_a1b2c3d4e5f6..."
# Create a person
curl -X POST https://your-app.com/api/v1/people \
-H "Authorization: Bearer ba_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{"firstName":"Ada","lastName":"Lovelace","email":"[email protected]"}'
# Update a person
curl -X PATCH https://your-app.com/api/v1/people/UUID_HERE \
-H "Authorization: Bearer ba_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{"jobTitle":"CTO","companyId":null}'
# Delete a person
curl -X DELETE https://your-app.com/api/v1/people/UUID_HERE \
-H "Authorization: Bearer ba_a1b2c3d4e5f6..."JavaScript / TypeScript
const API_KEY = "ba_a1b2c3d4e5f6...";
const BASE_URL = "https://your-app.com/api/v1";
// List people
const response = await fetch(
`${BASE_URL}/people?hasCompany=true&limit=10&sortOrder=desc`,
{ headers: { Authorization: `Bearer ${API_KEY}` } },
);
const { data, total } = await response.json();
// Create a person
const createRes = await fetch(`${BASE_URL}/people`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
firstName: "Ada",
lastName: "Lovelace",
email: "[email protected]",
companyId: "0c1f5b8e-...",
}),
});
const { data: newPerson } = await createRes.json();Python
import requests
API_KEY = "ba_a1b2c3d4e5f6..."
BASE_URL = "https://your-app.com/api/v1"
# List people
response = requests.get(
f"{BASE_URL}/people",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"companyId": "UUID_HERE", "limit": 10, "sortOrder": "desc"},
)
data = response.json()
# Create a person
response = requests.post(
f"{BASE_URL}/people",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"firstName": "Ada", "lastName": "Lovelace", "email": "[email protected]"},
)
new_person = response.json()