> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.askelephant.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.askelephant.ai/_mcp/server.

# Searching engagements

`GET /v2/engagements` is the main search surface for engagement discovery.

Use it when you need to:

* Find engagements linked to CRM records such as deals, companies, or contacts
* Find engagements for known AskElephant `contact` or `company` IDs
* Narrow results by time window, processing state, or engagement type
* Load related engagement details before fetching transcript timelines

If you specifically want CRM company, contact, or deal examples, see [Filter engagements by CRM object IDs](/filtering-engagements-by-crm-object-ids).
Every engagement object now includes `is_private` so your integration can see the privacy state of the engagements it is allowed to read.

## What the endpoint supports

The most common engagement search parameters are:

| Parameter                              | What it does                                                                                                    |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `filter[crm_associations][eq]`         | Matches engagements linked to one or more CRM objects.                                                          |
| `filter[contact_ids][eq\|in]`          | Matches engagements linked to AskElephant contact IDs.                                                          |
| `filter[company_ids][eq\|in]`          | Matches engagements linked to AskElephant company IDs.                                                          |
| `filter[start_at][gt\|gte\|lt\|lte]`   | Narrows by engagement start time.                                                                               |
| `filter[updated_at][gt\|gte\|lt\|lte]` | Narrows by last update time.                                                                                    |
| `filter[processing_status][eq\|in]`    | Filters by transcript or analysis processing state.                                                             |
| `filter[engagement_type][eq\|in]`      | Filters by engagement type such as `MEETING` or `CALL`.                                                         |
| `expand`                               | Includes related fields like `companies`, `contacts`, `owner`, `signals`, `transcript`, `media_url`, or `tags`. |
| `order_by`                             | Sorts by `updated_at:asc` or `updated_at:desc`.                                                                 |

## Important behavior

For readability, docs examples use indexed bracket syntax, but the equivalent object shape is:

```json
[
  { "id": "12345", "object_type": "company" },
  { "id": "67890", "object_type": "contact" },
  { "id": "deal_abc123", "object_type": "deal" }
]
```

Single-object examples:

* CRM company ID: `{ "id": "12345", "object_type": "company" }`
* CRM contact ID: `{ "id": "67890", "object_type": "contact" }`
* CRM deal ID: `{ "id": "deal_abc123", "object_type": "deal" }`

The CRM source is inferred from the workspace's connected CRM. You do not pass a separate `source` query parameter.

Engagement filters combine like this:

* Values in the same bucket use OR semantics
* Populated buckets combine with AND semantics
* AskElephant `company_ids` and CRM company associations share the company bucket
* AskElephant `contact_ids` and CRM contact associations share the participant bucket
* CRM deal associations populate a deal bucket

That means you can search for:

* Any engagement related to one of several contacts
* Any engagement related to one of several CRM deals
* Engagements that match both a company bucket and a participant bucket

## Search by CRM object ID

Use `filter[crm_associations][eq]` when your system already knows the CRM record IDs and you want the related engagements.

When using raw `filter[...]` query params with `curl`, add `--globoff` so `curl` does not interpret `[]` as URL globbing syntax.

Example: find engagements linked to a CRM deal:

```bash
curl --request GET \
  --globoff \
  --url 'https://app.askelephant.ai/api/v2/engagements?filter[crm_associations][eq][0][id]=deal_123&filter[crm_associations][eq][0][object_type]=deal&order_by=updated_at:desc&limit=25' \
  --header 'Authorization: sk-apik_<id>.<secret>' \
  --header 'Accept: application/json'
```

Example: search by multiple CRM objects in one request:

```bash
curl --request GET \
  --globoff \
  --url 'https://app.askelephant.ai/api/v2/engagements?filter[crm_associations][eq][0][id]=deal_123&filter[crm_associations][eq][0][object_type]=deal&filter[crm_associations][eq][1][id]=contact_456&filter[crm_associations][eq][1][object_type]=contact&order_by=updated_at:desc&limit=25' \
  --header 'Authorization: sk-apik_<id>.<secret>' \
  --header 'Accept: application/json'
```

Use this pattern when your integration starts from Salesforce, HubSpot, or another CRM and you need to map back to AskElephant engagements.

## Search by AskElephant company IDs

Use `filter[company_ids][eq]` or `filter[company_ids][in]` when you already have AskElephant company IDs.

Example:

```bash
curl --request GET \
  --globoff \
  --url 'https://app.askelephant.ai/api/v2/engagements?filter[company_ids][in]=cmp_01HQXVB4Y9Y0Q6P0QH5GX2CT7T,cmp_01HQXVB4Y9Y0Q6P0QH5GX2CT7U&expand=companies,owner&order_by=updated_at:desc&limit=25' \
  --header 'Authorization: sk-apik_<id>.<secret>' \
  --header 'Accept: application/json'
```

