Clients
The people you work for: add them, edit them, give them access to what you share.
List clients
/api/v1/clientsYour clients, newest first. Archived clients stay out unless you ask. Pages by cursor, 20 at a time and up to 50: follow links.next until it is null.
An API key carrying the scope named here, or a signed-in session.
- Scope
clients:read- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
status | Query | "active" | "archived" | "all" | Optional | active (the default) leaves archived clients out, archived lists only them, all lists both. It stays on the next page's link. |
limit | Query | integer | Optional | How many to return: 1 to 50, 20 when absent. A number outside that range is clamped; text is ignored. |
cursor | Query | string | Optional | Where the last page ended: the cursor inside the previous page's links.next. Pass links as given; a cursor you edit is refused. |
Examples
curl "https://liviamatch.com/api/v1/clients" \
-H "Authorization: Bearer $LIVIA_API_KEY"Responses
One page of clients.
{
"data": [
{
"id": "4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"name": "Tunde Bello",
"email": "tunde.bello@example.com",
"note": null,
"phone": null,
"archivedAt": null,
"createdAt": "2026-10-06T09:41:03.118Z",
"hasAccess": false,
"links": {
"self": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"access": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24/access"
}
},
{
"id": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"name": "Amara Okafor",
"email": "amara.okafor@example.com",
"note": "Relocating from Boston in January. Two kids, one dog.",
"phone": "+16155550142",
"archivedAt": null,
"createdAt": "2026-10-01T15:12:40.221Z",
"hasAccess": true,
"links": {
"self": "/api/v1/clients/9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"access": "/api/v1/clients/9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28/access"
}
}
],
"links": {
"next": null
}
}Fields (14)
| Field | Type | Description |
|---|---|---|
data | object[] | |
data[].id | string (uuid) | |
data[].name | string | |
data[].email | string | null | Where to reach them, or null when none is on file. |
data[].note | string | null | Your own note about them, or null. |
data[].phone | string | null | Their mobile number in E.164, or null. A ready match is texted to it when you ask to send it to them. |
data[].archivedAt | string (date-time) | null | When they were archived, or null for an active client. |
data[].createdAt | string (date-time) | |
data[].hasAccess | boolean | Whether they can sign in to see what you share: an invitation was sent, or an access code made. Needed before you share with them. |
data[].links | object | |
data[].links.self | string | |
data[].links.access | string | Where to POST to give them access. |
links | object | |
links.next | string | null | The URL of the next page, or null when this is the last. Follow it as given. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The status is not active, archived or all. |
| 400 | invalid-cursor | The cursor is not one a page linked to. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Add a client
/api/v1/clientsAdds a client and answers 201 with them and a Location. The server picks the id.
A POST that times out may or may not have landed, and a second one adds a second client. To make a retry safe, choose the id yourself and use PUT /api/v1/clients/{clientId}: the same id with the same body lands once.
An API key carrying the scope named here, or a signed-in session.
- Scope
clients:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Request body
The client. Only name is required. Unknown fields are refused.
Fields (4)
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Required | The client's name: 1 to 100 characters once trimmed.1 to 100 characters |
email | string | null | Optional | Where to reach them, or null. Needed before you can give them access to a share.at most 254 characters, default null |
note | string | null | Optional | Your own note about them, up to 2000 characters, or null.at most 2000 characters, default null |
phone | string | null | Optional | Their mobile number, kept as E.164 (a 10-digit US number gains +1), or null. A ready match is texted to it when you ask to send it to them.at most 40 characters |
{
"name": "Tunde Bello",
"email": "tunde.bello@example.com"
}Examples
curl -X POST "https://liviamatch.com/api/v1/clients" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Tunde Bello",
"email": "tunde.bello@example.com"
}'Responses
The client was added.
HeadersLocation
{
"data": {
"id": "4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"name": "Tunde Bello",
"email": "tunde.bello@example.com",
"note": null,
"phone": null,
"archivedAt": null,
"createdAt": "2026-10-06T09:41:03.118Z",
"hasAccess": false,
"links": {
"self": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"access": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24/access"
}
}
}Fields (12)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.name | string | |
data.email | string | null | Where to reach them, or null when none is on file. |
data.note | string | null | Your own note about them, or null. |
data.phone | string | null | Their mobile number in E.164, or null. A ready match is texted to it when you ask to send it to them. |
data.archivedAt | string (date-time) | null | When they were archived, or null for an active client. |
data.createdAt | string (date-time) | |
data.hasAccess | boolean | Whether they can sign in to see what you share: an invitation was sent, or an access code made. Needed before you share with them. |
data.links | object | |
data.links.self | string | |
data.links.access | string | Where to POST to give them access. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Read a client
/api/v1/clients/{clientId}One of your clients, archived or not, and whether they can sign in to see what you share.
An API key carrying the scope named here, or a signed-in session.
- Scope
clients:read- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
clientId | Path | string (uuid) | Required | The client's id. |
Examples
curl "https://liviamatch.com/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24" \
-H "Authorization: Bearer $LIVIA_API_KEY"Responses
The client.
{
"data": {
"id": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"name": "Amara Okafor",
"email": "amara.okafor@example.com",
"note": "Relocating from Boston in January. Two kids, one dog.",
"phone": "+16155550142",
"archivedAt": null,
"createdAt": "2026-10-01T15:12:40.221Z",
"hasAccess": true,
"links": {
"self": "/api/v1/clients/9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"access": "/api/v1/clients/9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28/access"
}
}
}Fields (12)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.name | string | |
data.email | string | null | Where to reach them, or null when none is on file. |
data.note | string | null | Your own note about them, or null. |
data.phone | string | null | Their mobile number in E.164, or null. A ready match is texted to it when you ask to send it to them. |
data.archivedAt | string (date-time) | null | When they were archived, or null for an active client. |
data.createdAt | string (date-time) | |
data.hasAccess | boolean | Whether they can sign in to see what you share: an invitation was sent, or an access code made. Needed before you share with them. |
data.links | object | |
data.links.self | string | |
data.links.access | string | Where to POST to give them access. |
Errors
| Status | Code | When |
|---|---|---|
| 404 | not-found | No such client in your workspace. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Save a client at an id
/api/v1/clients/{clientId}Saves a client at an id you choose, which makes a retry land once: 201 when the id is new, 200 when it names one of your clients, whose name, email and note become what you sent (an email or note you leave out is cleared).
To change one field, use PATCH. An id that belongs to someone else's client is 409, and nothing of theirs is touched.
An API key carrying the scope named here, or a signed-in session.
- Scope
clients:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
clientId | Path | string (uuid) | Required | The client's id. |
Request body
The whole client, as for adding one.
Fields (4)
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Required | The client's name: 1 to 100 characters once trimmed.1 to 100 characters |
email | string | null | Optional | Where to reach them, or null. Needed before you can give them access to a share.at most 254 characters, default null |
note | string | null | Optional | Your own note about them, up to 2000 characters, or null.at most 2000 characters, default null |
phone | string | null | Optional | Their mobile number, kept as E.164 (a 10-digit US number gains +1), or null. A ready match is texted to it when you ask to send it to them.at most 40 characters |
{
"name": "Tunde Bello",
"email": "tunde.bello@example.com",
"note": "Prefers email."
}Examples
curl -X PUT "https://liviamatch.com/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Tunde Bello",
"email": "tunde.bello@example.com",
"note": "Prefers email."
}'Responses
A new client was saved.
HeadersLocation
{
"data": {
"id": "4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"name": "Tunde Bello",
"email": "tunde.bello@example.com",
"note": null,
"phone": null,
"archivedAt": null,
"createdAt": "2026-10-06T09:41:03.118Z",
"hasAccess": false,
"links": {
"self": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"access": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24/access"
}
}
}Fields (12)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.name | string | |
data.email | string | null | Where to reach them, or null when none is on file. |
data.note | string | null | Your own note about them, or null. |
data.phone | string | null | Their mobile number in E.164, or null. A ready match is texted to it when you ask to send it to them. |
data.archivedAt | string (date-time) | null | When they were archived, or null for an active client. |
data.createdAt | string (date-time) | |
data.hasAccess | boolean | Whether they can sign in to see what you share: an invitation was sent, or an access code made. Needed before you share with them. |
data.links | object | |
data.links.self | string | |
data.links.access | string | Where to POST to give them access. |
One of your clients was replaced.
HeadersLocation
{
"data": {
"id": "4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"name": "Tunde Bello",
"email": "tunde.bello@example.com",
"note": "Prefers email.",
"phone": null,
"archivedAt": null,
"createdAt": "2026-10-06T09:41:03.118Z",
"hasAccess": false,
"links": {
"self": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"access": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24/access"
}
}
}Fields (12)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.name | string | |
data.email | string | null | Where to reach them, or null when none is on file. |
data.note | string | null | Your own note about them, or null. |
data.phone | string | null | Their mobile number in E.164, or null. A ready match is texted to it when you ask to send it to them. |
data.archivedAt | string (date-time) | null | When they were archived, or null for an active client. |
data.createdAt | string (date-time) | |
data.hasAccess | boolean | Whether they can sign in to see what you share: an invitation was sent, or an access code made. Needed before you share with them. |
data.links | object | |
data.links.self | string | |
data.links.access | string | Where to POST to give them access. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
| 409 | id-unavailable | The id belongs to something that is not yours. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Change a client
/api/v1/clients/{clientId}Changes the fields you send and leaves the rest. null clears an email or a note. "archived": true archives the client (they leave the default list and everything of theirs is kept) and false restores them. Each change that took hold is a line in the audit trail, naming which fields moved and never their contents.
An API key carrying the scope named here, or a signed-in session.
- Scope
clients:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
clientId | Path | string (uuid) | Required | The client's id. |
Request body
Any of the fields; at least one.
Fields (5)
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Optional | A new name.1 to 100 characters |
email | string | null | Optional | A new address, or null to clear it.at most 254 characters |
note | string | null | Optional | A new note, or null to clear it.at most 2000 characters |
phone | string | null | Optional | A new mobile number, or null to clear it.at most 40 characters |
archived | boolean | Optional | true archives the client, false restores them. Archiving ends nothing: an archived client still signs in and still sees what was shared. |
{
"archived": true
}Examples
curl -X PATCH "https://liviamatch.com/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"archived": true
}'Responses
The client after the change.
{
"data": {
"id": "4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"name": "Tunde Bello",
"email": "tunde.bello@example.com",
"note": null,
"phone": null,
"archivedAt": "2026-10-06T10:02:55.640Z",
"createdAt": "2026-10-06T09:41:03.118Z",
"hasAccess": false,
"links": {
"self": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"access": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24/access"
}
}
}Fields (12)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.name | string | |
data.email | string | null | Where to reach them, or null when none is on file. |
data.note | string | null | Your own note about them, or null. |
data.phone | string | null | Their mobile number in E.164, or null. A ready match is texted to it when you ask to send it to them. |
data.archivedAt | string (date-time) | null | When they were archived, or null for an active client. |
data.createdAt | string (date-time) | |
data.hasAccess | boolean | Whether they can sign in to see what you share: an invitation was sent, or an access code made. Needed before you share with them. |
data.links | object | |
data.links.self | string | |
data.links.access | string | Where to POST to give them access. |
Errors
| Status | Code | When |
|---|---|---|
| 404 | not-found | No such client in your workspace. |
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Give a client access
/api/v1/clients/{clientId}/accessGives a client access to what you share with them: records the address they sign in by and emails them a sign-in link naming you, good for three days. A client needs access before you can share a shortlist with them.
The link goes to the email you send, else to the one on the client; with neither there is nobody to send it to (client-needs-email). Sending again sends a fresh link and ends the last. This is the one call here that emails someone, so it needs the shares:write scope.
An API key carrying the scope named here, or a signed-in session.
- Scope
shares:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
clientId | Path | string (uuid) | Required | The client's id. |
Request body
Optional. The address to send the link to, when it is not the one on the client. The body is optional.
Fields (1)
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Optional | The address to send the sign-in link to. Left out, the client's own address is used. Giving one replaces the address their access signs in by.at most 254 characters |
{
"email": "tunde.bello@example.com"
}Examples
curl -X POST "https://liviamatch.com/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24/access" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "tunde.bello@example.com"
}'Responses
The invitation was sent; the client now has access.
{
"data": {
"id": "4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"name": "Tunde Bello",
"email": "tunde.bello@example.com",
"note": null,
"phone": null,
"archivedAt": null,
"createdAt": "2026-10-06T09:41:03.118Z",
"hasAccess": true,
"links": {
"self": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24",
"access": "/api/v1/clients/4b9e2d71-0c58-4a36-8d1f-7e3a5c9b0f24/access"
}
}
}Fields (12)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.name | string | |
data.email | string | null | Where to reach them, or null when none is on file. |
data.note | string | null | Your own note about them, or null. |
data.phone | string | null | Their mobile number in E.164, or null. A ready match is texted to it when you ask to send it to them. |
data.archivedAt | string (date-time) | null | When they were archived, or null for an active client. |
data.createdAt | string (date-time) | |
data.hasAccess | boolean | Whether they can sign in to see what you share: an invitation was sent, or an access code made. Needed before you share with them. |
data.links | object | |
data.links.self | string | |
data.links.access | string | Where to POST to give them access. |
Errors
| Status | Code | When |
|---|---|---|
| 404 | not-found | No such client in your workspace. |
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
| 409 | client-needs-email | The client has no email on file and none was sent. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal