# Contacts
Create, read, update, and delete contacts in your team's books. These are the core endpoints of the RelayBook API.
All contact endpoints require the `Authorization` header. Most also require the `X-Book-ID` header to specify which book to operate on.
---
## List Contacts
Retrieve a paginated list of contacts from a book.
```
GET /api/v1/contacts
```
### Headers
| Header | Required | Description |
|--------|----------|-------------|
| `Authorization` | Yes | `Bearer rlb_your_key` |
| `X-Book-ID` | Yes | UUID of the book |
### Query Parameters
| Parameter | Type | Default | Max | Description |
|-----------|------|---------|-----|-------------|
| `limit` | integer | 50 | 200 | Number of contacts to return |
| `offset` | integer | 0 | — | Number of contacts to skip |
### Example Request
```bash
curl -X GET "https://app.relaybook.io/api/v1/contacts?limit=10&offset=0" \
-H "Authorization: Bearer rlb_your_key" \
-H "X-Book-ID: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
```
### Response
```
HTTP/1.1 200 OK
```
```json
{
"data": [
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"book_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"first_name": "Jane",
"last_name": "Cooper",
"company": "Acme Corp",
"job_title": "Head of Partnerships",
"emails": [
{ "address": "
[email protected]", "label": "work", "primary": true },
{ "address": "
[email protected]", "label": "personal" }
],
"phones": [
{ "number": "+1 555-0123", "label": "mobile", "primary": true }
],
"custom_fields": null,
"notes": "",
"photo_url": null,
"starred": true,
"birthday": "1990-03-15",
"website": "https://acme.com",
"addresses": [
{
"label": "office",
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94105",
"country": "US"
}
],
"dates": [],
"socials": [
{ "label": "linkedin", "value": "https://linkedin.com/in/janecooper" }
],
"relationships": [],
"created_by": "5e1c820d-b605-4f9e-ae4f-47be164176ca",
"created_at": "2026-01-15T09:30:00Z",
"updated_at": "2026-02-10T14:22:00Z"
}
],
"meta": {
"page": 1,
"limit": 10,
"total": 47
}
}
```
### Errors
| Status | Code | Cause |
|--------|------|-------|
| 400 | `MISSING_BOOK` | `X-Book-ID` header is missing |
| 403 | `FORBIDDEN` | Book doesn't belong to your team |
| 500 | `SERVER_ERROR` | Database error |
---
## Get Contact
Retrieve a single contact by its ID.
```
GET /api/v1/contacts/{id}
```
### Path Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | UUID | The contact's unique ID |
### Headers
| Header | Required | Description |
|--------|----------|-------------|
| `Authorization` | Yes | `Bearer rlb_your_key` |
### Example Request
```bash
curl -X GET "https://app.relaybook.io/api/v1/contacts/f47ac10b-58cc-4372-a567-0e02b2c3d479" \
-H "Authorization: Bearer rlb_your_key"
```
### Response
```
HTTP/1.1 200 OK
```
```json
{
"data": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"book_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"first_name": "Jane",
"last_name": "Cooper",
"company": "Acme Corp",
"job_title": "Head of Partnerships",
"emails": [
{ "address": "
[email protected]", "label": "work", "primary": true }
],
"phones": [
{ "number": "+1 555-0123", "label": "mobile", "primary": true }
],
"custom_fields": null,
"notes": "Met at SaaStr 2025. Interested in partnership.",
"photo_url": null,
"starred": true,
"birthday": "1990-03-15",
"website": "https://acme.com",
"addresses": [],
"dates": [],
"socials": [],
"relationships": [],
"created_by": "5e1c820d-b605-4f9e-ae4f-47be164176ca",
"created_at": "2026-01-15T09:30:00Z",
"updated_at": "2026-02-10T14:22:00Z"
}
}
```
### Errors
| Status | Code | Cause |
|--------|------|-------|
| 404 | `NOT_FOUND` | Contact doesn't exist or doesn't belong to your team |
---
## Create Contact
Add a new contact to a book.
```
POST /api/v1/contacts
```
### Headers
| Header | Required | Description |
|--------|----------|-------------|
| `Authorization` | Yes | `Bearer rlb_your_key` |
| `Content-Type` | Yes | `application/json` |
| `X-Book-ID` | Yes | UUID of the book to add the contact to |
### Request Body
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `first_name` | string | Conditional | Contact's first name |
| `last_name` | string | Conditional | Contact's last name |
| `company` | string | Conditional | Company or organization |
| `job_title` | string | No | Role or position |
| `emails` | array | No | Email addresses (see format below) |
| `phones` | array | No | Phone numbers (see format below) |
| `notes` | string | No | Free-text notes |
| `birthday` | string | No | Date in `YYYY-MM-DD` format |
| `website` | string | No | URL |
| `addresses` | array | No | Postal addresses (see format below) |
| `socials` | array | No | Social media profiles (see format below) |
> [!note] Required fields
> At least one of `first_name`, `last_name`, or `company` must be provided. A contact needs a name or company to be identifiable.
> [!warning] Custom fields, labels, and photo_url
> The create endpoint does not accept `custom_fields`, `labels`, or `photo_url`. To set these after creating a contact:
> - **Custom fields**: Use [[Bulk Operations#Bulk Update Custom Field]] with the returned contact `id`
> - **Labels**: Use `POST /api/v1/contacts/{id}/labels` (see [[Books & Labels]])
> - **photo_url**: Not currently supported via API
### Array Field Formats
**Emails:**
```json
[
{ "address": "
[email protected]", "label": "work", "primary": true },
{ "address": "
[email protected]", "label": "personal" }
]
```
Supported email labels: `work`, `personal`, `other`
**Phones:**
```json
[
{ "number": "+1 555-0123", "label": "mobile", "primary": true },
{ "number": "+1 555-0456", "label": "work" }
]
```
Supported phone labels: `mobile`, `work`, `other`
**Addresses:**
```json
[
{
"label": "work",
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94105",
"country": "US"
}
]
```
Supported address labels: `home`, `work`, `other`
**Social profiles:**
```json
[
{ "label": "linkedin", "value": "https://linkedin.com/in/janecooper" },
{ "label": "x", "value": "https://x.com/janecooper" },
{ "label": "facebook", "value": "https://facebook.com/janecooper" },
{ "label": "instagram", "value": "https://instagram.com/janecooper" }
]
```
**Supported label values:**
| Label | Display Name |
| ----------- | ------------ |
| `linkedin` | LinkedIn |
| `x` | X |
| `instagram` | Instagram |
| `facebook` | Facebook |
| `github` | GitHub |
| `youtube` | YouTube |
| `other` | Other |
Only include entries that have a non-empty value - omit any social platform with no data.
### Example Request
```bash
curl -X POST "https://app.relaybook.io/api/v1/contacts" \
-H "Authorization: Bearer rlb_your_key" \
-H "Content-Type: application/json" \
-H "X-Book-ID: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" \
-d '{
"first_name": "Marcus",
"last_name": "Chen",
"company": "Bright Studios",
"job_title": "Creative Director",
"emails": [
{ "address": "
[email protected]", "label": "work", "primary": true }
],
"phones": [
{ "number": "+1 555-0456", "label": "mobile", "primary": true }
],
"notes": "Referred by Jane Cooper",
"website": "https://brightstudios.com"
}'
```
### Response
```
HTTP/1.1 201 Created
```
```json
{
"data": {
"id": "b23dc4e5-6789-4abc-def0-123456789abc",
"book_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"first_name": "Marcus",
"last_name": "Chen",
"company": "Bright Studios",
"job_title": "Creative Director",
"emails": [
{ "address": "
[email protected]", "label": "work", "primary": true }
],
"phones": [
{ "number": "+1 555-0456", "label": "mobile", "primary": true }
],
"custom_fields": null,
"notes": "Referred by Jane Cooper",
"photo_url": null,
"starred": false,
"birthday": null,
"website": "https://brightstudios.com",
"addresses": [],
"dates": [],
"socials": [],
"relationships": [],
"created_by": "5e1c820d-b605-4f9e-ae4f-47be164176ca",
"created_at": "2026-02-18T16:52:56Z",
"updated_at": "2026-02-18T16:52:56Z"
}
}
```
> [!tip] Webhook event
> Creating a contact fires a `contact.created` [[Webhooks|webhook event]] if webhooks are configured for your team.
### Errors
| Status | Code | Cause |
|--------|------|-------|
| 400 | `MISSING_BOOK` | `X-Book-ID` header is missing |
| 400 | `INVALID_INPUT` | Request body is not valid JSON |
| 403 | `FORBIDDEN` | Book doesn't belong to your team |
| 422 | `VALIDATION_ERROR` | Missing required fields (name or company) |
| 500 | `SERVER_ERROR` | Database error |
---
## Update Contact
Update one or more fields on an existing contact. Only include the fields you want to change — omitted fields are left unchanged.
```
PATCH /api/v1/contacts/{id}
```
### Path Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | UUID | The contact's unique ID |
### Headers
| Header | Required | Description |
|--------|----------|-------------|
| `Authorization` | Yes | `Bearer rlb_your_key` |
| `Content-Type` | Yes | `application/json` |
### Request Body
All fields are optional. Only provided fields are updated.
| Field | Type | Description |
|-------|------|-------------|
| `first_name` | string | Contact's first name |
| `last_name` | string | Contact's last name |
| `company` | string | Company or organization |
| `job_title` | string | Role or position |
| `emails` | array | Replaces all email addresses |
| `phones` | array | Replaces all phone numbers |
| `notes` | string | Free-text notes |
| `birthday` | string or null | Date (`YYYY-MM-DD`) or `null` to clear |
| `website` | string | URL |
| `addresses` | array | Replaces all addresses |
| `socials` | array | Replaces all social profiles |
| `starred` | boolean | Star or unstar the contact |
> [!note] Array fields are replaced, not merged
> When you update `emails`, `phones`, `addresses`, or `socials`, the entire array is replaced. Include all values you want to keep, not just the new ones.
### Example Request
```bash
curl -X PATCH "https://app.relaybook.io/api/v1/contacts/f47ac10b-58cc-4372-a567-0e02b2c3d479" \
-H "Authorization: Bearer rlb_your_key" \
-H "Content-Type: application/json" \
-d '{
"company": "Acme Global",
"job_title": "VP of Partnerships",
"starred": true
}'
```
### Response
```
HTTP/1.1 200 OK
```
```json
{
"data": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"book_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"first_name": "Jane",
"last_name": "Cooper",
"company": "Acme Global",
"job_title": "VP of Partnerships",
"emails": [
{ "address": "
[email protected]", "label": "work", "primary": true }
],
"phones": [
{ "number": "+1 555-0123", "label": "mobile", "primary": true }
],
"custom_fields": null,
"notes": "Met at SaaStr 2025. Interested in partnership.",
"photo_url": null,
"starred": true,
"birthday": "1990-03-15",
"website": "https://acme.com",
"addresses": [],
"dates": [],
"socials": [],
"relationships": [],
"created_by": "5e1c820d-b605-4f9e-ae4f-47be164176ca",
"created_at": "2026-01-15T09:30:00Z",
"updated_at": "2026-02-18T17:05:00Z"
}
}
```
> [!tip] Webhook event
> Updating a contact fires a `contact.updated` [[Webhooks|webhook event]] with the full updated contact in the payload.
### Errors
| Status | Code | Cause |
|--------|------|-------|
| 400 | `INVALID_INPUT` | Request body is not valid JSON |
| 404 | `NOT_FOUND` | Contact doesn't exist or doesn't belong to your team |
| 500 | `SERVER_ERROR` | Database error |
---
## Delete Contact
Permanently remove a contact from a book.
```
DELETE /api/v1/contacts/{id}
```
### Path Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | UUID | The contact's unique ID |
### Headers
| Header | Required | Description |
|--------|----------|-------------|
| `Authorization` | Yes | `Bearer rlb_your_key` |
### Example Request
```bash
curl -X DELETE "https://app.relaybook.io/api/v1/contacts/f47ac10b-58cc-4372-a567-0e02b2c3d479" \
-H "Authorization: Bearer rlb_your_key"
```
### Response
```
HTTP/1.1 204 No Content
```
No response body is returned.
> [!tip] Webhook event
> Deleting a contact fires a `contact.deleted` [[Webhooks|webhook event]]. The payload contains the contact's `id` since the full record is no longer available.
### Errors
| Status | Code | Cause |
|--------|------|-------|
| 404 | `NOT_FOUND` | Contact doesn't exist or doesn't belong to your team |
| 500 | `SERVER_ERROR` | Database error |
---
## Contact Object
The full contact object returned by the API:
| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique contact identifier |
| `book_id` | UUID | The book this contact belongs to |
| `first_name` | string | First/given name |
| `last_name` | string | Last/family name |
| `company` | string | Company or organization |
| `job_title` | string | Role or position |
| `emails` | array | List of `{ address, label, primary }` objects |
| `phones` | array | List of `{ number, label, primary }` objects |
| `custom_fields` | object or null | Custom key-value data |
| `notes` | string | Free-text notes |
| `photo_url` | string or null | URL to the contact's photo |
| `starred` | boolean | Whether the contact is starred |
| `birthday` | string or null | Date in `YYYY-MM-DD` format |
| `website` | string | URL to the contact's website |
| `addresses` | array | List of structured address objects |
| `dates` | array | List of `{ label, value }` date objects |
| `socials` | array | List of `{ label, value }` social profiles |
| `relationships` | array | List of relationship objects |
| `created_by` | UUID or empty | User ID of the contact creator |
| `created_at` | timestamp | When the contact was created |
| `updated_at` | timestamp | When the contact was last modified |
---
**Next:** Work with [[Books & Labels]] to manage books and organize contacts with labels.