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

# Overview

> API specification for endpoints you must implement in your CRM

<Warning>
  **Important**: This section describes the API endpoints that **your system must implement** to integrate
  with Enginy. These are not Enginy API endpoints — they are the interface specification that Enginy will call
  on your CRM.
</Warning>

## Introduction

To integrate your custom CRM with Enginy, your system must expose a set of HTTP endpoints that Enginy will call. This documentation covers the exact specification your API must follow.

<Info>
  All endpoints must be accessible via HTTPS and accept the API key header you configured during connection
  setup.
</Info>

***

## Required Endpoints

Your Custom CRM integration supports three main use cases:

<AccordionGroup>
  <Accordion title="Data Export & Sync" icon="database" defaultOpen>
    Export contacts and companies from Enginy to your CRM, and sync data bidirectionally.

    | Endpoint          | Method | Purpose                                                |
    | ----------------- | ------ | ------------------------------------------------------ |
    | `/health`         | GET    | Connection validation                                  |
    | `/contacts`       | POST   | Batch create contacts                                  |
    | `/contacts/sync`  | POST   | Find existing contacts, return CRM IDs and properties  |
    | `/contacts`       | PUT    | Update contacts (including engagement fields)          |
    | `/companies`      | POST   | Batch create companies                                 |
    | `/companies/sync` | POST   | Find existing companies, return CRM IDs and properties |
    | `/companies`      | PUT    | Update companies (including engagement fields)         |
    | `/associations`   | POST   | Link contacts to companies                             |
  </Accordion>

  <Accordion title="Campaign Activity Logging" icon="message-dots">
    Log all conversation activity (messages, emails, connection requests) from campaigns.

    | Endpoint      | Method | Purpose                                                |
    | ------------- | ------ | ------------------------------------------------------ |
    | `/activities` | POST   | Create activity record (LinkedIn message, email, etc.) |

    **Activity Types:**

    * `EMAIL` - Email messages sent/received
    * `LINKEDIN` - LinkedIn messages sent/received
    * `LINKEDIN_INMAIL` - LinkedIn InMail sent/received
    * `LINKEDIN_CONNECTION` - Connection requests sent/accepted
  </Accordion>

  <Accordion title="Task Management" icon="list-check">
    Create and manage tasks triggered by campaign sequences.

    | Endpoint       | Method | Purpose                 |
    | -------------- | ------ | ----------------------- |
    | `/tasks`       | POST   | Create a task           |
    | `/tasks/:id`   | GET    | Get a task              |
    | `/tasks/:id`   | PATCH  | Update/complete a task  |
    | `/tasks/batch` | POST   | Get multiple tasks      |
    | `/tasks/batch` | PATCH  | Complete multiple tasks |
  </Accordion>

  <Accordion title="Users/Owners (Optional)" icon="users">
    Provide a list of users for owner assignment in exports, tasks, and activities.

    | Endpoint | Method | Purpose                          |
    | -------- | ------ | -------------------------------- |
    | `/users` | GET    | List users/owners for assignment |

    <Info>
      **Optional**: If not implemented (returns 404/501/405), owner selection is disabled in the UI.
    </Info>
  </Accordion>
</AccordionGroup>

***

## Quick Reference Cards

<CardGroup cols={2}>
  <Card title="Contacts" icon="user" href="/api-reference/custom-crm/contacts">
    Create, sync, and update contacts
  </Card>

  <Card title="Companies" icon="building" href="/api-reference/custom-crm/companies">
    Create, sync, and update companies
  </Card>

  <Card title="Associations" icon="link" href="/api-reference/custom-crm/associations">
    Link contacts to companies
  </Card>

  <Card title="Activities" icon="message-dots" href="/api-reference/custom-crm/activities">
    Log LinkedIn and email activity
  </Card>

  <Card title="Tasks" icon="list-check" href="/api-reference/custom-crm/tasks">
    Create and manage campaign tasks
  </Card>

  <Card title="Users" icon="users" href="/api-reference/custom-crm/endpoints/users/get">
    List users for owner assignment (optional)
  </Card>

  <Card title="Examples" icon="code" href="/api-reference/custom-crm/examples">
    Complete implementation examples
  </Card>

  <Card title="Activities API" icon="message-dots" href="/api-reference/custom-crm/activities">
    Log LinkedIn and email activities
  </Card>
</CardGroup>

***

## Authentication

Your API must validate the API key sent in the header you specified during setup (default: `X-API-Key`).

