---
title: "A contact's activity timeline"
method: GET
path: "/business/contacts/{contactId}/activities"
tags: ["Contacts"]
---

# A contact's activity timeline

`GET /business/contacts/{contactId}/activities`

## Request

- Base URL: `https://api.tryfuse.ai/api/v1`
- URL: `https://api.tryfuse.ai/api/v1/business/contacts/{contactId}/activities`
- Auth: HTTP basic

The contact's activity timeline as the app's activity drawer shows it, newest first: campaign emails sent and replies received, LinkedIn invitations and messages, dialer calls with their transcript, recording, disposition and the notes the rep saved, activity logged by a connected CRM, and campaign steps still scheduled to go out (`scheduledAt` set, `sentAt` null). Every entry carries the same keys whatever the channel, so `channel` tells you which of them are filled: email rows carry `subject`, `body`, `from` and `to`; LinkedIn rows carry the message text in `body` and the profile in `linkedinUrl`; call rows carry `phoneNumber`, `direction`, `callTranscript`, `callRecordingUrl`, `callDisposition`, and the call notes — not a message — in `body`. Page with `limit` (1-100, default 100) and `cursor`: copy `nextCursor` from the previous page and stop when it is null. `limit` bounds the page of history; the first page can carry a few entries on top of it (campaign steps still scheduled, and LinkedIn activity, neither of which pages on its own), so read `activities.length` rather than assuming `limit`. Free to call.

Errors:
- `404` `CONTACT_NOT_FOUND` — No contact with that id exists.
- `422` `VALIDATION_FAILED` — The path, query or body failed validation; `param` names the offending field and `message` says why.
- `429` `RATE_LIMITED` — More than 120 requests per minute or 10,000 per day on the contacts bucket, which every /business/contacts endpoint shares; retry after Retry-After.
- `500` `UNEXPECTED_ERROR` — The timeline could not be assembled; quote request_id to support.

Requires one of the following token scopes: contacts.

## Path parameters

- `contactId` string, required: Id of the contact: the row id from GET /business/lists/{listId}/rows, or the contactId returned by POST /business/contacts/resolve.

## Query parameters

- `limit` integer: Maximum number of activities per page, 1-100 (default 100). It bounds the page of history; the first page can carry a few entries on top of it (campaign steps still scheduled, and LinkedIn activity, neither of which pages on its own), so read `activities.length` rather than assuming `limit`.
- `cursor` string: Opaque cursor copied from the previous page's `nextCursor` (max 500 characters). Omit it for the first page.

## Response `200`

Success

- object
  - `contactId` string, required: Id of the contact.
  - `activities` object[], required: One page of the timeline, newest first, with any campaign steps still scheduled pinned at the top. The first page can hold a few entries more than `limit`.
    - `id` string, nullable, required: Id of the activity (the provider message id on LinkedIn rows).
    - `channel` string, nullable, required: Where the activity happened: `email`, `linkedin_message`, `linkedin_connection`, `inmail`, `linkedin_visit`, `linkedin_like`, `non_campaign_call` (a call that was placed, campaign or one-off), `dialer` (a campaign call step still scheduled), `todo` (a manual campaign step), and `crm_activity` for a note, meeting or task logged in a connected CRM. A CRM's own emails and calls arrive as `email` and `non_campaign_call` like any other.
    - `status` string, nullable, required: Delivery state, in the vocabulary of the store the row came from. Campaign steps: scheduled, approval_required, action_required, sent, replied, skipped, stopped, overwritten, preview, failed. Calls: answered, answered-subsequent, notAnswered, callEnded, answeredByMachine, message-played, busy, inbound-forwarding-not-accepted, failed, cancelled, pick-up-race-dropped. LinkedIn: sent, replied. Activity logged by a connected CRM: logged.
    - `direction` 'inbound' | 'outbound' | 'null', nullable, required: Direction when the channel records one (calls always do); null otherwise.
    - `campaignId` string, nullable, required: Campaign the activity belongs to; null for activity outside a campaign.
    - `subject` string, nullable, required: Email subject; null on every other channel.
    - `body` object, nullable, required: The text the activity carries: the email body, the LinkedIn message text, or — on call rows — the notes the rep saved against the call. Null when the activity carries no text. Read `contentType` before rendering it: the same key carries HTML and plain text, and a long body can arrive shortened to a snippet.
      - `contentType` 'html' | 'text', required: How to read `content`.
      - `content` string, required: The body text, in the form `contentType` names; a long body can be shortened to a snippet.
    - `sentAt` string, date-time, nullable, required: When an outbound activity went out.
    - `receivedAt` string, date-time, nullable, required: When an inbound activity arrived.
    - `scheduledAt` string, date-time, nullable, required: For a campaign step that has not gone out yet: when it will.
    - `from` object, nullable, required: Email rows: the sender; null when the channel records no address.
      - `name` string, nullable, required: Display name when known.
      - `address` string, required: Email address.
    - `to` object[], required: Email rows: the recipients; empty on channels that record no address.
      - `name` string, nullable, required: Display name when known.
      - `address` string, required: Email address.
    - `linkedinUrl` string, nullable, required: LinkedIn rows: the profile the message or invitation was sent to.
    - `phoneNumber` string, nullable, required: Call rows: the number dialled.
    - `callTranscript` string, nullable, required: Call rows: the transcript when one was produced.
    - `callRecordingUrl` string, nullable, required: Call rows: a time-limited recording URL when a recording exists.
    - `callDisposition` 'connected-positive' | 'connected-neutral' | 'connected-negative' | 'connected-disqualified' | 'connected-follow-up' | 'left-voicemail' | 'no-answer' | 'connected-busy' | 'bad-wrong-number' | 'not-specified' | 'null', nullable, required: Call rows: the outcome the rep recorded. `not-specified` is the default a call carries until the rep sets one; null on every other channel.
  - `nextCursor` string, nullable, required: Cursor for the next page; null on the last page.

## Other responses

- `400`: Error
- `401`: Error
- `402`: Error
- `403`: Error
- `404`: Error
- `409`: Error
- `422`: Error
- `429`: Error
- `500`: Error

---

[API](https://skmtc.dev/fuseai/apis/fuse-business-api.md) · [All operations](https://skmtc.dev/fuseai/apis/fuse-business-api/llms.txt) · [OpenAPI document](https://skmtc.dev/fuseai/apis/fuse-business-api/revisions/50d95e0536f4?raw)
