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

# List Call Logs

> Returns a paginated list of call logs with optional filters.

# List Call Logs

Returns a paginated list of call logs with optional filters. Default sort: `created_at` DESC. Supports incremental polling via `updated_since`.

> [!NOTE]
>
> Need ready-made totals (connection rate, conversion rate, sessions...)? Use [Get Statistics](https://docs.reachedapp.com/rest-api/statistics/get-stats), which returns the same numbers as the Statistics page of the app.

## Request

`GET /v1/call-logs`

### Query Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `page` | integer | No | Page number (default: 1) |
| `per_page` | integer | No | Results per page, max 100 (default: 25) |
| `lead_id` | uuid | No | Filter by lead ID |
| `campaign_id` | uuid | No | Filter by campaign ID |
| `agent_id` | uuid | No | Filter by agent (user) ID |
| `disposition` | string | No | Filter by disposition (e.g. `connected`, `voicemail-left`, `appointment-scheduled`, `not-interested`) |
| `date_from` | string | No | Filter calls created after this ISO 8601 timestamp (e.g. `2026-03-01T00:00:00Z`) |
| `date_to` | string | No | Filter calls created before this ISO 8601 timestamp |
| `updated_since` | string | No | Incremental polling: return only calls updated after this ISO 8601 timestamp. Use the latest `updated_at` from the previous poll. (`updated_after` is accepted as an alias.) |
| `updated_after` | string | No | Alias for `updated_since`. |
| `include` | string | No | Set to `"lead"` to embed lead details (`first_name`, `last_name`, `email`, `phone`, `company_name`, `title`) in each call log object. Avoids a separate `/v1/leads` lookup per call. |
| `is_conversation` | boolean | No | Filter for real conversations (answered + bridged exchange, duration >= 60s). `true` = only conversations, `false` = only non-conversations. |

## Example

```bash cURL theme={null}
curl -X GET \
  "https://kdjmltmhxvvmiuehafgl.supabase.co/functions/v1/api-gateway/v1/call-logs?campaign_id=c1d2e3f4-...&date_from=2026-03-01T00:00:00Z" \
  -H "Authorization: Bearer rchd_live_xxxxxxxxxxxx"
```

```javascript JavaScript theme={null}
const params = new URLSearchParams({
  campaign_id: "c1d2e3f4-...",
  date_from: "2026-03-01T00:00:00Z"
});

const response = await fetch(
  `https://kdjmltmhxvvmiuehafgl.supabase.co/functions/v1/api-gateway/v1/call-logs?${params}`,
  {
    headers: {
      "Authorization": "Bearer rchd_live_xxxxxxxxxxxx"
    }
  }
);
const { data, meta } = await response.json();
```

```python Python theme={null}
import requests

response = requests.get(
    "https://kdjmltmhxvvmiuehafgl.supabase.co/functions/v1/api-gateway/v1/call-logs",
    headers={"Authorization": "Bearer rchd_live_xxxxxxxxxxxx"},
    params={
        "campaign_id": "c1d2e3f4-...",
        "date_from": "2026-03-01T00:00:00Z"
    }
)
result = response.json()
```

### Incremental polling example

```bash cURL theme={null}
curl -X GET \
  "https://kdjmltmhxvvmiuehafgl.supabase.co/functions/v1/api-gateway/v1/call-logs?updated_since=2026-03-21T14:33:00Z&per_page=100" \
  -H "Authorization: Bearer rchd_live_xxxxxxxxxxxx"
```

```javascript JavaScript theme={null}
const params = new URLSearchParams({
  updated_since: "2026-03-21T14:33:00Z",
  per_page: "100"
});

const response = await fetch(
  `https://kdjmltmhxvvmiuehafgl.supabase.co/functions/v1/api-gateway/v1/call-logs?${params}`,
  { headers: { "Authorization": "Bearer rchd_live_xxxxxxxxxxxx" } }
);
const { data, meta } = await response.json();
```

### Embed lead details

```bash cURL theme={null}
curl -X GET \
  "https://kdjmltmhxvvmiuehafgl.supabase.co/functions/v1/api-gateway/v1/call-logs?include=lead&per_page=25" \
  -H "Authorization: Bearer rchd_live_xxxxxxxxxxxx"
