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

# Contacts

> Manage CRM contacts – attendees, leads, and customers

Contacts are the people who interact with your scheduling pages. meetergo automatically creates a contact record each time someone books a meeting, and you can also create and manage contacts via the API.

## How Contacts Are Created

| Source | Description |
| - | - |
| **Automatic (booking)** | A contact is created or updated whenever someone books a meeting. The contact is matched by email address — existing contacts are enriched, new ones are created. |
| **Manual (API)** | Create contacts directly via `POST /crm` for leads or customers who haven't booked yet. |
| **Import** | Bulk-create contacts via `POST /crm/bulk` (e.g. from a CSV import). |

## Contact Fields

| Field | Type | Description |
| - | - | - |
| `id` | uuid | Unique contact identifier |
| `firstName` | string | First name |
| `lastName` | string | Last name |
| `email` | string | Email address (unique per company) |
| `phoneNumber` | string | Phone number |
| `tags` | string\[] | Labels like `Lead`, `Enterprise`, `Recurring` |
| `notes` | string | Internal notes |
| `crmCompanyId` | uuid | Associated CRM company |
| `accountOwnerId` | uuid | Responsible meetergo user |
| `additionalData` | object | Custom data field values, keyed by `DataField.name` |
| `noShowCount` | number | Number of times marked as no-show |

### Automatic Tags

meetergo adds these tags automatically:

| Tag | When applied |
| - | - |
| `Recurring` | Contact has 2+ bookings |
| `Optin` | Contact completed double opt-in |

## Create a Contact

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.meetergo.com/crm" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "firstName": "Anna",
      "lastName": "Müller",
      "email": "anna.mueller@example.com",
      "tags": ["Lead"],
      "additionalData": { "CustomerNo": "589" },
      "address": {
        "line1": "Musterweg 1",
        "city": "Muttenz",
        "zip": "4132",
        "country": "DE"
      }
    }'
  ```

  <Warning>
    `address` is a nested object. Sending `line1`, `city`, `zip` or `country` at the
    top level returns `400 property line1 should not exist` — the route rejects
    properties it does not declare rather than dropping them silently.
  </Warning>

  <Tip>
    `additionalData` is how you carry your own record id onto a contact, so the two
    systems stay reconcilable. The keys are `DataField.name` values — list them with
    `GET /data-field`. Setting a key to `null` on `PATCH /crm/{id}` removes it.
  </Tip>

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.meetergo.com/crm', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      firstName: 'Anna',
      lastName: 'Müller',
      email: 'anna.mueller@example.com',
      tags: ['Lead']
    })
  });

  const contact = await response.json();
  console.log(contact.item.id); // contact UUID
  ```
</CodeGroup>

<Note>
  Either `email` or `phoneNumber` must be provided. Both can be supplied.
</Note>

### Response

```json theme={null}
{
  "item": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "firstName": "Anna",
    "lastName": "Müller",
    "email": "anna.mueller@example.com",
    "phoneNumber": null,
    "tags": ["Lead"],
    "notes": null,
    "noShowCount": 0,
    "crmCompanyId": null,
    "accountOwnerId": null,
    "additionalData": {},
    "createdAt": "2024-06-01T10:00:00Z",
    "updatedAt": "2024-06-01T10:00:00Z"
  },
  "appointments": [],
  "nextAppointmentIso": null
}
```

## Search Contacts

```bash theme={null}
curl "https://api.meetergo.com/crm?searchTerm=anna&tags=Lead&page=1&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Query Parameters

| Parameter | Type | Description |
| - | - | - |
| `searchTerm` | string | Search by name, email, or phone |
| `tags` | string\[] | Filter by one or more tags |
| `ownerId` | uuid | Filter by account owner |
| `sortBy` | string | `firstName`, `lastName`, `email`, or `createdAt` |
| `sortOrder` | string | `ASC` or `DESC` |
| `page` | number | Page number (default: 1) |
| `limit` | number | Results per page (default: 10, max: 1000) |

### Response

```json theme={null}
{
  "result": [...],
  "page": 1,
  "limit": 20,
  "total": 42,
  "totalPages": 3
}
```

## Get Contact Details

Look up a contact by their ID or by an attendee ID from a booking:

```bash theme={null}
# By contact ID
curl "https://api.meetergo.com/crm/details?contactId=550e8400-..." \
  -H "Authorization: Bearer YOUR_API_KEY"

# By attendee ID (from a booking webhook)
curl "https://api.meetergo.com/crm/details?attendeeId=660e8400-..." \
  -H "Authorization: Bearer YOUR_API_KEY"
```

The response includes the full contact record, all linked appointments, and form submission history.

## Update a Contact

```bash theme={null}
curl -X PATCH "https://api.meetergo.com/crm/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tags": ["Lead", "Qualified"],
    "notes": "Interested in Enterprise plan"
  }'
```

## Delete a Contact

```bash theme={null}
curl -X DELETE "https://api.meetergo.com/crm/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

## Contacts and Webhooks

When a booking is created, you can look up the contact immediately using the `attendeeId` from the webhook payload:

```javascript theme={null}
app.post('/webhooks/meetergo', async (req, res) => {
  res.status(200).send('OK');

  const payload = req.body;

  if (payload.webhookType === 'booking_created') {
    const attendeeId = payload.attendees[0]?.id;

    // Fetch full contact record including form answers
    const response = await fetch(
      `https://api.meetergo.com/crm/details?attendeeId=${attendeeId}`,
      { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
    );

    const { item: contact } = await response.json();
    console.log('Contact:', contact.email, contact.additionalData);
  }
});
```

## Best Practices

<Check>
  **Use `attendeeId` lookups** – When processing booking webhooks, look up the contact via `attendeeId` to get enriched data including all form field answers.
</Check>

<Check>
  **Check `additionalData`** – Custom data field values are stored in `additionalData` on the contact, keyed by the field's `name`. Booking form answers land here automatically; you can also write your own with `POST /crm`, `PATCH /crm/{id}` or `POST /crm/bulk`. List the available keys with `GET /data-field`, and note that `searchTerm` does not search inside `additionalData`.
</Check>

<Check>
  **Use tags for segmentation** – Tags are free-form strings; define a consistent set (e.g., `Lead`, `Customer`, `Enterprise`) to filter contacts efficiently.
</Check>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.