---
title: "List scheduled, sent, and failed messages"
method: GET
path: "/discord/messages"
tags: ["Discord"]
---

# List scheduled, sent, and failed messages

`GET /discord/messages`

## Request

- Base URL: `https://api.reacherapp.com/public/v1`
- URL: `https://api.reacherapp.com/public/v1/discord/messages`
- Auth: API key in header `x-api-key` and API key in header `x-shop-id`

Discord messages this shop has scheduled or posted through Reacher, with their engagement (`reaction_count`, `reply_count`, `audience_count`) once delivered. `counts` totals each tab. Single shop only.

## Query parameters

- `tab` 'scheduled' | 'sent' | 'failed': Which set to return.
- `page` integer: Page number (1-indexed).
- `page_size` integer: Rows per page (max 100).

## Response `200`

Successful Response

- DiscordMessageListResponse
  - `data` DiscordMessage[]
    - `id` integer, required
    - `channel_id` string, required
    - `channel_name` string, nullable
    - `title` string, nullable
    - `content` string, required
    - `mention` MentionTarget: Who the announcement pings. `role` requires an explicit role_id.
      - `type` 'none' | 'here' | 'everyone' | 'role'
      - `role_id` string, nullable
    - `campaign_id` integer, nullable
    - `attachments` DiscordAttachment[]
      - `filename` string, required
      - `content_type` string, required
      - `size_bytes` integer, required
    - `sender` SenderIdentity: Who the announcement appears to come from. The post is always made by the Reacher webhook; `member` only overrides the webhook's display name + avatar for that one message (the APP badge stays). `brand` = the shop name, which is also what a NULL sender means on the row. On a request only `user_id` is trusted: the controller resolves it against the linked guild and takes `display_name` / `avatar_url` from Discord, so a caller cannot persist a fabricated identity.
      - `type` 'brand' | 'member'
      - `user_id` string, nullable
      - `display_name` string, nullable
      - `avatar_url` string, nullable
    - `status` string, required: `scheduled`, `sending`, `sent`, `failed`, or `canceled`.
    - `scheduled_for` string, nullable
    - `timezone` string, required
    - `recurrence` Recurrence: The parsed ``recurrence`` JSONB column / API contract. Parsed ONCE at the query-layer boundary (``parse_recurrence``) so a malformed shape 400s at create time and can never blow up mid-dispatch. ``paused`` deliberately lives in its own column (the claim predicate must match the partial index literally), so extra keys are ignored rather than rejected — legacy rows carrying ``paused`` in the blob still parse.
      - `frequency` 'daily' | 'weekdays' | 'weekly' | 'biweekly' | 'monthly', required
      - `end_type` 'never' | 'on' | 'after'
      - `until` string, date, nullable
      - `occurrences` integer, nullable
    - `paused` boolean
    - `series_id` integer, nullable: Set on an occurrence of a recurring series.
    - `occurrence_num` integer, nullable
    - `occurrences_sent` integer
    - `discord_message_id` string, nullable
    - `message_url` string, nullable
    - `sent_at` string, nullable
    - `failure_reason` string, nullable
    - `failure_code` string, nullable
    - `audience_count` integer, nullable
    - `reaction_count` integer, nullable
    - `reply_count` integer, nullable
    - `link_click_count` integer, nullable
    - `upcoming_occurrences` string[], nullable: Next fire times, on a recurring series' detail read.
    - `created_at` string, nullable, required
    - `updated_at` string, nullable, required
  - `counts` DiscordMessageCounts
    - `scheduled` integer
    - `sent` integer
    - `failed` integer
  - `pagination` PublicApiCorePaginationPaginationMeta, required: Pagination metadata returned in responses.
    - `total_count` integer, required
    - `page` integer, required
    - `page_size` integer, required
    - `total_pages` integer, required

## Other responses

- `422`: Validation Error

## Changes

> 76 revisions in range; 1 not diffed.

- **2026-09-19** `291230805105`: 4 breaking
  - response property `data/items/created_at` list-of-types was widened by adding types `null` to media type `application/json` of response `200`
  - response property `data/items/updated_at` list-of-types was widened by adding types `null` to media type `application/json` of response `200`
  - the `data/items/scheduled_for/anyOf[subschema #1]/` response's property format changed from `date-time` to no format for status `200`
  - the `data/items/sent_at/anyOf[subschema #1]/` response's property format changed from `date-time` to no format for status `200`
- **2026-09-10** `7dad11366a32`: 1 info
  - added the optional property `data/items/sender` to the response with the `200` status
- **2026-09-05** `643422c93bd8`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/reacherapp/apis/reacher-data-api/changes/discord/messages/get.md)

---

[API](https://skmtc.dev/reacherapp/apis/reacher-data-api.md) - [All operations](https://skmtc.dev/reacherapp/apis/reacher-data-api/llms.txt) - [OpenAPI document](https://skmtc.dev/reacherapp/apis/reacher-data-api/revisions/63ce74b30b5a?raw)