```

```javascript JavaScript theme={null}
const response = await fetch(
  "https://kdjmltmhxvvmiuehafgl.supabase.co/functions/v1/api-gateway/v1/call-logs?include=lead&per_page=25",
  { headers: { "Authorization": "Bearer rchd_live_xxxxxxxxxxxx" } }
);
const { data } = await response.json();
// Each call log now includes a "lead" object with first_name, last_name, email, phone, company_name, title
```

## Response

```json theme={null}
{
  "data": [
    {
      "id": "log-uuid-1",
      "lead_id": "a1b2c3d4-...",
      "campaign_id": "c1d2e3f4-...",
      "agent_id": "user-uuid-1",
      "from_number": "+33140000000",
      "to_number": "+33612345678",
      "duration": 145,
      "status": "completed",
      "direction": "outbound",
      "disposition": "appointment-scheduled",
      "is_conversation": true,
      "session_id": "session-uuid-1",
      "was_bridged_to_agent": true,
      "transcription_status": "completed",
      "recording_url": "https://...",
      "started_at": "2026-03-21T14:30:00.000Z",
      "ended_at": "2026-03-21T14:32:25.000Z",
      "created_at": "2026-03-21T14:30:00.000Z",
      "updated_at": "2026-03-21T14:33:00.000Z"
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total": 1
  }
}
```

### Response Fields

| Field | Type | Description |
| - | - | - |
| `id` | uuid | Call log unique identifier |
| `lead_id` | uuid | Associated lead ID |
| `campaign_id` | uuid | Associated campaign ID |
| `agent_id` | uuid | ID of the agent who handled the call |
| `from_number` | string | Caller phone number |
| `to_number` | string | Called phone number |
| `duration` | integer | Call duration in seconds |
| `status` | string | Call status (`completed`, `no-answer`, `busy`, `failed`) |
| `direction` | string | `outbound` or `inbound` |
| `disposition` | string | Call outcome disposition |
| `is_conversation` | boolean | Whether the call was a real conversation (answered + bridged, duration >= 60s) |
| `session_id` | uuid | Dialing attempt this call belongs to. With parallel dialing, every line dialed in the same attempt shares the same `session_id`, so one session = one dialing attempt |
| `was_bridged_to_agent` | boolean | `true` when the person who picked up was actually put through to the agent (a real live connection), `false` otherwise |
| `transcription_status` | string | Transcription status (`pending`, `completed`, `failed`, `none`) |
| `recording_url` | string | URL to the call recording (if available) |
| `started_at` | datetime | Call start timestamp |
| `ended_at` | datetime | Call end timestamp |
| `created_at` | datetime | Call log creation timestamp |
| `updated_at` | datetime | Last update timestamp (use for incremental polling) |

### Disposition Values

| Disposition | Category | Description |
| - | - | - |
| `appointment-scheduled` | Reached | Meeting or demo scheduled |
| `conversation` | Reached | Real conversation happened |
| `not-interested` | Reached | Lead not interested |
| `voicemail-left` | Attempted | Voicemail message left |
| `no-answer` | Attempted | No one answered |
| `voicemail` | Attempted | Reached voicemail (no message left) |
| `callback-requested` | Attempted | Lead requested a callback |
| `wrong-number` | Attempted | Wrong number |
| `wrong-person` | Attempted | Wrong contact |
| `gatekeeper` | Attempted | Blocked by gatekeeper |
| `no-longer-at-company` | Attempted | Contact left the company |
| `abandoned-call` | Attempted | Call abandoned |
| `voice-assistant-filtered` | Attempted | Filtered by voice assistant |
| `voicemail-avoided` | Attempted | Voicemail detected, call ended before connecting |

> [!NOTE]
>
> Custom dispositions created in your workspace have values prefixed with `custom_` (e.g. `custom_a1b2c3d4`). Use the [List Dispositions](https://docs.reachedapp.com/rest-api/dispositions/list-dispositions) endpoint to retrieve all available dispositions including custom ones.


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