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

# List campaign engagement events

> Returns engagement events (opens, clicks, bounces, etc.) for a campaign, with optional filtering by type, recipient email, and date range. Uses cursor-based pagination: keep requesting with the returned `next_cursor` until it is `null`. When a filter is applied, a page may come back with an empty `events` array **and** a non-null `next_cursor` — that means more pages remain, not that there are no matching events, so continue paginating until `next_cursor` is `null`. Requires the `campaigns:read` scope.

Returns engagement events (opens, clicks, bounces, etc.) for a campaign, with optional filtering by type, recipient email, and date range. Uses cursor-based pagination: keep requesting with the returned `next_cursor` until it is `null`. When a filter is applied, a page may come back with an empty `events` array **and** a non-null `next_cursor` — that means more pages remain, not that there are no matching events, so continue paginating until `next_cursor` is `null`. Requires the `campaigns:read` scope.


## OpenAPI

````yaml GET /campaigns/{campaignId}/events
openapi: 3.1.0
info:
  title: Lettr API
  version: 1.0.0
  description: >-
    Lettr Email API - Send transactional emails with tracking, attachments, and
    personalization.
  contact:
    name: Lettr Support
    url: https://lettr.com
  license:
    name: Proprietary
    url: https://lettr.com/terms
servers:
  - url: https://app.lettr.com/api
    description: Production
security: []
tags:
  - name: Emails
    description: Email sending operations
  - name: Templates
    description: Email template management operations
  - name: Domains
    description: Domain management operations
  - name: Webhooks
    description: Webhook management operations
  - name: Audience
    description: 'Audience management: lists, contacts, topics, properties, and segments'
  - name: Campaigns
    description: >-
      Campaign operations: listing, stats, engagement events, and
      dispatch/scheduling
paths:
  /campaigns/{campaignId}/events:
    get:
      tags:
        - Campaigns
      summary: List campaign engagement events
      description: >-
        Returns engagement events (opens, clicks, bounces, etc.) for a campaign,
        with optional filtering by type, recipient email, and date range. Uses
        cursor-based pagination: keep requesting with the returned `next_cursor`
        until it is `null`. When a filter is applied, a page may come back with
        an empty `events` array **and** a non-null `next_cursor` — that means
        more pages remain, not that there are no matching events, so continue
        paginating until `next_cursor` is `null`. Requires the `campaigns:read`
        scope.
      operationId: listCampaignEvents
      parameters:
        - name: campaignId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Campaign ID
          example: 0193e6a8-1f3a-7c2a-b9e2-1aa1d2e5d3f0
        - name: event_type
          in: query
          required: false
          schema:
            type: string
            enum:
              - injection
              - delivery
              - bounce
              - spam_complaint
              - open
              - click
              - list_unsubscribe
          description: Filter by event type
        - name: email
          in: query
          required: false
          schema:
            type: string
            format: email
          description: Filter by recipient email
        - name: start_date
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: >-
            Only events at or after this time (ISO 8601). A date-only value
            (e.g. `2026-05-01`) is treated as the start of that day in UTC.
        - name: end_date
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: >-
            Only events at or before this time (ISO 8601), inclusive. A
            date-only value (e.g. `2026-05-01`) covers the whole of that day in
            UTC (through 23:59:59).
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
          description: Max events per page (max 100)
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Pagination cursor from a previous response
      responses:
        '200':
          description: Campaign events retrieved successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListCampaignEventsResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                message: Campaign not found.
                error_code: not_found
        '422':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'
      security:
        - bearerAuth: []
