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

# Activities API: Stream Engagement Events from Topo

> Query every tracked outreach event in your Topo workspace — sent messages, opens, replies, hot leads, and LinkedIn events — via a paginated REST API.

The Activities API gives you a queryable, paginated log of every engagement event Topo records across your outreach sequences. Each activity carries a strongly-typed `payload` that describes exactly what happened — which contact it involved, which sequence it came from, what channel was used, and any event-specific metadata. The payload schema is the same discriminated union that Topo delivers to webhook subscribers, so you can build consistent pipelines whether you are polling or streaming.

Activities are ideal for building custom reporting dashboards, syncing engagement history to a CRM, or auditing outreach behaviour without setting up a webhook endpoint.

<Note>
  All Activities endpoints require an API key with the **`activities:read`** scope.
</Note>

## Event types

The `payload.type` field is always one of the following values. Topo may add new event types as additive changes within `/v1`; your code should handle unknown values gracefully rather than treating them as errors.

| `type` value           | What it means                                                                                                                                    |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `message.sent`         | An email or LinkedIn message was sent to the contact.                                                                                            |
| `message.opened`       | The contact opened an email.                                                                                                                     |
| `message.link_clicked` | The contact clicked a tracked link in an email.                                                                                                  |
| `message.replied`      | The contact replied to a message. Includes a `metadata.reply_category` field (filled asynchronously — may be `null` immediately after delivery). |
| `invitation.sent`      | A LinkedIn connection request was sent. Includes `metadata.step_number`.                                                                         |
| `invitation.accepted`  | The contact accepted a LinkedIn connection request.                                                                                              |
| `hot_lead.created`     | Topo's AI flagged the contact as a hot lead.                                                                                                     |
| `meeting.created`      | A meeting was booked with the contact.                                                                                                           |
| `sequence.created`     | A contact was enrolled into a sequence.                                                                                                          |
| `sequence.paused`      | The sequence was paused. May include `metadata.paused_until` and `metadata.last_step_executed`.                                                  |
| `sequence.resumed`     | A paused sequence was resumed.                                                                                                                   |
| `sequence.stopped`     | The sequence was stopped early. Includes `metadata.stopped_reason` and `metadata.error_code`.                                                    |
| `sequence.completed`   | The sequence ran to completion.                                                                                                                  |
| `task.created`         | A manual task was created for the contact.                                                                                                       |
| `task.completed`       | A manual task was marked complete.                                                                                                               |
| `task.skipped`         | A manual task was skipped.                                                                                                                       |
| `task.reopened`        | A previously completed or skipped task was reopened.                                                                                             |

<Tip>
  Use the `event_type` filter to pull only the events you care about — for example, `REPLIED` and `CREATED` events to drive CRM updates without fetching the entire activity stream.
</Tip>

## Embedded summaries

Beyond the ids naming the records an activity touched, each `payload` embeds compact copies of those records — so a single page of activities is usually enough to build a report or a CRM update without fanning out into per-contact lookups. Every payload carries `contact` and `sequence_template`, plus one event-specific embed: `message` on message events, `meeting` on `meeting.created`, `task` on task events, and `hot_lead` on `hot_lead.created`. All field names are `snake_case`.

<Note>
  These are the same embeds Topo delivers to webhook subscribers. See [Embedded summaries](/api-reference/webhooks#embedded-summaries) on the Webhooks page for the field-by-field reference and the null-degradation rules.
</Note>

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "01954b2e-aaaa-7000-eeee-777777777777",
  "created_at": "2026-08-03T10:15:00Z",
  "payload": {
    "type": "message.sent",
    "organization_id": "01954b2e-0000-7000-aaaa-111111111111",
    "contact_id": "01954b2e-1111-7000-bbbb-222222222222",
    "sequence_id": "01954b2e-2222-7000-cccc-333333333333",
    "sequence_template_id": "01954b2e-3333-7000-dddd-444444444444",
    "resource_type": "MESSAGE",
    "channel": "EMAIL",
    "message_id": "01954b2e-4444-7000-dddd-555555555555",
    "hot_lead_id": null,
    "calendar_event_id": null,
    "task_id": null,
    "contact": {
      "id": "01954b2e-1111-7000-bbbb-222222222222",
      "first_name": "Ada",
      "last_name": "Lovelace",
      "email": "ada@analytical.io",
      "linkedin_url": null,
      "job_title": "CTO",
      "company_name": "Analytical Engines",
      "company_domain": "analytical.io"
    },
    "sequence_template": {
      "id": "01954b2e-3333-7000-dddd-444444444444",
      "name": "Outbound to CTOs"
    },
    "message": {
      "id": "01954b2e-4444-7000-dddd-555555555555",
      "subject": "Quick question",
      "body": "Hi Ada, are you the right person?",
      "channel": "EMAIL",
      "direction": "OUTBOUND",
      "sent_at": "2026-08-03T10:14:58Z",
      "step_number": 2
    }
  }
}
```

## Polling for new activities

The Activities API is well-suited to incremental polling patterns for CRM sync or custom reporting. A reliable approach:

1. On first run, fetch activities with `sort_order=ASC` and store the `created_at` of the last item in your cursor.
2. On subsequent runs, pass that cursor as `created_at_after` to retrieve only new events.
3. Page through results using `has_more` until it is `false`.

<Tip>
  If you need real-time delivery instead of polling, use [Webhooks](/api-reference/webhooks) — Topo will push events to your endpoint within seconds of them occurring.
</Tip>

<Note>
  The pagination envelope shape — `{ items, total_count, total_pages, has_more }` — is frozen by Topo's API stability policy. New response fields may be added additively; your client should ignore unknown fields.
</Note>
