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

# get_attendee_touch_status

> Whether anyone followed up with the attendees of one event, read from CRM activity after the event ended.

> [!NOTE]
>
> Read-only. It never writes to the CRM, never enriches and never ranks attendees.

The marketer's question is "did anyone follow up after the event?" Reach for this tool when the user asks who was contacted, who on the handoff list is still untouched, or whether follow-up happened at all.

Follows the [heyBTW MCP rules](https://heybtw.com/docs/mcp/rules.md).

## When to use it

* "Did sales follow up with the Austin dinner attendees?"
* "Who from the qualified list is still untouched a week later?"
* "Give me the untouched attendees so I can ping their owners."
* "How many people did we reach in the first 14 days?"

## Parameters

| Parameter | Type | Required | Notes |
| - | - | - | - |
| `channel_id` | string | One of `channel_id` or `event_name` | One of this workspace's own collabs, from list\_events. |
| `event_name` | string | See above | An ambiguous name returns no rows and lists the candidates. |
| `window_days` | integer | No | Follow-up window in calendar days after the event day. Default 7, from 1 to 90. |
| `qualified_list_id` | string | No | A `list_id` from get\_lists whose basis is this collab, used as the qualified set instead of the default (an MQL or an attendee with an open opportunity). |
| `limit` | integer | No | Attendees per page. Default and maximum 200. |
| `cursor` | string | No | `metadata.pagination.next_cursor`. Pass it with the SAME `channel_id`, `window_days` and `qualified_list_id`. Expires after 15 minutes. |
| `contract_version` | string | No | Omit for v1. Only v1 is advertised. |

> [!TIP]
>
> When the user wants the summary, use the default page size. CRM activity is read only for the attendees on the current page (`metadata.pagination.activity_read_page_only: true`); attendees on other pages count as `unresolved` in the summary until their page is read. A small `limit` leaves most of the summary unresolved.

## Response

`crm_context` comes first. Then `data[0]`:

| Field | Meaning |
| - | - |
| `collab` | `channel_id` and `event_name`. |
| `window` | `window_days`, `opens_at` (when the event ended), `event_date`, `last_day` (in the collab's zone), `timezone`, `days_since_event`, `state` (`open` or `closed`). |
| `qualified_set` | The default set (`source: "default"`, `rule`) or the List named by `qualified_list_id`. |
| `summary` | Counts over ALL attendees: `total`, `touched`, `untouched`, `unknown_no_crm_match`, `unresolved`; the same for `qualified`; `qualification_not_evaluable`. |
| `activity_source` | `provider`, `activity_types` read, `sequence_sends_distinguishable`. |
| `resolved_at`, `degraded` | When this page was read, and whether the read was cut short. |
| `attendees[]` | One page in email order: `contact.contact_email`, `touch_state`, `first_touch_at`, `touch_source`, `inferred` (always true), `days_since_event`, `qualified` (true, false, or null when not evaluable), and `recorded_follow_ups` when any exist. |

### Touch states

| `touch_state` | Meaning |
| - | - |
| `touched` | The CRM shows a counted activity in the window; `first_touch_at` and `touch_source` name the first. |
| `untouched` | A CRM contact with no counted activity of the types read, in the window. |
| `unknown_no_crm_match` | No CRM contact has this exact email. Nothing is known. |
| `unresolved` | The CRM or activity read for this attendee did not complete. |

`unknown_no_crm_match` and `unresolved` are never untouched. A type missing from `activity_source.activity_types` was not read, so `untouched` means no counted activity of the types that were read.

`recorded_follow_ups` (each `observation_id`, `at`, `channel`, `source`, at most 10, with `recorded_follow_ups_total` beyond that) lists follow-ups recorded with record\_observation inside the window. When a CRM connected in heyBTW serves the answer, they are supporting evidence only and never change a state or a count.

A real response from the Austin dinner fixture at the default page size, trimmed to two attendees and two coverage notes:

```json theme={null}
{
  "tool": "get_attendee_touch_status",
  "crm_context": {
    "connection_path": "dashboard",
    "provider": "attio",
    "status": "connected",
    "capability": "activity_read",
    "capability_served": true,
    "crm_joined": true,
    "crm_join_key": ["email", "company_domain"]
  },
  "data": [
    {
      "collab": { "channel_id": "cmvifxsyp000j07l2hmp06n1u", "event_name": "ScaleUp.ai Field Dinner: Austin" },
      "window": {
        "window_days": 7,
        "opens_at": "2026-09-28T02:30:00.000Z",
        "event_date": "2026-09-27",
        "last_day": "2026-10-04",
        "timezone": "America/Chicago",
        "days_since_event": 37,
        "state": "closed"
      },
      "qualified_set": { "source": "default", "rule": "is_mql_or_open_opportunity" },
      "summary": {
        "total": 28,
        "touched": 0,
        "untouched": 25,
        "unknown_no_crm_match": 3,
        "unresolved": 0,
        "qualified": { "total": 6, "touched": 0, "untouched": 6, "unknown_no_crm_match": 0, "unresolved": 0 },
        "qualification_not_evaluable": 0
      },
      "activity_source": { "provider": "attio", "activity_types": ["meeting", "note"], "sequence_sends_distinguishable": false },
      "resolved_at": "2026-11-03T19:38:28.837Z",
      "degraded": false,
      "attendees": [
        {
          "contact": { "contact_email": "cole.okafor@presidiolending.com" },
          "touch_state": "untouched",
          "first_touch_at": null,
          "touch_source": null,
          "inferred": true,
          "days_since_event": 37,
          "qualified": true,
          "recorded_follow_ups": [
            { "observation_id": "cmvig4qu7001u08kvnuedj3i3", "at": "2026-09-29T16:00:00.000Z", "channel": "email", "source": "manual" }
          ]
        },
        {
          "contact": { "contact_email": "freya.petrov@ceruleanstream.com" },
          "touch_state": "unknown_no_crm_match",
          "first_touch_at": null,
          "touch_source": null,
          "inferred": true,
          "days_since_event": 37,
          "qualified": false
        }
      ]
    }
  ],
  "metadata": {
    "coverage_notes": [
      "Emails and calls are not read from Attio, so a follow-up by email or call is not seen, and untouched means no counted meeting or note in the window.",
      "To return inside the client timeout, Attio activity was read only for the attendees on this page. Attendees on other pages are counted as unresolved in the summary until their page is read: they are not untouched. Request a smaller limit for a faster read."
    ],
    "pagination": { "limit": 200, "returned": 28, "has_more": false, "next_cursor": null, "activity_read_page_only": true }
  },
  "status": "ok",
  "gaps": []
}
```

The same call with `limit: 3` returned `unresolved: 23` and an `upstream_crm_error` gap (retryable), because only the three attendees on that page were read.

### When no CRM in heyBTW serves activity

When no CRM is connected in heyBTW, it must be reconnected, or heyBTW cannot read activity from it, the answer is `status: "partial"`, never an error, with `crm_context.capability_served: false`. This shape is from the contract (`attendee-touch-status-contract.json`), not captured today, because the heyBTW workspace has a CRM connected:

| Field | In this shape |
| - | - |
| `attendees[]` | Join keys for your own CRM connector: `contact.contact_email`, `company_domain`, `name`, `attendance_status`, `checked_in`, plus `touch_state`, `first_touch_at`, `touch_source`, `inferred`, `days_since_event`, `qualified` (null). |
| `touch_state` | `touched` (with `touch_source: "agent_reported"`) only for an attendee with a follow-up recorded with record\_observation inside the window; `not_evaluated` for everyone else. |
| `summary` | `total`, `touched`, `not_evaluated` are counts. `untouched` and the other CRM counts are null, never 0. |
| `activity_source`, `qualified_set` | Null. |

Then: check follow-up in the user's own CRM connector for each `not_evaluated` attendee, joined on `contact_email` and `company_domain` (an outbound email, call, meeting held or note after `window.opens_at` and on or before `window.last_day`, never an inbound reply); record each one with [record\_observation](https://heybtw.com/docs/tools/record-observation.md) (kind `followed_up`, `source: "agent_crm_read"`); report what the CRM shows. The next call returns those as touched.

## Rules the agent must follow

* Never report `unknown_no_crm_match`, `unresolved` or `not_evaluated` attendees as not followed up.
* Every touch is inferred from logged CRM activity. Say what the CRM shows, not that someone was contacted.
* An inbound email from the attendee does not count; a sequence email does, because the CRM does not mark one.
* A touch is evidence of contact, never a commitment. Report it separately from [get\_due\_tasks](https://heybtw.com/docs/tools/get-due-tasks.md).
* A gap or a failed read is never an all-untouched list. Relay `coverage_notes` as written.

## Errors and gaps

| Code | When | What to do |
| - | - | - |
| `touch_status_unavailable` | The message names the gap: `no_crm_activity_source` (a sample collab), `no_event_date`, `event_not_ended` (with when the window opens). | Say the reason; for `event_not_ended`, come back after the event. |
| `upstream_crm_error` | The CRM read did not complete for some or all attendees. | Retryable. Treat unresolved as unknown. Retry in a few minutes. |
| `entity_not_found` | Not one of this workspace's own collabs. | Check list\_events. |
| `invalid_parameter` | A malformed argument or cursor, or a `qualified_list_id` with no usable List built on this collab. | Fix it. |
| `rate_limit_exceeded`, `internal_error` | | Honor Retry-After; retry once on internal\_error. |

## Related

* [record\_observation](https://heybtw.com/docs/tools/record-observation.md): record follow-ups you find in the user's CRM.
* [get\_due\_tasks](https://heybtw.com/docs/tools/get-due-tasks.md): committed follow-up tasks.
* [get\_event\_details](https://heybtw.com/docs/tools/get-event-details.md): the roster.
* Playbooks: [Follow-up and ROI](https://heybtw.com/docs/playbooks/follow-up-and-roi.md), [What each system knows](https://heybtw.com/docs/playbooks/what-each-system-knows.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.