Skip to main content
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.
All Activities endpoints require an API key with the activities:read scope.

The activity object

An activity represents a single discrete event in the lifecycle of an outreach sequence. The payload.type field is the discriminator — it tells you which event occurred and determines which additional fields are present on the payload.
{
  "id": "01954b2e-fc10-7000-8a3f-d2e591c04a77",
  "created_at": "2025-03-14T09:22:11.483Z",
  "payload": {
    "type": "message.sent",
    "organization_id": "01954b2e-0000-7000-aaaa-111111111111",
    "person_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-eeee-555555555555",
    "hot_lead_id": null,
    "calendar_event_id": null,
    "task_id": null
  }
}

Activity fields

id
string (UUID)
required
Unique identifier for this activity record.
created_at
string (ISO 8601)
required
Timestamp at which the activity was recorded, in UTC with timezone offset.
payload
object
required
Discriminated event payload. The shape varies by payload.type; see Event types below.

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 valueWhat it means
message.sentAn email or LinkedIn message was sent to the contact.
message.openedThe contact opened an email.
message.link_clickedThe contact clicked a tracked link in an email.
message.repliedThe contact replied to a message. Includes a metadata.reply_category field (filled asynchronously — may be null immediately after delivery).
invitation.sentA LinkedIn connection request was sent. Includes metadata.step_number.
invitation.acceptedThe contact accepted a LinkedIn connection request.
hot_lead.createdTopo’s AI flagged the contact as a hot lead.
meeting.createdA meeting was booked with the contact.
sequence.createdA contact was enrolled into a sequence.
sequence.pausedThe sequence was paused. May include metadata.paused_until and metadata.last_step_executed.
sequence.resumedA paused sequence was resumed.
sequence.stoppedThe sequence was stopped early. Includes metadata.stopped_reason and metadata.error_code.
sequence.completedThe sequence ran to completion.
task.createdA manual task was created for the contact.
task.completedA manual task was marked complete.
task.skippedA manual task was skipped.
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.

List activities

GET /v1/activities
Returns a paginated list of activities recorded in your workspace, ordered by created_at descending by default. Use the filter parameters to narrow results.

Query parameters

person_id
string (UUID)
Return only activities for a specific contact.
sequence_id
string (UUID)
Return only activities from a specific sequence run (enrollment).
sequence_template_id
string (UUID)
Return only activities from sequences based on a specific template.
resource_type
string
Filter by the type of resource involved. One of MESSAGE, INVITATION, HOT_LEAD, MEETING, SEQUENCE_STEP, SEQUENCE, TASK.
channel
string
Filter by outreach channel. One of EMAIL, LINKEDIN, MANUAL.
event_type
string
Filter by the low-level event type. One of SENT, REPLIED, OPENED, LINK_CLICKED, ACCEPTED, CREATED, PAUSED, RESUMED, STOPPED, COMPLETED, SKIPPED.
created_at_after
string (ISO 8601)
Return activities strictly after this timestamp (exclusive). Useful for incremental polling — store the created_at of the most recent activity you processed and pass it here on the next request.
created_at_before
string (ISO 8601)
Return activities strictly before this timestamp (exclusive).
sort_by
string
Field to sort by. Currently only created_at is supported.
sort_order
string
default:"DESC"
Sort direction. ASC or DESC.
page
integer
default:"1"
Page number to retrieve. Must be ≥ 1.
size
integer
default:"10"
Number of items per page. Must be between 1 and 100.

Response

Returns a pagination envelope containing a list of activity objects.
items
array
Array of activity objects for the current page.
total_count
integer
Total number of activities matching the current filters.
total_pages
integer
Total number of pages given the current size.
has_more
boolean
Whether there are more pages beyond the current one.

Example — list replies and hot leads for a contact

curl -G https://api.topo.io/v1/activities \
  -H "Authorization: Bearer topo_live_sk_••••••••••••••••" \
  -d person_id="01954b2e-1111-7000-bbbb-222222222222" \
  -d event_type="REPLIED" \
  -d sort_order="ASC" \
  -d page=1 \
  -d size=50
{
  "items": [
    {
      "id": "01954b2e-fc10-7000-8a3f-d2e591c04a77",
      "created_at": "2025-03-14T09:22:11.483Z",
      "payload": {
        "type": "message.replied",
        "organization_id": "01954b2e-0000-7000-aaaa-111111111111",
        "person_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-eeee-555555555555",
        "hot_lead_id": null,
        "calendar_event_id": null,
        "task_id": null,
        "metadata": {
          "reply_category": "INTERESTED"
        }
      }
    }
  ],
  "total_count": 1,
  "total_pages": 1,
  "has_more": false
}

Get a single activity

GET /v1/activities/{id}
Fetches a single activity by its ID.

Path parameters

id
string (UUID)
required
The unique identifier of the activity to retrieve.

Example

curl https://api.topo.io/v1/activities/01954b2e-fc10-7000-8a3f-d2e591c04a77 \
  -H "Authorization: Bearer topo_live_sk_••••••••••••••••"
Returns a single activity object.

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.
If you need real-time delivery instead of polling, use Webhooks — Topo will push events to your endpoint within seconds of them occurring.
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.