Contacts API
The Contacts API is available under /apis/v1/contact and requires a Ration bearer token. All contacts and address books are scoped to the authenticated user and tenant.
Authorization: Bearer <ration_access_token>
Content-Type: application/json
Ration supports CardDAV interoperability. The REST routes below are the API-client equivalent for address-book and contact management.
Address books
Default books
GET /apis/v1/contact/addressbooks returns { "results": [AddressBook] }, including these built-in concepts:
| Book | special_use | Behavior |
|---|---|---|
| My Contact | self | The caller's own profile contact. It is read-only as an address-book container: it cannot be renamed, deleted, bulk-moved, imported into, or used for CardDAV-style sync. Update it through /contact/self. |
| Global directory | global | Tenant directory of organization members plus administrator-managed contacts. Retrieve it with the dedicated /global endpoint. It is not a writable personal import target. |
| Personal books | — | Books created by the caller and used for ordinary contact CRUD, imports, and sync. |
An address-book object contains id, name, destructible, type, sync_token, created_at, updated_at, and special_use.
Create, update, delete, or bulk move
| Method | Path | Request | Successful response |
|---|---|---|---|
POST | /addressbooks | { "name": "Customers" } | Created address book (201). |
GET | /addressbooks/:id | — | Address book. |
PUT | /addressbooks/:id | { "addressbook": { "name": "Customers 2026" } } | Text Address book updated successfully. |
DELETE | /addressbooks/:id | — | 204 No Content. |
POST | /addressbooks/:id/move_contacts | { "to_addressbook_id": "destination-id" } | { "message": "Contacts successfully moved", "contacts_moved": ... }. |
My Contact cannot be renamed, deleted, or used as either side of a bulk move. A missing book returns 404.
Contacts
Contact representation
Contact responses use this stable shape:
| Field | Type | Description |
|---|---|---|
id, external_id | String | Internal and interoperable contact identifiers. Use external_id in the contact routes. |
addressbook_id, owner_id | String | Owning book and user. |
given_name, family_name, additional_names, nickname | String | Personal names. |
organization | Object | company, department, and title. |
emails, phone_numbers, urls, ims, related | Array | Values shaped as { "type": "…", "value": "…" }. |
addresses | Array | Postal addresses with type, street_address, extended_address, locality, region, postal_code, country, and post_office_box. |
categories, custom_fields, vcard_properties | Array | Categories and extensible vCard fields. |
birthday, anniversary, timezone, language, gender, notes, photo, logo, kind | Mixed | Optional profile and vCard details. |
vcard | String | Serialized vCard representation. |
created_at, updated_at | String | Timestamps. |
List, read, create, update, and delete
| Method | Path | Description |
|---|---|---|
GET | /addressbooks/:addressbook_id/contacts | List contacts in an owned address book: { "results": [Contact] }. |
GET | /addressbooks/:addressbook_id/contacts/:external_id | Read one contact. |
POST | /addressbooks/:addressbook_id/contacts | Create a contact. |
PUT | /addressbooks/:addressbook_id/contacts/:external_id | Update a contact; upsert: true permits creation for a missing external ID. |
DELETE | /addressbooks/:addressbook_id/contacts/:external_id | Delete an owned contact (204). |
POST | /addressbooks/:addressbook_id/contacts/:external_id/move | Move one contact; body requires to_addressbook_id. |
For create/update, send fields either under contact or at the top level. Example:
{
"contact": {
"given_name": "Ada",
"family_name": "Okafor",
"organization": { "company": "Ration", "title": "Designer" },
"emails": [{ "type": "work", "value": "ada@example.com" }],
"phone_numbers": [{ "type": "mobile", "value": "+234…" }]
}
}
POST can include a top-level external_id for idempotent imports; if it already exists, the API returns 409 { "error": "Contact already exists" }. Do not place that field only inside contact and expect it to be an import key.
Global tenant directory
GET /apis/v1/contact/addressbooks/:addressbook_id/global
This lists the Global directory with pagination:
| Query parameter | Type | Description |
|---|---|---|
query or q | String | Search name, email, and vCard data. |
name, email, organization/company, phone, state | String | Field-specific filters. |
page | Integer | Page number; defaults to 1. |
limit | Integer | Page size; defaults to 50, maximum 200. |
Response: { "success": true, "results": [Contact], "total": Number, "page": Number, "limit": Number, "has_more": Boolean }.
Self contact and card
| Method | Path | Description |
|---|---|---|
GET | /self | Read the caller's own Contact object. |
PUT | /self | Update the caller's own contact; accepts contact nesting or direct contact fields. |
GET | /self/card | Returns contact, vcard, vcard_url, profile_url, and host_origin. |
Sync and phone contacts
Address-book synchronization
| Method | Path | Request / response |
|---|---|---|
GET | /addressbooks/:addressbook_id/contacts/sync?sync_token=… | Returns { "sync_token": "…", "changes": [...] }. Each change has external_id, change_type, and (except deletes) contact. |
POST | /addressbooks/:addressbook_id/contacts/sync | Applies sync operations and returns addressbook_id, sync_token, counts, and per-operation results. |
The Self book cannot be synchronized. A sync apply with no operations returns 400 { "error": "No sync operations provided" }.
Phone sync
GET /apis/v1/contact/phone-sync/export
Use include_self (Boolean; default true) and supported contact-source filters. Response: { "contacts": [Contact], "count": Number, "include_self": Boolean }.
POST /apis/v1/contact/phone-sync/import
Send a contact collection plus optional target_addressbook_id (or addressbook_id) and prefer_fetched:
{
"target_addressbook_id": "personal-book-id",
"contacts": [{ "given_name": "Ada", "emails": [{ "type": "work", "value": "ada@example.com" }] }]
}
Response supplies addressbook_id, addressbook_name, processed, created_count, updated_count, failure_count, and individual results. Global and My Contact are read-only import targets.
Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid bearer authentication. |
404 | Address book or contact not found. |
409 | Contact already exists for an idempotent import key. |
400 | Missing phone-sync contacts or sync operations. |
422 | Validation error, attempt to modify a protected book/contact, or invalid writable target. |