# listContactActivities

A contact's activity timeline (calls, texts, emails, notes, system events), most recent first. Filter with activityTypes (e.g. [CALL, TEXT, EMAIL]) and direction (INBOUND/OUTBOUND); page with limit/offset (default 25, max 50). Each entry inlines its call/text/email/note payload: notes and texts in full, calls with their summary, emails with BOTH `snippet` (a short preview) and `textExtract` (the FULL plain-text body). Read textExtract, not snippet, whenever the question is whether something appears in the history: a detail past the preview's first lines would wrongly look absent. textExtract is NULL on the ~3% of emails that arrived HTML-only, whose body this op cannot read AT ALL: null means UNREAD, not empty. Never conclude such an email lacks what you look for; say which emails you could not read and send the user to the contact's timeline in the app. On a NOTE entry, the note's own `author` { kind: USER | AI, id, name } says who wrote it (the entry's `createdByUser` is null for a note an AI assistant wrote for a teammate). An UPDATED_TASK entry carries `metadata.oldValues` (title, endDate, timezone, taskType, participants) plus `newValues.endDate`: THE UNDO PATH for a task edit, the only durable record of a prior value since Task keeps no history. Reverse an edit by writing oldValues back with updateTask; never call a previous value unrecoverable without having looked. Only tasks WITH a contact get one, one per edit, so the oldest holds the pre-change value. The heavier history read: getContact does not include this feed. "Did they call / call back" reads activityTypes [CALL]: texts and notes never answer it. A NOTE is the team's own record, never a message: never quote one as the contact's text or email.

- Call: `POST https://server.goliathdata.com/api/v1/call` with `{ "operationId": "listContactActivities", "variables": {...} }`
- Scope: `READ`
- Type: query
- Idempotency: a read; rejects `Idempotency-Key`
- Rate-limited: no
- Result cap: `limit` is clamped to 50, without an error

## Variables

- `contactId`: `ID!` (required)
- `limit`: `Int` (default `25`)
- `offset`: `Int` (default `0`)
- `activityTypes`: `[ActivityType!]` (one of `NOTE_CREATED`, `NOTE_DELETED`, `NOTE_UPDATED`, `NOTE_PINNED`, `NOTE_UNPINNED`, `USER_MENTIONED_IN_NOTE`, `REPLY_CREATED`, `REPLY_DELETED`, `REPLY_UPDATED`, `REACTED_TO_REPLY`, `REACTED_TO_NOTE`, `REMOVED_REPLY_REACTION`, `REMOVED_NOTE_REACTION`, `ASSIGNED`, `UNASSIGNED`, `CONTACT_ARCHIVED`, `CONTACT_UNARCHIVED`, `ADD_COLLABORATOR`, `REMOVE_COLLABORATOR`, `CALL`, `VOICEMAIL_LEFT`, `TEXT`, `EMAIL`, `FILE_ADDED`, `FILE_REMOVED`, `CREATED_APPOINTMENT`, `DELETED_APPOINTMENT`, `UPDATED_APPOINTMENT`, `CREATED_TASK`, `DELETED_TASK`, `UPDATED_TASK`, `ADDED_PERSON_EMAIL`, `DELETED_PERSON_EMAIL`, `UPDATED_PERSON_EMAIL`, `ADDED_PERSON_PHONE`, `DELETED_PERSON_PHONE`, `UPDATED_PERSON_PHONE`, `UPDATED_PIPELINE_ITEM_PRICE`, `UPDATED_DEAL_PRICE`, `UPDATED_SELLER_MOTIVATION`, `UPDATED_TIME_FRAME`, `CREATED_IMPORTANT_NOTE`, `DELETED_IMPORTANT_NOTE`, `UPDATED_IMPORTANT_NOTE`, `AI_EMPLOYEE_ATTACHED`, `AI_EMPLOYEE_REMOVED`, `AI_EMPLOYEE_RAN`, `AI_EMPLOYEE_ESCALATED`, `AI_EMPLOYEE_SCHEDULE_CHANGED`, `TAG_UPDATED`, `LIST_UPDATED`, `CSV_ROW_IMPORTED`, `DIRECT_MAIL`, `EMAIL_OUTREACH`, `CONTRACT`, `SLACK`, `META_LEAD_FORM_SUBMITTED`, `PROPERTY_VISITED`)
- `direction`: `ActivityDirection` (one of `INBOUND`, `OUTBOUND`)

## Response (data, an example)

```json
{
  "contactQuery": {
    "contact": {
      "id": "<ID>",
      "activities": [
        {
          "id": "<ID>",
          "type": "NOTE_CREATED",
          "createdAt": "2026-01-01T00:00:00.000Z",
          "isFromTeamMember": true,
          "createdByUser": {
            "id": "<ID>",
            "firstName": "…",
            "lastName": "…"
          },
          "note": {
            "id": "<ID>",
            "body": "…",
            "createdAt": "2026-01-01T00:00:00.000Z",
            "author": {
              "kind": "USER",
              "id": "<ID>",
              "name": "…"
            }
          },
          "call": {
            "id": "<ID>",
            "direction": "INBOUND",
            "durationSeconds": 0,
            "status": "RINGING",
            "summary": "…",
            "recordingUrl": "…",
            "disposition": "Connected",
            "dispositionNote": "…",
            "fromPhoneNumber": "…",
            "toPhoneNumber": "…",
            "createdAt": "2026-01-01T00:00:00.000Z"
          },
          "text": {
            "id": "<ID>",
            "message": "…",
            "direction": "INBOUND",
            "fromPhoneNumber": "…",
            "toPhoneNumber": "…",
            "createdAt": "2026-01-01T00:00:00.000Z"
          },
          "email": {
            "id": "<ID>",
            "subject": "…",
            "snippet": "…",
            "textExtract": "…",
            "fromEmail": "…",
            "fromName": "…",
            "toEmails": [
              "…"
            ],
            "direction": "…",
            "internalDate": "2026-01-01T00:00:00.000Z"
          },
          "metadata": {
            "taskId": "…",
            "oldValues": {
              "title": "…",
              "endDate": "…",
              "timezone": "…",
              "taskType": "…",
              "participants": [
                "…"
              ]
            },
            "newValues": {
              "endDate": "…"
            }
          }
        }
      ]
    }
  }
}
```

## Example

```bash
curl https://server.goliathdata.com/api/v1/call \
  -H "Authorization: Bearer $GOLIATH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operationId": "listContactActivities",
    "variables": {
      "contactId": "<contactId>"
    }
  }'
```
