> ## Documentation Index
> Fetch the complete documentation index at: https://usegately.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Contacts (CRM)

> Manage contacts, tags, lists, and segments with the Gately SDK

The `GatelyContacts` class gives you full programmatic access to Gately's CRM — create and manage contacts, organise them into lists and segments, apply tags, track email activity, and handle unsubscribes.

## Installation

```bash theme={null}
npm install @gately/sdk
```

## Setup

```typescript theme={null}
import { GatelyContacts } from '@gately/sdk'

const crm = new GatelyContacts({
  projectId: 'YOUR_PROJECT_ID',
  // apiUrl: 'https://api.usegately.com' // optional override
})
```

<Note>
  `GatelyContacts` works in both browser and Node.js environments. It automatically selects the right internal client for each platform.
</Note>

***

## Contacts

### `list(options?)`

List contacts with optional filtering and pagination.

```typescript theme={null}
const { contacts, pagination } = await crm.list({
  page: 1,
  limit: 50,
  status: 'active',
  search: 'jane',
})

console.log(`${pagination.total} contacts found`)
```

| Option       | Type            | Description                            |
| ------------ | --------------- | -------------------------------------- |
| `page`       | `number`        | Page number (default `1`)              |
| `limit`      | `number`        | Results per page (default `50`)        |
| `search`     | `string`        | Search by name or email                |
| `status`     | `ContactStatus` | Filter by status                       |
| `tags`       | `string`        | Comma-separated tag names to filter by |
| `segment_id` | `string`        | Filter to contacts in a segment        |

***

### `get(contactId)`

Fetch a single contact by ID.

```typescript theme={null}
const contact = await crm.get('con_abc123')
console.log(contact.email) // 'jane@example.com'
```

***

### `create(data)`

Create a new contact.

```typescript theme={null}
const contact = await crm.create({
  name: 'Jane Smith',
  email: 'jane@example.com',
  company: 'Acme Corp',
  status: 'active',
  tags: ['VIP', 'Newsletter'],
  custom_fields: { plan: 'pro', source: 'webinar' },
})
```

***

### `update(contactId, data)`

Partially update a contact. Only the fields you pass are changed.

```typescript theme={null}
await crm.update('con_abc123', {
  status: 'inactive',
  notes: 'Churned after trial',
})
```

***

### `delete(contactId)`

Permanently delete a contact.

```typescript theme={null}
await crm.delete('con_abc123')
```

***

### `bulkImport(contacts, options?)`

Import up to 1,000 contacts in a single call. Duplicate emails are skipped by default.

```typescript theme={null}
const result = await crm.bulkImport([
  { name: 'Alice', email: 'alice@example.com' },
  { name: 'Bob',   email: 'bob@example.com', company: 'Acme' },
])

console.log(`Imported: ${result.imported}, Skipped: ${result.skipped}`)

if (result.errors.length) {
  result.errors.forEach(e => console.warn(`Row ${e.row}: ${e.error}`))
}
```

***

## Contact Sub-resources

### `getContactLists(contactId)`

Get all lists and segments a contact belongs to.

```typescript theme={null}
const { lists, segments } = await crm.getContactLists('con_abc123')
```

***

### `getEmailActivity(contactId)`

Get the email send/open/click history for a contact.

```typescript theme={null}
const activity = await crm.getEmailActivity('con_abc123')

activity.forEach(event => {
  console.log(`${event.type} — ${event.occurred_at}`)
})
```

Activity types: `sent` · `delivered` · `opened` · `clicked` · `bounced` · `complained` · `unsubscribed`

***

### `getCustomFields(contactId)`

Retrieve the custom fields object for a contact.

```typescript theme={null}
const fields = await crm.getCustomFields('con_abc123')
console.log(fields.plan) // 'pro'
```

***

### `updateCustomFields(contactId, fields)`

Merge-update custom fields (PATCH — existing keys are preserved).

```typescript theme={null}
await crm.updateCustomFields('con_abc123', {
  plan: 'enterprise',
  onboarded: true,
})
```

***

### `unsubscribeContact(contactId, data?)`

Unsubscribe a contact and record the reason.

```typescript theme={null}
await crm.unsubscribeContact('con_abc123', {
  reason: 'Too many emails',
  source: 'user_request',
})
```

***

### `resubscribeContact(contactId)`

Remove the unsubscribe record and re-enable email delivery.

```typescript theme={null}
await crm.resubscribeContact('con_abc123')
```

***

### `getUnsubscribes(contactId)`

Get all unsubscribe records for a contact.

```typescript theme={null}
const records = await crm.getUnsubscribes('con_abc123')
```

***

## Tags

### `listTags()`

List all tags for the project.

```typescript theme={null}
const tags = await crm.listTags()
```

***

### `createTag(data)`

Create a new tag.

```typescript theme={null}
const tag = await crm.createTag({ name: 'VIP', color: '#ff6600' })
```

***

### `updateTag(tagId, data)`

Update a tag's name or colour.

```typescript theme={null}
await crm.updateTag('tag_abc', { color: '#0066ff' })
```

***

### `deleteTag(tagId)`

Delete a tag. It is automatically removed from all contacts.

```typescript theme={null}
await crm.deleteTag('tag_abc')
```

***

### `addTagToContact(contactId, tagId)`

Attach a tag to a contact.

```typescript theme={null}
await crm.addTagToContact('con_abc123', 'tag_abc')
```

***

### `removeTagFromContact(contactId, tagId)`

Remove a tag from a contact.

```typescript theme={null}
await crm.removeTagFromContact('con_abc123', 'tag_abc')
```

***

## Contact Lists

### `listContactLists()`