components:
  schemas:
    ListCampaignEventsResponse:
      type: object
      required:
        - message
        - data
      properties:
        message:
          type: string
          example: Campaign events retrieved successfully.
        data:
          type: object
          required:
            - events
            - next_cursor
          properties:
            events:
              type: array
              items:
                $ref: '#/components/schemas/CampaignEvent'
            next_cursor:
              type:
                - string
                - 'null'
              description: >-
                Opaque cursor for the next page; null when there are no more
                events
    ErrorResponse:
      type: object
      required:
        - message
        - error_code
      description: Error response for non-validation errors (400, 409, 500, 502).
      properties:
        message:
          type: string
          description: Human-readable error message
          example: The sender domain could not be determined from the email address.
        error_code:
          $ref: '#/components/schemas/ErrorCode'
    ValidationErrorResponse:
      type: object
      required:
        - message
        - error_code
        - errors
      properties:
        message:
          type: string
          description: Human-readable error message
          example: Validation failed.
        error_code:
          type: string
          description: Error code (always `validation_error` for 422 responses)
          const: validation_error
          example: validation_error
        errors:
          type: object
          description: Field-specific validation errors
          additionalProperties:
            type: array
            items:
              type: string
          example:
            from:
              - The sender email address is required.
            to:
              - At least one recipient email address is required.
    CampaignEvent:
      type: object
      description: A single campaign engagement event.
      required:
        - event_id
        - event_type
        - email
        - timestamp
      properties:
        event_id:
          type: string
          example: '92356829'
        event_type:
          type: string
          enum:
            - injection
            - delivery
            - bounce
            - spam_complaint
            - open
            - click
            - list_unsubscribe
          example: open
        email:
          type: string
          format: email
          example: jane@example.com
        timestamp:
          type: string
          format: date-time
          example: '2026-05-01T12:30:00+00:00'
        bounce_class:
          type:
            - string
            - 'null'
          description: SparkPost bounce classification code
        reason:
          type:
            - string
            - 'null'
          description: Failure or bounce reason
        target_link_url:
          type:
            - string
            - 'null'
          description: Clicked link URL (click events only)
        user_agent:
          type:
            - string
            - 'null'
          description: Recipient user agent (open/click events)
    UnauthorizedResponse:
      type: object
      required:
        - message
      description: Error response for authentication failures (401).
      properties:
        message:
          type: string
          description: Human-readable error message
          example: API key is required.
    ErrorCode:
      type: string
      description: >-
        Error codes returned by the API.


        | Code | HTTP Status | Description |

        |------|-------------|-------------|

        | `validation_error` | 422 | Request validation failed. Check the
        `errors` object for field-specific messages. |

        | `invalid_domain` | 400 | The sender domain could not be determined
        from the email address. |

        | `unconfigured_domain` | 400 | The sender domain is not configured or
        approved for sending. |

        | `send_error` | 400, 500 | General error during email send preparation
        or domain registration. |

        | `retrieval_error` | 400, 500 | Failed to retrieve resources (e.g.,
        sandbox key without associated user, upstream error). |

        | `transmission_failed` | 502 | Email transmission to the upstream
        provider failed. |

        | `resource_already_exists` | 409 | The resource (e.g., domain) already
        exists. |

        | `not_found` | 404 | The specified resource was not found. |

        | `template_not_found` | 404 | The specified template, project, or
        template version was not found. |

        | `insufficient_scope` | 403 | The API key does not have the required
        scope for this endpoint. |

        | `schedule_cancellation_failed` | 409, 500 | Could not cancel the
        scheduled transmission (already sent or upstream error). |

        | `quota_exceeded` | 429 | Monthly sending quota exceeded. Upgrade your
        plan to continue sending. |

        | `daily_quota_exceeded` | 429 | Daily sending quota exceeded. Try again
        tomorrow. |

        | `campaign_not_sendable` | 422 | The campaign cannot be sent in its
        current state (not a draft, or missing subject/sender/content). |

        | `campaign_not_scheduled` | 422 | The campaign is not scheduled, so it
        cannot be unscheduled. |
      enum:
        - validation_error
        - invalid_domain
        - unconfigured_domain
        - send_error
        - retrieval_error
        - transmission_failed
        - resource_already_exists
        - not_found
        - template_not_found
        - insufficient_scope
        - schedule_cancellation_failed
        - quota_exceeded
        - daily_quota_exceeded
        - campaign_not_sendable
        - campaign_not_scheduled
      example: invalid_domain
  responses:
    UnauthorizedError:
      description: Unauthorized - Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UnauthorizedResponse'
          examples:
            missing_key:
              summary: Missing API key
              value:
                message: API key is required.
            invalid_key:
              summary: Invalid API key
              value:
                message: Invalid API key.
    ForbiddenError:
      description: Forbidden - API key does not have the required scope
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            message: >-
              Your API key does not have the required permissions for this
              action.
            error_code: insufficient_scope
    ServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            message: An unexpected error occurred. Please try again later.
            error_code: send_error
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key for authentication

````