# Contacts

Manage individual contacts (people) in the CRM. Supports search, filtering, sub-resources (opportunities, activities, employers), and AI enrichment.

**Base URL:** `https://api.trustpager.com/functions/v1/api/v1`

## Endpoints

### GET /contacts

List all contacts with cursor-based pagination. Supports search, source filter, and date range. Note: contacts do not have tags -- tags live on opportunities only.

**Scopes:** `contacts:read` — [full detail](./contacts/get-contacts.md)

### GET /contacts/:id

Retrieve a single contact by ID. Supports field selection and employer expansion.

**Scopes:** `contacts:read` — [full detail](./contacts/get-contacts-id.md)

### POST /contacts

Create a new contact. first_name is required; last_name is optional. Contacts without a last name render cleanly in automation templates via {{contact.display_name}} and {{contact.greeting}}. Empty or whitespace-only last_name values are stored as NULL. Set skip_automations: true to suppress the contact_created trigger -- recommended for historical imports.

**Scopes:** `contacts:write` — [full detail](./contacts/post-contacts.md)

### PATCH /contacts/:id

Update an existing contact. Only include fields you want to change. Every successful PATCH emits a field-level audit row to crm_field_change_log (viewable at /data/crm-logs with the crm_audit:read scope).

**Scopes:** `contacts:write` — [full detail](./contacts/patch-contacts-id.md)

### DELETE /contacts/:id

Delete a contact. Returns 204 No Content on success.

**Scopes:** `contacts:write` — [full detail](./contacts/delete-contacts-id.md)

### POST /contacts/search

Full-text search across contact names, email, and phone. Returns up to 100 results.

**Scopes:** `contacts:read` — [full detail](./contacts/post-contacts-search.md)

### GET /contacts/:id/opportunities

List all opportunities associated with a contact. Legacy alias: GET /contacts/:id/deals.

**Scopes:** `contacts:read`, `opportunities:read` — [full detail](./contacts/get-contacts-id-opportunities.md)

### GET /contacts/:id/activities

List all activities (calls, meetings, notes) for a contact.

**Scopes:** `contacts:read`, `activities:read` — [full detail](./contacts/get-contacts-id-activities.md)

### GET /contacts/:id/employers

List company / account links for this contact.

**Scopes:** `contacts:read` — [full detail](./contacts/get-contacts-id-employers.md)

### POST /contacts/:id/employers/:customerId

Link a contact to a company / account (employer relationship). Path param name preserved for backward compatibility.

**Scopes:** `contacts:write` — [full detail](./contacts/post-contacts-id-employers-customerId.md)

### DELETE /contacts/:id/employers/:customerId

Remove an employer link between a contact and a company. Path param name preserved for backward compatibility.

**Scopes:** `contacts:write` — [full detail](./contacts/delete-contacts-id-employers-customerId.md)

### GET /contacts/:id/birthday-sends

Get birthday message send history for a contact. Returns all years birthday emails/SMS were sent, the channels used, and the send date. Useful for auditing birthday message delivery.

**Scopes:** `contacts:read` — [full detail](./contacts/get-contacts-id-birthday-sends.md)

### POST /contacts/bulk-create

Create up to 100 contacts in a single request. Built for historical migrations and bulk imports. Each record accepts the same fields as POST /contacts (first_name required). Set skip_automations: true to suppress contact_created triggers across all records. Returns a created array and an errors array.

**Scopes:** `contacts:write` — [full detail](./contacts/post-contacts-bulk-create.md)

### POST /contacts/voice/unsubscribe

Voice-agent unsubscribe endpoint. Returns text/plain. Called by a Retell voice agent when a caller asks to opt out. Resolves the caller by phone number (from args.phone in the Retell call envelope, with fallback to from_number for inbound or to_number for outbound calls). Unsubscribes ALL contacts in the workspace that share the phone number (Spam Act compliance -- up to 20 matched contacts). Sets email_unsubscribed and sms_unsubscribed to true on each matched contact. Returns a plain-text confirmation string the agent reads aloud. On error the response body begins with "UNSUBSCRIBE NOT COMPLETED -- DO NOT TELL THE CALLER THEY HAVE BEEN REMOVED" so the agent script can instruct the agent to handle the failure gracefully without misleading the caller.

**Scopes:** `contacts:write` — [full detail](./contacts/post-contacts-voice-unsubscribe.md)

### POST /contacts/bulk-delete

Permanently delete up to 100 contacts in a single request. Returns a count of deleted records. Cannot be undone.

**Scopes:** `contacts:delete` — [full detail](./contacts/post-contacts-bulk-delete.md)
