---
title: Calls and call logs
description: Log phone calls against Contacts, Deals and prospect contacts, and read your call history.
product: signal
section: api
order: 19
---

# Calls and call logs

A call log records a phone call: who it was with, which way it went, how long it lasted and how it ended. Logged calls count in Signal's call history and call reporting.

These endpoints use the `activities:read` and `activities:write` scopes.

There are two ways to log a call. They take the same body and return the same object:

- **`POST /calls`** (recommended).
- **`POST /call-logs`** is the original endpoint and is kept for existing integrations.

Both check that any linked Contact, Deal or prospect contact belongs to your workspace.

## The call object

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string | 25-character ID. |
| `subject` | string | |
| `direction` | string or null | `outbound` or `inbound`. |
| `duration_seconds` | integer or null | |
| `outcome` | string or null | Free text. |
| `notes` | string or null | |
| `contact_id` | string or null | |
| `deal_id` | string or null | |
| `prospect_id` | string or null | |
| `owner_id` | string or null | User ID of the person who made or took the call. |
| `owner_name` | string or null | That user's name. Read-only. |
| `created_at` | string | ISO 8601 timestamp. |
| `updated_at` | string | ISO 8601 timestamp. |

```json
{
  "id": "cm3ka1m4s0002ab7c9d1e3f5g",
  "subject": "Discovery call with Maria Garcia",
  "direction": "outbound",
  "duration_seconds": 754,
  "outcome": "connected",
  "notes": "Wants pricing for 250 seats.",
  "contact_id": "a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54",
  "deal_id": "7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81",
  "prospect_id": null,
  "owner_id": "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20",
  "owner_name": "Jordan Smith",
  "created_at": "2026-11-12T09:30:00.000Z",
  "updated_at": "2026-11-12T09:30:00.000Z"
}
```

---

## List calls

`GET /calls` · Scope: `activities:read`

Returns calls newest first.

### Query parameters

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `contact_id` | string | No | |
| `deal_id` | string | No | |
| `prospect_id` | string | No | |
| `owner_id` | string | No | |
| `limit` | integer | No | 1 to 100. Default 20. |
| `cursor` | string | No | `meta.cursor` from the previous page. |

### Example

```bash
curl "https://crm.empiraa.app/api/v1/calls?owner_id=5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```js
const url = new URL("https://crm.empiraa.app/api/v1/calls");
url.searchParams.set("owner_id", "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20");
const res = await fetch(url, { headers: { Authorization: "Bearer YOUR_API_KEY" } });
const { data, meta } = await res.json();
```

### Response `200`

An array of [call objects](#the-call-object) in `data`; `meta` has `cursor` and `hasMore`.

---

## Log a call

`POST /calls` · Scope: `activities:write` · Supports [`Idempotency-Key`](../conventions.md#idempotency)

### Request body

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `subject` | string | Yes | |
| `direction` | string or null | No | `outbound` or `inbound`. |
| `duration_seconds` | integer or null | No | Ignored unless it's a number. |
| `outcome` | string or null | No | |
| `notes` | string or null | No | |
| `contact_id` | string or null | No | Must be a Contact in this workspace. |
| `deal_id` | string or null | No | Must be a Deal in this workspace. |
| `prospect_id` | string or null | No | Must be a prospect contact in this workspace. |
| `owner_id` | string or null | No | A member of the workspace. Leave out and the call has no owner, so it isn't credited to anyone. |

### Example

```bash
curl -X POST https://crm.empiraa.app/api/v1/calls \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: call-20261112-0930-a1d4e7b2" \
  -d '{
    "subject": "Discovery call with Maria Garcia",
    "direction": "outbound",
    "duration_seconds": 754,
    "outcome": "connected",
    "contact_id": "a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54",
    "owner_id": "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20"
  }'
```

```js
const res = await fetch("https://crm.empiraa.app/api/v1/calls", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    subject: "Discovery call with Maria Garcia",
    direction: "outbound",
    duration_seconds: 754,
    outcome: "connected",
    contact_id: "a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54",
    owner_id: "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20",
  }),
});
```

### Response `201`

The new [call object](#the-call-object) in `data`.

### Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_REQUEST` | `subject` is missing, or `owner_id` isn't a string or `null`. |
| 404 | `NOT_FOUND` | A linked Contact, Deal or prospect contact isn't in this workspace, or `owner_id` isn't a member of the workspace. |

**Webhook events:** none.

---

## Get a call

`GET /calls/{id}` · Scope: `activities:read`

### Example

```bash
curl https://crm.empiraa.app/api/v1/calls/cm3ka1m4s0002ab7c9d1e3f5g \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```js
await fetch("https://crm.empiraa.app/api/v1/calls/cm3ka1m4s0002ab7c9d1e3f5g", {
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});
```

### Response `200`

The [call object](#the-call-object) in `data`.

### Errors

| Status | Code | When |
| --- | --- | --- |
| 404 | `NOT_FOUND` | No call with that ID in this workspace. |

---

## Log a call (original endpoint)

`POST /call-logs` · Scope: `activities:write` · Supports [`Idempotency-Key`](../conventions.md#idempotency)

Takes the same body as [`POST /calls`](#log-a-call) and returns the same `201` response. It doesn't check that linked records belong to your workspace, so prefer `POST /calls` for new integrations. `/call-logs` accepts only `POST`.

### Example

```bash
curl -X POST https://crm.empiraa.app/api/v1/call-logs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject": "Follow-up call", "direction": "inbound", "duration_seconds": 180}'
```

```js
await fetch("https://crm.empiraa.app/api/v1/call-logs", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ subject: "Follow-up call", direction: "inbound", duration_seconds: 180 }),
});
```

### Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_REQUEST` | `subject` is missing, or `owner_id` isn't a string or `null`. |
| 404 | `NOT_FOUND` | A linked Contact, Deal or prospect contact isn't in this workspace, or `owner_id` isn't a member of the workspace. |

**Webhook events:** none.
