---
title: Activities
description: Log, read, update and delete activities such as Actions, calls, meetings and emails.
product: signal
section: api
order: 17
---

# Activities

An activity is something someone did, or plans to do, about a record: an Action, a call, a meeting, an email, a LinkedIn touchpoint or a note. Activities can link to a Deal, a Contact, an Account and a prospect contact at the same time. They appear on the **Activities** page and on each linked record in Signal.

**Actions** are activities with `type_id` `rat_action`. To create an Action that appears in Signal's Actions list, use `POST /activities` with that type.

To log a phone call with its duration and direction, you can also use [`POST /calls`](calls-and-call-logs.md).

## The activity object

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string | UUID. |
| `subject` | string | |
| `description` | string or null | |
| `type_id` | string | See [Activity types](#activity-types). |
| `start_at` | string or null | ISO 8601 timestamp. |
| `end_at` | string or null | ISO 8601 timestamp. |
| `is_completed` | boolean | |
| `completed_at` | string or null | Set when the activity is marked complete. |
| `outcome` | string or null | Free text. |
| `deal_id` | string or null | Linked Deal. |
| `contact_id` | string or null | Linked Contact. |
| `prospect_id` | string or null | Linked prospect contact. |
| `account_id` | string or null | Linked Account. |
| `owner_id` | string or null | User ID of the person the activity belongs to. |
| `assigned_user_id` | string or null | User ID of the teammate the activity has been handed to, if any. |
| `created_at` | string | ISO 8601 timestamp. |
| `updated_at` | string | ISO 8601 timestamp. |

```json
{
  "id": "4e6a8c0b-2d4f-4a6c-8e0b-3d5f7a9c1e22",
  "subject": "Send revised quote",
  "description": "Include the 3-year option.",
  "type_id": "rat_action",
  "start_at": "2026-11-14T15:00:00.000Z",
  "end_at": null,
  "is_completed": false,
  "completed_at": null,
  "outcome": null,
  "deal_id": "7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81",
  "contact_id": "a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54",
  "prospect_id": null,
  "account_id": "3f6c2a1e-8b4d-4c2a-9f1e-2b7d5a9c4e10",
  "owner_id": "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20",
  "assigned_user_id": null,
  "created_at": "2026-11-12T09:30:00.000Z",
  "updated_at": "2026-11-12T09:30:00.000Z"
}
```

## Activity types

| `type_id` | Type |
| --- | --- |
| `rat_action` | Action |
| `rat_call` | Call |
| `rat_email` | Email |
| `rat_meeting` | Meeting |
| `rat_linkedin` | LinkedIn |
| `rat_task` | Task |
| `rat_note` | Note |

Signal also records some activities itself, which you may see when reading: `rat_stage_change` (a Deal moved stage), `rat_deal_won` and `rat_deal_lost`.

`type_id` must be one of these, or an older type Signal still records, such as `rat_demo` or `rat_follow_up`. A bare name also works and means the type with the `rat_` prefix: `call` is `rat_call`, `Meeting` is `rat_meeting`. Any other value answers `400 INVALID_REQUEST` ("Unknown type_id"), and nothing is created.

---

## List activities

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

Returns activities newest first (by when they were created).

### Query parameters

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `deal_id` | string | No | Only activities linked to this Deal. |
| `contact_id` | string | No | Only activities linked to this Contact. |
| `prospect_id` | string | No | Only activities linked to this prospect contact. |
| `account_id` | string | No | Only activities linked to this Account. |
| `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/activities?deal_id=7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```js
const url = new URL("https://crm.empiraa.app/api/v1/activities");
url.searchParams.set("deal_id", "7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81");
const res = await fetch(url, { headers: { Authorization: "Bearer YOUR_API_KEY" } });
const { data, meta } = await res.json();
```

### Response `200`

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

---

## Create an activity

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

### Request body

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `subject` | string | Yes | |
| `type_id` | string | Yes | See [Activity types](#activity-types). |
| `description` | string or null | No | |
| `start_at` | string or null | No | ISO 8601 timestamp. |
| `end_at` | string or null | No | ISO 8601 timestamp. |
| `outcome` | string or null | No | |
| `deal_id` | string or null | No | Must be a Deal in this workspace. |
| `contact_id` | string or null | No | Must be a Contact in this workspace. |
| `prospect_id` | string or null | No | Must be a prospect contact in this workspace. |
| `account_id` | string or null | No | Must be an Account in this workspace. |
| `owner_id` | string or null | No | A member of the workspace. Leave out for no owner. |
| `assigned_user_id` | string or null | No | A member of the workspace. |

### Example: create an Action

```bash
curl -X POST https://crm.empiraa.app/api/v1/activities \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6d1f3b5a-8c2e-4a7d-9f0b-1e3c5a7d9b42" \
  -d '{
    "subject": "Send revised quote",
    "type_id": "rat_action",
    "start_at": "2026-11-14T15:00:00Z",
    "deal_id": "7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81",
    "owner_id": "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20"
  }'
