# Contacts (/docs/api/contacts)



Contacts are the core object in the CRM. Each contact belongs to a workspace and stores profile information alongside custom property values defined for that workspace.

## Custom Properties [#custom-properties]

Custom property values are stored under the property's key (e.g. `custom.lead_status`). See [Custom Properties](/docs/api/custom-properties) for how to define and manage these fields.

## Endpoints [#endpoints]

<Cards>
  <Card href="/docs/api/contacts/contacts.list" title="List contacts">
    Retrieve a paginated list of contacts in a workspace
  </Card>

  <Card href="/docs/api/contacts/contacts.get" title="Get contact">
    Retrieve a single contact by ID
  </Card>

  <Card href="/docs/api/contacts/contacts.create" title="Create contact">
    Create a new contact in a workspace
  </Card>

  <Card href="/docs/api/contacts/contacts.patch" title="Update contact">
    Partially update an existing contact
  </Card>

  <Card href="/docs/api/contacts/contacts.delete" title="Delete contact">
    Delete a contact by ID
  </Card>
</Cards>

## Custom Properties Enrichment [#custom-properties-enrichment]

[Custom properties](/docs/api/custom-properties) store raw values (option keys, user IDs) on the contact. Contact responses include a `_meta` field that resolves these to human-readable labels and names — saving you from having to cross-reference option lists or look up workspace members separately.

```json title="Contact response"
{
  "id": "abc123",
  "fullName": "Jane Smith",
  "custom": {
    "status": "active",
    "assignee": "usr_xyz"
  },
  "_meta": {
    "properties": {
      "custom.status": {
        "type": "single-select",
        "name": "Status",
        "selected": { "label": "Active", "value": "active" }
      },
      "custom.assignee": {
        "type": "user-select",
        "name": "Assignee",
        "selected": {
          "label": "Alice Smith",
          "value": "usr_xyz",
          "avatarUrl": "https://..."
        }
      }
    }
  }
}
```

`_meta.properties` is a map keyed by the property's dot-path key (e.g. `"custom.status"`). Each entry includes the property `name` as configured in your workspace, the `type`, and type-specific resolved data.

### `single-select` [#single-select]

```json
{
  "type": "single-select",
  "name": "Status",
  "selected": { "label": "Active", "value": "active" }
}
```

`selected` is `null` if the stored value no longer matches any configured option — for example, when an option has been deleted from the workspace after the contact was saved.

### `multi-select` [#multi-select]

```json
{
  "type": "multi-select",
  "name": "Tags",
  "selected": [
    { "label": "VIP", "value": "vip" },
    { "label": "Enterprise", "value": "enterprise" }
  ]
}
```

Only values that match a currently configured option appear in `selected`. Deleted options are silently dropped — if a contact had `["vip", "lead"]` and the `"lead"` option was removed, `selected` will contain only `[{ "label": "VIP", "value": "vip" }]`.

### `user-select` [#user-select]

```json
{
  "type": "user-select",
  "name": "Assignee",
  "selected": {
    "label": "Alice Smith",
    "value": "usr_xyz",
    "avatarUrl": "https://..."
  }
}
```

`selected` is `null` if the stored user ID is no longer a member of the workspace — for example, when a user has been removed after being assigned to a contact. `avatarUrl` is omitted when the user has no avatar.

### Text-like types [#text-like-types]

For `text`, `textarea`, `url`, `email`, `tel`, and `amount` properties, the entry contains only the name and type — the raw value is already on the contact object itself.

```json
{
  "type": "email",
  "name": "Email"
}
```

### Omitted Properties [#omitted-properties]

A property is omitted from `_meta.properties` entirely when its value is empty (`null`, `undefined`, `""`, or `[]`). Only properties with a non-empty value appear.
