---
title: CRM Lookup
excerpt: Search and create contacts, log phone calls, record call contexts, and push chat sessions into your CRM — the core PBX integration interface.
order: 4
---

All CRM Lookup endpoints are **workspace-scoped** and require both the `client` and `crm` OAuth scopes. These endpoints are called by the CRM Middleware during PBX events (incoming calls, outgoing calls, chat sessions).

**Headers:** `Authorization: Bearer {workspace-token}`

The customer context is resolved automatically from the OAuth token.

---

## GET /api/crm/lookup/contacts/search

Searches for contacts in the connected CRM by phone number or email address. Typically called during a screen-pop when a call rings on the PBX.

**Query parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `phone` | string | yes* | Phone number to search for |
| `email` | string | yes* | Email address to search for |
| `display_name` | string | no | Caller display name — used by URL-builder integrations to construct the contact URL |

*At least one of `phone` or `email` is required.

**Response `200`**

```json
{
  "contacts": [
    {
      "id": "crm-contact-uuid",
      "first_name": "Alice",
      "last_name": "Martin",
      "email": "alice.martin@acme.com",
      "phone_business_1": "+33142000000",
      "phone_mobile_1": "+33612345678",
      "company": "Acme Corp",
      "entity_url": "https://crm.example.com/contacts/123"
    }
  ]
}
```

> If no match is found in the CRM, a dummy contact placeholder is returned so that 3CX can still trigger the call creation step.

---

## POST /api/crm/lookup/contacts

Creates a new contact in the connected CRM.

**Request body**

| Field | Type | Required | Description |
|---|---|---|---|
| `last_name` | string | yes | Last name |
| `first_name` | string | no | First name |
| `civility` | string | no | Civility / salutation |
| `email` | string | no | Email address |
| `phone` | string | no | Generic phone number |
| `phone_business` | string | no | Business phone number |
| `phone_mobile` | string | no | Mobile phone number |
| `custom_value` | string | no | Custom CRM field value |
| `call_direction` | string | no | Direction of the triggering call (`inbound` or `outbound`) |

**Response `201`**

```json
{
  "response": "ok",
  "contact": { ... }
}
```

**Response `400`** — the CRM connector could not create the contact.

---

## GET /api/crm/lookup/phone-calls

Returns the list of phone calls from the connected CRM connector.

**Query parameters**

| Parameter | Description |
|---|---|
| `filter[direction]` | `inbound`, `outbound`, or `internal` |
| `filter[status]` | Call status |
| `filter[answered]` | Boolean — whether the call was answered |
| `filter[number_from]` | Caller number |
| `filter[number_to]` | Callee number |
| `filter[number_did]` | DID / ring group number |
| `filter[start_time]` | Call start time |
| `filter[end_time]` | Call end time |
| `filter[agent_email]` | Agent email address |
| `filter[subject]` | Call subject |
| `include` | Comma-separated relationships to sideload: `segments`, `logs` |
| `sort` | Sort field: `start_time`, `end_time`, `created_at`, `updated_at`. Prefix with `-` for descending |
| `per_page` | Results per page (default: 20) |

**Response `200`** — paginated list of call objects.

---

## POST /api/crm/lookup/phone-calls

Records a completed phone call in the connected CRM. This is the primary endpoint called by the CRM Middleware when a call ends on the PBX.

**Request body**