```

```js
const res = await fetch("https://crm.empiraa.app/api/v1/activities", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    subject: "Send revised quote",
    type_id: "rat_action",
    start_at: "2026-11-14T15:00:00Z",
    deal_id: "7c2e9f14-3a6b-4d1e-8f5a-9b0c2d4e6f81",
    owner_id: "5b8d1f3a-7e2c-4a9b-b6d4-1c3e5f7a9b20",
  }),
});
```

### Response `201`

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

### Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_REQUEST` | `subject` or `type_id` is missing, or `type_id` isn't an activity type; `start_at` or `end_at` isn't a date; or `owner_id` or `assigned_user_id` isn't a string or `null`. |
| 404 | `NOT_FOUND` | A linked Deal, Contact, Account or prospect contact isn't in this workspace (`title` says which), or `owner_id` or `assigned_user_id` isn't a member of the workspace. |

**Webhook events:** none.

---

## Get an activity

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

### Example

```bash
curl https://crm.empiraa.app/api/v1/activities/4e6a8c0b-2d4f-4a6c-8e0b-3d5f7a9c1e22 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```js
await fetch("https://crm.empiraa.app/api/v1/activities/4e6a8c0b-2d4f-4a6c-8e0b-3d5f7a9c1e22", {
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});
```

### Response `200`

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

### Errors

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

---

## Update an activity

`PATCH /activities/{id}` · Scope: `activities:write`

Send only the fields you want to change. Use `is_completed` to mark an activity done.

### Request body

| Field | Type | Notes |
| --- | --- | --- |
| `subject` | string | |
| `description` | string or null | |
| `outcome` | string or null | |
| `start_at` | string or null | ISO 8601 timestamp. |
| `end_at` | string or null | ISO 8601 timestamp. |
| `is_completed` | boolean | `true` marks it done and sets `completed_at` to now; `false` clears `completed_at`. |
| `deal_id` | string or null | Must be a Deal in this workspace. |
| `contact_id` | string or null | Must be a Contact in this workspace. |
| `account_id` | string or null | Must be an Account in this workspace. |
| `owner_id` | string or null | A member of the workspace, or `null`. |
| `assigned_user_id` | string or null | A member of the workspace, or `null`. |

`type_id` and `prospect_id` can't be changed after creation.

### Example: mark complete

```bash
curl -X PATCH https://crm.empiraa.app/api/v1/activities/4e6a8c0b-2d4f-4a6c-8e0b-3d5f7a9c1e22 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"is_completed": true, "outcome": "Quote sent"}'
```

```js
await fetch("https://crm.empiraa.app/api/v1/activities/4e6a8c0b-2d4f-4a6c-8e0b-3d5f7a9c1e22", {
  method: "PATCH",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ is_completed: true, outcome: "Quote sent" }),
});
```

### Response `200`

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

### Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_REQUEST` | A date isn't valid, or `owner_id` or `assigned_user_id` isn't a string or `null`. |
| 404 | `NOT_FOUND` | The activity, a linked record or a named user isn't in this workspace. |

**Webhook events:** none.

---

## Delete an activity

`DELETE /activities/{id}` · Scope: `activities:write`

Deletes the activity permanently.

### Example

```bash
curl -X DELETE https://crm.empiraa.app/api/v1/activities/4e6a8c0b-2d4f-4a6c-8e0b-3d5f7a9c1e22 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```js
await fetch("https://crm.empiraa.app/api/v1/activities/4e6a8c0b-2d4f-4a6c-8e0b-3d5f7a9c1e22", {
  method: "DELETE",
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});
```

### Response `204`

No body.

### Errors

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