List all contact lists for the project.

```typescript theme={null}
const lists = await crm.listContactLists()
```

***

### `getContactList(listId)`

Get a single list by ID.

```typescript theme={null}
const list = await crm.getContactList('list_abc')
```

***

### `createContactList(data)`

Create a new list.

```typescript theme={null}
const list = await crm.createContactList({
  name: 'Newsletter Subscribers',
  description: 'Monthly newsletter opt-ins',
})
```

***

### `updateContactList(listId, data)`

Update a list's name or description.

```typescript theme={null}
await crm.updateContactList('list_abc', { name: 'Weekly Newsletter' })
```

***

### `deleteContactList(listId)`

Delete a list. Contacts are not deleted.

```typescript theme={null}
await crm.deleteContactList('list_abc')
```

***

### `getListMembers(listId, options?)`

Get all contacts in a list.

```typescript theme={null}
const members = await crm.getListMembers('list_abc', { page: 1, limit: 100 })
```

***

### `addContactToList(listId, contactId)`

Add a contact to a list.

```typescript theme={null}
await crm.addContactToList('list_abc', 'con_abc123')
```

***

### `removeContactFromList(listId, contactId)`

Remove a contact from a list.

```typescript theme={null}
await crm.removeContactFromList('list_abc', 'con_abc123')
```

***

## Segments

Segments are dynamic — they automatically include contacts that match a set of filter rules.

### `listSegments()`

```typescript theme={null}
const segments = await crm.listSegments()
```

***

### `getSegment(segmentId)`

```typescript theme={null}
const segment = await crm.getSegment('seg_abc')
```

***

### `createSegment(data)`

Create a segment with filter rules.

```typescript theme={null}
const segment = await crm.createSegment({
  name: 'Pro Plan Users',
  rules: [
    { field: 'custom_fields.plan', operator: 'eq', value: 'pro' },
    { field: 'status',             operator: 'eq', value: 'active' },
  ],
})
```

**Supported operators:**

| Operator       | Description                   |
| -------------- | ----------------------------- |
| `eq`           | Equals                        |
| `neq`          | Not equals                    |
| `contains`     | String contains               |
| `not_contains` | String does not contain       |
| `gt` / `lt`    | Greater / less than           |
| `gte` / `lte`  | Greater / less than or equal  |
| `exists`       | Field is present and non-null |
| `empty`        | Field is null or missing      |

***

### `updateSegment(segmentId, data)`

```typescript theme={null}
await crm.updateSegment('seg_abc', {
  name: 'Enterprise Users',
  rules: [{ field: 'custom_fields.plan', operator: 'eq', value: 'enterprise' }],
})
```

***

### `deleteSegment(segmentId)`

```typescript theme={null}
await crm.deleteSegment('seg_abc')
```

***

## TypeScript Types

All types are exported from `@gately/sdk`:

```typescript theme={null}
import type {
  ContactsConfig,
  Contact,
  ContactStatus,
  CreateContactRequest,
  UpdateContactRequest,
  ContactListOptions,
  ContactsResult,
  ContactTag,
  ContactList,
  ContactListMember,
  ContactSegment,
  SegmentRule,
  BulkImportResult,
  EmailActivity,
  UnsubscribeRecord,
} from '@gately/sdk'
```

### `ContactStatus`

```typescript theme={null}
type ContactStatus = 'new' | 'active' | 'inactive' | 'archived'
```

### `Contact`

```typescript theme={null}
interface Contact {
  id: string
  project_id: string
  name: string
  email: string
  phone?: string | null
  company?: string | null
  status: ContactStatus
  notes?: string | null
  custom_fields?: Record<string, any>
  unsubscribed?: boolean
  tags?: ContactTag[]
  last_contacted_at?: string | null
  created_at: string
  updated_at?: string
}
```

### `SegmentRule`

```typescript theme={null}
interface SegmentRule {
  field: string
  operator: 'eq' | 'neq' | 'contains' | 'not_contains' | 'gt' | 'lt' | 'gte' | 'lte' | 'exists' | 'empty'
  value?: string | number | boolean
}
```

***

## Complete Example

```typescript theme={null}
import { GatelyContacts } from '@gately/sdk'

const crm = new GatelyContacts({ projectId: 'YOUR_PROJECT_ID' })

async function onboardUser(email: string, name: string, plan: string) {
  // 1. Create the contact
  const contact = await crm.create({
    name,
    email,
    status: 'active',
    custom_fields: { plan, onboarded_at: new Date().toISOString() },
  })

  // 2. Add to the right list
  const lists = await crm.listContactLists()
  const onboardingList = lists.find(l => l.name === 'Onboarding')
  if (onboardingList) {
    await crm.addContactToList(onboardingList.id, contact.id)
  }

  // 3. Tag as VIP if on pro plan
  if (plan === 'pro') {
    const tags = await crm.listTags()
    const vipTag = tags.find(t => t.name === 'VIP')
    if (vipTag) {
      await crm.addTagToContact(contact.id, vipTag.id)
    }
  }

  return contact
}
```

***

## Related

<CardGroup cols={2}>
  <Card title="List Contacts API" icon="address-book" href="/docs/api-reference/contacts/list">
    REST endpoint reference
  </Card>

  <Card title="Create Contact API" icon="user-plus" href="/docs/api-reference/contacts/create">
    Create endpoint reference
  </Card>

  <Card title="Tags API" icon="tag" href="/docs/api-reference/contacts/tags">
    Tags endpoint reference
  </Card>

  <Card title="Segments API" icon="filter" href="/docs/api-reference/contacts/segments">
    Segments endpoint reference
  </Card>
</CardGroup>
