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

# List events for a lead

> List interaction events for a lead — messages, calls, emails, tool calls, custom events, compliance blocks, and other types documented in this reference.

Apten may add new `type` values over time without a version bump or prior notice. Clients should treat unknown types as forward-compatible (skip or store opaquely) unless they filter with the `types` query parameter.


## Incremental sync

Pair this endpoint with [`GET /leads`](/api-reference/leads/list):

1. Poll `GET /leads?updatedSince=…` for leads whose profile or activity changed.
2. When `lastActivityAt` moves forward, call `GET /leads/{leadId}/events?since=…` for that lead.
3. Paginate with `nextToken` until it is `null`.

Events are ordered by `updatedAt` ascending. Use the largest `updatedAt` from the page as the next `since` cursor for that lead.

## Event types and forward compatibility

The `type` field identifies each event (`MESSAGE`, `CALL`, `EMAIL`, and others documented in the API reference). **Apten may add new event types at any time** as the product evolves. We do not treat new types as a breaking API change and do not guarantee advance notice before they appear in production responses.

Build integrations to be forward-compatible:

* **Prefer an explicit `types` filter** when you only need a fixed set of shapes for your pipeline.
* **Otherwise, tolerate unknown `type` values** — persist the raw event, skip it, or route it to a generic handler. Do not fail the entire sync or reject the payload because `type` is unrecognized.
* **Use `type` plus `id`** as the stable key; do not assume every event is a message, call, or email.

The OpenAPI schema lists event types Apten documents today; it is not an exhaustive guarantee of all types you may receive over time.

## Compliance blocks (`COMPLIANCE_BLOCKED`)

When [external compliance](/compliance/external-checks) is enabled and an outbound SMS, email, or voice attempt is **suppressed**, Apten writes a `COMPLIANCE_BLOCKED` room event. It appears on this endpoint like any other interaction event.

| Expectation                        | Detail                                                                                                             |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| No outbound message/call/email row | A blocked send does **not** create a `MESSAGE`, `EMAIL`, or `CALL` for that attempt — only `COMPLIANCE_BLOCKED`.   |
| Allowed checks are omitted         | Pass results are stored in Apten's internal audit log only; they are **not** returned by this API.                 |
| Filter                             | `?types=COMPLIANCE_BLOCKED` (or include it in a comma-separated list).                                             |
| PII                                | `data` includes channel, reason, source, and optional `triggeredBy` — not phone, email, or full provider payloads. |

Example event:

```json theme={null}
{
  "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "roomId": "room_abc",
  "orgId": "org_123",
  "type": "COMPLIANCE_BLOCKED",
  "timestamp": "2026-05-20T05:50:12.000Z",
  "updatedAt": "2026-05-20T05:50:12.000Z",
  "data": {
    "channel": "sms",
    "reason": "STATE_DNC",
    "source": "http",
    "triggeredBy": "llm"
  }
}
```

The event `id` is the stable identifier for the block (same UUID as the internal audit row). `data` does not repeat it — same pattern as `MESSAGE` / `CALL` / `EMAIL`.

`reason` is either an Apten system code (e.g. `COMPLIANCE_TARGET_MISSING`, `DNC`) or a string returned by your compliance HTTP API when `allowed` is `false`. `source` indicates which check leg blocked (`http`, `apten-dnc`, `configuration`, `provider-error`, etc.).

Treat `COMPLIANCE_BLOCKED` in your warehouse as **suppressed outreach** — not a delivery failure on an existing message.

## Call events and enrichment

`CALL` events include direction, status, duration, and transfer flags — but **not** the transcript or AI summary. Webhook payloads that include `callId` behave the same way (except `call.completed`, which may include a short `summary`).

To fetch the full transcript and call metadata, call [`GET /calls/{callId}`](/api-reference/calls/get-call) using the event's top-level `id` (for CALL events) or `data.callId` (for webhook payloads). Call recordings are not available via the public API today.


## OpenAPI

````yaml get /leads/{leadId}/events
openapi: 3.0.1
info:
  title: AttentPublicAPI
  version: '2024-05-17T21:09:13Z'
servers:
  - url: https://api.attent.app/v1