| Field | Type | Required | Description |
|---|---|---|---|
| `direction` | string | yes | `inbound`, `outbound`, or `internal` (case-insensitive) |
| `status` | string | yes | `completed`, `failed`, `busy`, `no-answer`, `Missed`, or `Notanswered` |
| `number_from` | string | yes* | Caller number |
| `number_to` | string | yes* | Callee number |
| `number_external` | string | yes** | External party number |
| `number_internal` | string | yes** | Internal extension number |
| `source` | string | no | PBX source identifier (e.g. `3cx`) |
| `answered` | boolean | no | Whether the call was answered |
| `duration` | string/integer | no | Call duration |
| `start_time_utc` | string | no | Call start time in UTC — format: `YYYY-MM-DDTHH:MM:SSZ` |
| `end_time_utc` | string | no | Call end time in UTC — format: `YYYY-MM-DDTHH:MM:SSZ` |
| `subject` | string | no | Call subject |
| `body` | string | no | Call notes or transcript excerpt |
| `recording_url` | string | no | URL to the call recording |
| `agent_first_name` | string | no | Answering agent's first name |
| `agent_last_name` | string | no | Answering agent's last name |
| `agent_email` | string | no | Answering agent's email address |
| `queue_extension` | string | no | Queue or ring group extension |

*`number_from` and `number_to` are mutually required — provide both or neither.  
**`number_external` and `number_internal` are mutually required — provide both or neither.

**Response `201`**

```json
{
  "response": "success",
  "message": "Call saved successfully.",
  "call": { ... }
}
```

**Response `400`** — phone call logging is not enabled for this account.

---

## POST /api/crm/lookup/phone-calls/context

Creates or updates a call context record during a live call. The context is used to pre-populate the CRM record so that when the call ends, the full entry can be enriched without relying solely on the final call payload.

**Request body**

| Field | Type | Required | Description |
|---|---|---|---|
| `direction` | string | yes | `inbound` or `outbound` |
| `number_from` | string | yes (if inbound) | Caller number |
| `number_to` | string | yes (if outbound) | Callee number |
| `source` | string | no | PBX source identifier (e.g. `3cx`) |
| `start_time_utc` | string | no | Call start time in UTC — format: `YYYY-MM-DDTHH:MM:SSZ` |
| `start_time_reference` | string | no | Which timestamp to use as the context anchor: `call` (default) or `segment` |
| `segment_dn_type` | string | no | DN type: `extension`, `trunk`, `call_queue`, `voicemail`, `ivr`, `external_number`, `call_flow` |
| `context_data` | array | no | Arbitrary key-value pairs to store alongside the context |

**Response `201`**

```json
{
  "response": "success",
  "message": "Call context saved successfully.",
  "call_context_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

### Token-based variant

For scenarios where the CRM Middleware cannot use a standard OAuth token, a pre-issued context token can be used instead:

```
POST /api/crm/phone-calls-context/{token}/{tokenSecret}
```

This endpoint accepts the same request body and requires no `Authorization` header. The `token` (UUID) and `tokenSecret` are issued by CX-Engine and scoped to a specific customer.

---

## POST /api/crm/lookup/chats

Records a completed chat session in the connected CRM. Called by the CRM Middleware when a chat session ends on the PBX.

**Request body**

| Field | Type | Required | Description |
|---|---|---|---|
| `email` | string | yes | Customer's email address |
| `source` | string | no | PBX source identifier (e.g. `3cx`) |
| `number_external` | string | no | Customer's external number or identifier |
| `name` | string | no | Customer's display name |
| `start_time_utc` | string | no | Chat start time in UTC — format: `YYYY-MM-DDTHH:MM:SSZ` |
| `end_time_utc` | string | no | Chat end time in UTC — format: `YYYY-MM-DDTHH:MM:SSZ` |
| `duration` | string/integer | no | Chat duration |
| `subject` | string | no | Chat subject |
| `chat_messages` | string | no | Chat transcript or summary |
| `queue_extension` | string | no | Queue or ring group extension |
| `entity_type` | string | no | CRM entity type to link the chat to |
| `entity_id` | string | no | CRM entity ID to link the chat to |
| `agent` | string | no | Agent identifier |
| `agent_first_name` | string | no | Answering agent's first name |
| `agent_last_name` | string | no | Answering agent's last name |
| `agent_email` | string | no | Answering agent's email address |

**Response `201`**

```json
{
  "response": "success",
  "message": "Chat saved successfully.",
  "chat": { ... }
}
```

**Response `400`** — chat logging is not enabled for this account.