This is useful when your app stores AskElephant company IDs directly and wants all engagement activity around one or more companies.

## Search by AskElephant contact IDs

Use `filter[contact_ids][eq]` or `filter[contact_ids][in]` when you know the AskElephant contact IDs.

Example:

```bash
curl --request GET \
  --globoff \
  --url 'https://app.askelephant.ai/api/v2/engagements?filter[contact_ids][eq]=cnt_01HQY3JMS2QAXJGX6X7CH7CM6X&expand=contacts,owner&order_by=updated_at:desc&limit=25' \
  --header 'Authorization: sk-apik_<id>.<secret>' \
  --header 'Accept: application/json'
```

This is the cleanest path when a user is already looking at a known AskElephant contact record and wants the related meetings, calls, emails, or notes.

## Combine company, contact, and CRM filters

You can combine filters to narrow results further.

Example: find engagements that are tied to a specific company, a specific contact, and a CRM deal:

```bash
curl --request GET \
  --globoff \
  --url 'https://app.askelephant.ai/api/v2/engagements?filter[company_ids][eq]=cmp_01HQXVB4Y9Y0Q6P0QH5GX2CT7T&filter[contact_ids][eq]=cnt_01HQY3JMS2QAXJGX6X7CH7CM6X&filter[crm_associations][eq][0][id]=deal_123&filter[crm_associations][eq][0][object_type]=deal&filter[start_at][gte]=2026-03-01T00:00:00.000Z&order_by=updated_at:desc&limit=25' \
  --header 'Authorization: sk-apik_<id>.<secret>' \
  --header 'Accept: application/json'
```

This is useful for account-level timeline views where you want only the subset of engagements that match a particular relationship context.

## Loading transcript-related data

There are two distinct patterns:

| Need                                                              | Recommended call                                          |
| ----------------------------------------------------------------- | --------------------------------------------------------- |
| Include transcript-related data in the engagement payload         | `GET /v2/engagements?...&expand=transcript`               |
| Retrieve the full transcript timeline entries with speaker timing | `GET /v2/engagements/{engagement_id}/transcript_timeline` |

If you want full transcript timeline entries, first search engagements, then fetch timelines by the returned engagement IDs.

Example: search engagements and include transcript expansion:

```bash
curl --request GET \
  --globoff \
  --url 'https://app.askelephant.ai/api/v2/engagements?filter[contact_ids][eq]=cnt_01HQY3JMS2QAXJGX6X7CH7CM6X&filter[processing_status][eq]=COMPLETED&expand=transcript,contacts,companies&order_by=updated_at:desc&limit=10' \
  --header 'Authorization: sk-apik_<id>.<secret>' \
  --header 'Accept: application/json'
```

Then fetch the transcript timeline for one engagement:

```bash
curl --request GET \
  --url 'https://app.askelephant.ai/api/v2/engagements/ngmt_01JWAS.../transcript_timeline' \
  --header 'Authorization: sk-apik_<id>.<secret>' \
  --header 'Accept: application/json'
```

The transcript timeline endpoint requires the `transcript_timelines:read` scope. If the engagement is private, it also requires `engagements:read` and `engagements:private:read`.

## Recommended search recipes

### CRM-first integration

1. Start with `filter[crm_associations][eq]`
2. Add `filter[start_at][gte]` or `filter[updated_at][gte]` if you are narrowing to recent activity
3. Use `expand=contacts,companies,owner` if you want richer context in the search response
4. Fetch `/transcript_timeline` only for the engagement IDs you actually need

### Contact activity timeline

1. Use `filter[contact_ids][eq]`
2. Add `filter[processing_status][eq]=COMPLETED` if you only want engagements with completed processing
3. Sort with `order_by=updated_at:desc`
4. Fetch transcript timelines for the selected meetings or calls

### Company account review

1. Use `filter[company_ids][eq]` or `filter[company_ids][in]`
2. Add `filter[engagement_type][in]=MEETING,CALL`
3. Expand `companies,contacts,owner,tags`
4. Load transcript timelines for the specific engagements that matter

## Practical notes

* `search` only matches engagement titles. It does not search transcript text, contact names, company names, descriptions, or CRM fields.
* `filter[crm_associations][eq]` supports up to 20 CRM association objects.
* `filter[company_ids][in]` and `filter[contact_ids][in]` support up to 25 values each.
* `expand=contacts` additionally requires `contacts:read`.
* `expand=owner` additionally requires `users:read`.
* `engagements:read` returns only non-private engagements; include `engagements:private:read` if your integration must read private engagements too.
* Use `next_cursor` to continue large result sets.

See also: [Pagination and filtering](/pagination)