security: []
paths:
  /leads/{leadId}/events:
    get:
      tags:
        - Events
      description: >
        List interaction events for a lead — messages, calls, emails, tool
        calls, custom events, compliance blocks, and other types documented in
        this reference.


        Apten may add new `type` values over time without a version bump or
        prior notice. Clients should treat unknown types as forward-compatible
        (skip or store opaquely) unless they filter with the `types` query
        parameter.
      operationId: listLeadEvents
      parameters:
        - name: leadId
          in: path
          required: true
          schema:
            type: string
          description: The lead (room) ID to fetch events for.
        - name: since
          in: query
          required: true
          schema:
            type: string
            format: date-time
          description: >-
            ISO 8601 timestamp. Returns events whose `updatedAt` is greater than
            or equal to this value.
        - name: types
          in: query
          required: false
          schema:
            type: string
            example: MESSAGE,COMPLIANCE_BLOCKED
          description: >
            Comma-separated list of event types to include. Documented values
            include `MESSAGE`, `CALL`, `EMAIL`, `TOOL_CALL`, `CUSTOM`, and
            `COMPLIANCE_BLOCKED`. Use this filter when your integration only
            handles a known set of types. Defaults to all types currently stored
            for the lead (including types not listed here). When supplied, a
            page may return fewer than `limit` items even if more matching
            events exist — always paginate using `nextToken`.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 100
          description: >-
            Maximum number of events to return per page. Defaults to 100, max
            1000.
        - name: nextToken
          in: query
          required: false
          schema:
            type: string
          description: >-
            Opaque cursor from a prior response. Pass it back to fetch the next
            page. `null` in the response means you have reached the end; a
            non-null value means more pages exist (true regardless of how many
            items the current page returned).
      responses:
        '200':
          description: A page of events ordered by `updatedAt` ascending.
          content:
            application/json:
              schema:
                type: object
                properties:
                  events:
                    type: array
                    items:
                      $ref: '#/components/schemas/interactionEvent'
                  nextToken:
                    type: string
                    nullable: true
                    description: >-
                      Cursor for the next page. `null` if there are no more
                      results.
              example:
                events:
                  - id: msg_abc
                    roomId: room_abc
                    orgId: org_123
                    type: MESSAGE
                    timestamp: '2026-05-14T15:00:00.000Z'
                    updatedAt: '2026-05-14T15:00:00.000Z'
                    data:
                      direction: INBOUND
                      content:
                        text: Hi, I am interested
                      isAiGenerated: false
                      deliveryChannel: SMS
                  - id: f47ac10b-58cc-4372-a567-0e02b2c3d479
                    roomId: room_abc
                    orgId: org_123
                    type: COMPLIANCE_BLOCKED
                    timestamp: '2026-05-20T05:50:12.000Z'
                    updatedAt: '2026-05-20T05:50:12.000Z'
                    data:
                      channel: sms
                      reason: STATE_DNC
                      source: http
                      triggeredBy: llm
                nextToken: null
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          description: >-
            Lead not found, or the lead has been deleted. Deleted leads surface
            in `GET /leads` with `deleted: true` — drop all events for them in
            your sync target.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                example:
                  message: Lead not found.
        '500':
          $ref: '#/components/responses/500'
      security:
        - api_key: []