```bash theme={null}
# Enginy will send requests like this:
POST /contacts HTTP/1.1
Host: your-api.com
X-API-Key: your-configured-secret
Content-Type: application/json
Accept: application/json
```

<Tip>Return `401 Unauthorized` if the API key is invalid or missing.</Tip>

***

## Health Endpoint

<Card title="Your Implementation Required" icon="triangle-exclamation" color="#f59e0b">
  This endpoint must be implemented on **your server**
</Card>

Enginy calls this endpoint to validate the connection during setup and periodic health checks.

```http theme={null}
GET /health
```

Return any `2xx` status code to indicate your API is healthy:

```json theme={null}
{
  "status": "ok"
}
```

***

## Complete Endpoint Reference

### Data Export & Sync

| Endpoint          | Method | Purpose                        | Details                                                                |
| ----------------- | ------ | ------------------------------ | ---------------------------------------------------------------------- |
| `/health`         | GET    | Connection validation          | Return 200 if healthy                                                  |
| `/contacts`       | POST   | Batch create contacts          | [View docs](/api-reference/custom-crm/contacts)                        |
| `/contacts/sync`  | POST   | Sync contacts (find existing)  | [View docs](/api-reference/custom-crm/contacts-sync)                   |
| `/contacts`       | PUT    | Update contacts                | [View docs](/api-reference/custom-crm/contacts-sync#update-contacts)   |
| `/companies`      | POST   | Batch create companies         | [View docs](/api-reference/custom-crm/companies)                       |
| `/companies/sync` | POST   | Sync companies (find existing) | [View docs](/api-reference/custom-crm/companies-sync)                  |
| `/companies`      | PUT    | Update companies               | [View docs](/api-reference/custom-crm/companies-sync#update-companies) |
| `/associations`   | POST   | Link contacts to companies     | [View docs](/api-reference/custom-crm/associations)                    |

### Campaign Activity

| Endpoint      | Method | Purpose                | Details                                           |
| ------------- | ------ | ---------------------- | ------------------------------------------------- |
| `/activities` | POST   | Create activity record | [View docs](/api-reference/custom-crm/activities) |

### Tasks

| Endpoint       | Method | Purpose                 | Details                                                           |
| -------------- | ------ | ----------------------- | ----------------------------------------------------------------- |
| `/tasks`       | POST   | Create a task           | [View docs](/api-reference/custom-crm/tasks#create-task)          |
| `/tasks/:id`   | GET    | Get a task              | [View docs](/api-reference/custom-crm/tasks#get-task)             |
| `/tasks/:id`   | PATCH  | Update a task           | [View docs](/api-reference/custom-crm/tasks#update-task-complete) |
| `/tasks/batch` | POST   | Get tasks in batch      | [View docs](/api-reference/custom-crm/tasks#get-tasks-batch)      |
| `/tasks/batch` | PATCH  | Complete tasks in batch | [View docs](/api-reference/custom-crm/tasks#complete-tasks-batch) |

### Users (Optional)

| Endpoint | Method | Purpose           | Details                                                    |
| -------- | ------ | ----------------- | ---------------------------------------------------------- |
| `/users` | GET    | List users/owners | [View docs](/api-reference/custom-crm/endpoints/users/get) |

<Info>
  The Users endpoint is optional. If not implemented, return `404`, `501`, or `405` to gracefully disable
  owner selection in the UI.
</Info>

***

## Data Flows

### Export Flow

When exporting data from Enginy to your CRM, **sync endpoints are always called first** to identify existing records. Based on the sync results, Enginy will either **create new records** or **update existing ones**:

* **New records**: If a contact/company is not found during sync (no `crmId` returned), it will be created via `POST /contacts` or `POST /companies`
* **Existing records**: If a contact/company already exists in your CRM (a `crmId` is returned from sync), it will be updated via `PUT /contacts` or `PUT /companies`

```mermaid theme={null}
sequenceDiagram
    participant G as Enginy
    participant C as Your CRM

    Note over G,C: Step 1: Sync Contacts (find existing)
    G->>C: POST /contacts/sync
    C-->>G: { results: [{ externalId, crmId }] }

    Note over G,C: Step 2: Sync Companies (find existing)
    G->>C: POST /companies/sync
    C-->>G: { results: [{ externalId, crmId }] }

    Note over G,C: Step 3: Update EXISTING Companies
    G->>C: PUT /companies (matched in sync)
    C-->>G: { results: [{ crmId, success }] }

    Note over G,C: Step 4: Create NEW Companies
    G->>C: POST /companies (not found in sync)
    C-->>G: { results: [{ externalId, crmId }] }

    Note over G,C: Step 5: Update EXISTING Contacts
    G->>C: PUT /contacts (matched in sync)
    C-->>G: { results: [{ crmId, success }] }

    Note over G,C: Step 6: Create NEW Contacts
    G->>C: POST /contacts (not found in sync)
    C-->>G: { results: [{ externalId, crmId }] }

    Note over G,C: Step 7: Create Associations
    G->>C: POST /associations
    C-->>G: { success: true }
```

<Warning>
  **Important**: The `/contacts/sync` and `/companies/sync` endpoints are **required**. Enginy always calls
  them first to determine which records already exist in your CRM, then routes each record to either an update
  (PUT) or create (POST) operation accordingly.
</Warning>

### Sync Endpoint Behavior

The sync endpoints (`POST /contacts/sync` and `POST /companies/sync`) allow Enginy to:

1. **Find existing records** - Your CRM matches by email, LinkedIn URL, domain, etc.
2. **Return CRM IDs** - So Enginy can link records for future updates
3. **Sync properties back** - Optionally return data to update in Enginy

```mermaid theme={null}
sequenceDiagram
    participant G as Enginy
    participant C as Your CRM

    G->>C: POST /contacts/sync (all contacts)
    Note over C: Match by email, LinkedIn URL, etc.
    C-->>G: { results: [{ externalId, crmId, properties }] }

    Note over G: Enginy saves crmIds and syncs properties back
```

<Tip>
  **Key Principle**: All matching logic lives in YOUR CRM. You decide how to identify existing records and
  what properties to sync back.
</Tip>

<Info>
  **Minimal Implementation**: If you don't need matching logic, your sync endpoints can return an empty array `{ results: [] }`. This will treat all records as new and proceed to create them.
</Info>

### Campaign Activity Flow

When a campaign sends messages or receives replies:

```mermaid theme={null}
sequenceDiagram
    participant G as Enginy
    participant C as Your CRM

    Note over G,C: Message sent from campaign
    G->>C: POST /activities (type: LINKEDIN, direction: OUTBOUND)
    C-->>G: { id: "activity-123" }

    Note over G,C: Contact replies
    G->>C: POST /activities (type: LINKEDIN, direction: INBOUND)
    C-->>G: { id: "activity-456" }

    Note over G,C: Update engagement fields
    G->>C: PUT /contacts (engagement status, last activity, etc.)
    C-->>G: { results: [{ crmId, success }] }
```

***

## Engagement Field Updates

When contacts engage with campaigns (reply, click, connect), Enginy updates your CRM via `PUT /contacts` with fields like:

| Field                      | Example Value                           | Description                                     |
| -------------------------- | --------------------------------------- | ----------------------------------------------- |
| `campaignEngagementStatus` | `Message Replied (2/3) - LINKEDIN`      | Current engagement status                       |
| `campaignSequenceStatus`   | `Ongoing`                               | `Not Started`, `Ongoing`, `Finished`, `Replied` |
| `campaignOpens`            | `5`                                     | Number of email opens                           |
| `campaignClicks`           | `2`                                     | Number of link clicks                           |
| `activities`               | `Connection Request Sent, Message Sent` | Activity log                                    |

<Info>
  Field names are user-configurable in Enginy. Your CRM should accept **any** field name and store it
  appropriately.
</Info>

<Info>
  Engagement field updates are sent through `PUT /contacts` and `PUT /companies` using the mappings you
  configure in Enginy (e.g., last engaged date).
</Info>

***

## CRM Record Links (Optional)

Enginy can generate clickable links to open records in your CRM:

| Configuration         | Example                                 |
| --------------------- | --------------------------------------- |
| `contactLinkTemplate` | `https://mycrm.com/contacts/{{crmId}}`  |
| `companyLinkTemplate` | `https://mycrm.com/companies/{{crmId}}` |

***

## Error Handling

Return appropriate HTTP status codes:

| Status Code   | When to Use        |
| ------------- | ------------------ |
| `200` / `201` | Success            |
| `400`         | Invalid request    |
| `401`         | Invalid API key    |
| `404`         | Resource not found |
| `500`         | Server error       |

***

## Reference Implementation

<Card title="GitHub Repository" icon="github" href="https://github.com/Genesy-AI/custom-crm-example">
  Clone our complete working example: Node.js/Express server with SQLite, including all endpoints and a web
  dashboard for viewing data.
</Card>