components:
  schemas:
    interactionEvent:
      description: >
        An interaction event between a lead and your organization. The `type`
        discriminator determines the shape of `data`.


        New interaction event types may be added in the future. Integrations
        must accept responses containing undocumented `type` values or restrict
        ingestion with the `types` query parameter on `GET
        /leads/{leadId}/events`.
      oneOf:
        - $ref: '#/components/schemas/messageInteractionEvent'
        - $ref: '#/components/schemas/callInteractionEvent'
        - $ref: '#/components/schemas/emailInteractionEvent'
        - $ref: '#/components/schemas/toolCallInteractionEvent'
        - $ref: '#/components/schemas/customInteractionEvent'
        - $ref: '#/components/schemas/complianceBlockedInteractionEvent'
      discriminator:
        propertyName: type
        mapping:
          MESSAGE:
            $ref: '#/components/schemas/messageInteractionEvent'
          CALL:
            $ref: '#/components/schemas/callInteractionEvent'
          EMAIL:
            $ref: '#/components/schemas/emailInteractionEvent'
          TOOL_CALL:
            $ref: '#/components/schemas/toolCallInteractionEvent'
          CUSTOM:
            $ref: '#/components/schemas/customInteractionEvent'
          COMPLIANCE_BLOCKED:
            $ref: '#/components/schemas/complianceBlockedInteractionEvent'
    messageInteractionEvent:
      allOf:
        - $ref: '#/components/schemas/baseInteractionEvent'
        - type: object
          properties:
            type:
              type: string
              enum:
                - MESSAGE
            data:
              $ref: '#/components/schemas/messageEventData'
          required:
            - type
            - data
    callInteractionEvent:
      allOf:
        - $ref: '#/components/schemas/baseInteractionEvent'
        - type: object
          properties:
            type:
              type: string
              enum:
                - CALL
            data:
              $ref: '#/components/schemas/callEventData'
          required:
            - type
            - data
    emailInteractionEvent:
      allOf:
        - $ref: '#/components/schemas/baseInteractionEvent'
        - type: object
          properties:
            type:
              type: string
              enum:
                - EMAIL
            data:
              $ref: '#/components/schemas/emailEventData'
          required:
            - type
            - data
    toolCallInteractionEvent:
      allOf:
        - $ref: '#/components/schemas/baseInteractionEvent'
        - type: object
          properties:
            type:
              type: string
              enum:
                - TOOL_CALL
            data:
              $ref: '#/components/schemas/toolCallEventData'
          required:
            - type
            - data
    customInteractionEvent:
      allOf:
        - $ref: '#/components/schemas/baseInteractionEvent'
        - type: object
          properties:
            type:
              type: string
              enum:
                - CUSTOM
            data:
              $ref: '#/components/schemas/customEventData'
          required:
            - type
            - data
    complianceBlockedInteractionEvent:
      allOf:
        - $ref: '#/components/schemas/baseInteractionEvent'
        - type: object
          properties:
            type:
              type: string
              enum:
                - COMPLIANCE_BLOCKED
            data:
              $ref: '#/components/schemas/complianceBlockedEventData'
          required:
            - type
            - data
    baseInteractionEvent:
      type: object
      properties:
        id:
          type: string
          description: >-
            Stable unique identifier for the event (e.g. `msg_abc`). Unique on
            its own — pair with `type` if you need to disambiguate.
        roomId:
          type: string
          description: ID of the lead this event belongs to.
        orgId:
          type: string
          description: Your organization ID.
        timestamp:
          type: string
          format: date-time
          description: When the event occurred. Immutable.
        updatedAt:
          type: string
          format: date-time
          description: >-
            Last time the event row was written (creation, edit, or
            soft-delete). Equal to `timestamp` for events that have never
            changed.
      required:
        - id
        - roomId
        - orgId
        - timestamp
        - updatedAt
    messageEventData:
      type: object
      description: Payload for a MESSAGE event (SMS, iMessage, or similar text channel).
      properties:
        direction:
          type: string
          enum:
            - INBOUND
            - OUTBOUND
          description: >-
            `INBOUND` if the message was sent by the lead; `OUTBOUND` if sent by
            your AI or team.
        content:
          type: object
          properties:
            text:
              type: string
              description: The message body.
          required:
            - text
        isAiGenerated:
          type: boolean
          description: >-
            `true` for messages drafted by Apten AI; `false` for human-sent or
            inbound messages.
        deliveryChannel:
          type: string
          description: >-
            Channel the message was delivered over (e.g. `SMS`). Absent until
            the message is sent.
        sentAt:
          type: string
          format: date-time
          description: When the message left our system. OUTBOUND only.
        deliveredAt:
          type: string
          format: date-time
          description: When the carrier confirmed delivery. OUTBOUND only, if reported.
        readAt:
          type: string
          format: date-time
          description: When the recipient read the message. OUTBOUND only, if reported.
        guardrailTriggered:
          type: boolean
          description: '`true` if a content guardrail blocked or flagged this message.'
        violatedGuardrails:
          type: array
          items:
            type: string
          description: Names of guardrails this message violated, if any.
        followUpStrategyId:
          type: string
          description: ID of the follow-up strategy that triggered this message, if any.
      required:
        - direction
        - content
        - isAiGenerated
    callEventData:
      type: object
      description: >-
        Payload for a CALL event. Written when the call reaches a terminal
        state. Does not include transcript or summary — use `GET
        /calls/{callId}` to fetch those.
      properties:
        direction:
          type: string
          enum:
            - INBOUND
            - OUTBOUND
        callStatus:
          type: string
          description: >-
            Final status of the call (e.g. `COMPLETE`, `NO_ANSWER`, `BUSY`,
            `FAILED`).
        durationSeconds:
          type: integer
          description: Length of the call in seconds.
        isTransfer:
          type: boolean
          description: '`true` if this was a transfer to a human agent.'
        midCallTransfer:
          type: boolean
          description: >-
            `true` if a transfer happened mid-call (vs. a pure transfer from the
            start).
        endedBy:
          type: string
          enum:
            - LEAD
            - AGENT
            - SYSTEM
            - UNKNOWN
          description: Who ended the call.
        hasVoicemail:
          type: boolean
          description: '`true` if the call left a voicemail.'
        transferAnswerStatus:
          type: string
          enum:
            - answered
            - no_answer
            - pending
          description: >-
            Whether the transfer recipient picked up. Only set for transfer
            calls.
        isFollowUp:
          type: boolean
          description: '`true` if this call was placed as part of a follow-up strategy.'
        isAiInitiated:
          type: boolean
          description: '`true` if Apten AI initiated this call (vs. a human agent).'
      required:
        - direction
        - callStatus
    emailEventData:
      type: object
      description: Payload for an EMAIL event.
      properties:
        direction:
          type: string
          enum:
            - INBOUND
            - OUTBOUND
        subject:
          type: string
        fromAddress:
          type: string
        toAddresses:
          type: array
          items:
            type: string
        plainTextBody:
          type: string
          description: Plain-text body of the email.
        hasAttachments:
          type: boolean
        attachmentMetadata:
          type: array
          items:
            type: object
            description: >-
              Per-attachment metadata (filename, content type, size). Does not
              contain the file bytes.
        guardrailTriggered:
          type: boolean
        violatedGuardrails:
          type: array
          items:
            type: string
        followUpStrategyId:
          type: string
      required:
        - direction
        - hasAttachments
    toolCallEventData:
      type: object
      description: >-
        Payload for a TOOL_CALL event — recorded when Apten AI invokes one of
        your configured tools during a conversation.
      properties:
        toolId:
          type: string
          description: Identifier of the tool that was invoked.
        toolInput:
          type: object
          description: Arguments the AI passed to the tool. Free-form, tool-specific.
        toolResponse:
          type: object
          description: Response the tool returned. Free-form, tool-specific.
      required:
        - toolId
    customEventData:
      type: object
      description: >-
        Payload for a CUSTOM event — created by your application via `POST
        /leads/{leadId}/addEvent`.
      properties:
        text:
          type: string
          description: The free-form text supplied when the event was created.
      required:
        - text
    complianceBlockedEventData:
      type: object
      description: >
        Payload for a `COMPLIANCE_BLOCKED` event. Emitted when Apten suppresses
        an outbound SMS, email, or voice attempt because Apten-managed DNC or an
        external compliance API check did not allow the send. There is no
        corresponding `MESSAGE`, `EMAIL`, or `CALL` event for the suppressed
        attempt.


        Allowed checks are not exported — only blocks appear on this endpoint.
        Use the top-level event `id` to correlate with Apten support or audit
        exports. For configuration and middleware contract details, see
        [External Compliance Checks](/compliance/external-checks).
      properties:
        channel:
          type: string
          enum:
            - sms
            - voice
            - email
          description: Outbound channel that was blocked.
        reason:
          type: string
          description: >
            Machine-readable block reason. Common Apten codes include `DNC`
            (internal list), `COMPLIANCE_TARGET_MISSING`,
            `COMPLIANCE_PROVIDER_ERROR_FAIL_CLOSED`,
            `COMPLIANCE_PROVIDER_ERROR_FAIL_OPEN`, and configuration errors such
            as `COMPLIANCE_HTTP_ENDPOINT_MISSING`. When your HTTP compliance API
            returns `{ "allowed": false }`, `reason` is whatever your API
            returned (e.g. a suppression code from your middleware).
        source:
          type: string
          description: >
            Which leg of the compliance stack produced the block — e.g. `http`
            (external API), `apten-dnc`, `configuration`, or `provider-error`.
        triggeredBy:
          type: string
          description: >-
            Optional label for the Apten code path that initiated the check
            (e.g. `llm`, `send-email`, `queue-call`).
      required:
        - channel
        - source
  responses:
    '400':
      description: Bad Request
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
            example:
              message: 'Invalid format for field: phone'
    '401':
      description: Unauthorized - Invalid or missing API key
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
            example:
              message: Unauthorized
    '403':
      description: Forbidden API key
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
            example:
              message: Forbidden
    '500':
      description: Internal Server Error
      content:
        application/json:
          schema:
            type: object
            properties:
              message:
                type: string
            example:
              message: Internal Server Error
  securitySchemes:
    api_key:
      type: apiKey
      name: x-api-key
      in: header

````