# List outbound messages

Source: https://kirora.io/docs/api/reference/listMessages

`GET /v1/messages`

Requires an API key with the `messages:read` scope. [About keys and scopes](https://kirora.io/docs/api/reference/authentication)

Works with integration, agent and partner keys.

Lists the workspace message log: one row per outbound message from campaigns, journeys and transactional sends, newest first, whether it was sent, is still queued, or was skipped or failed. `campaign` and `journey` say where a row came from (neither, for a transactional send), and `bounce`, `failure` or `skip` what became of it.

**Filters.** `recipient` matches the address a message was sent to, and also email messages not yet given one (queued, or skipped before sending) whose contact's current email is that address. Messages past the retention period name no one and match no `recipient`. `query` is a search: part of the recipient's address or name, matched against the contact each message went to. `campaignId` and `journeyId` narrow to one source, and `stepId` with `journeyId` to one step of the journey, in every version of its steps. `templateId` and `correlationId` narrow to transactional sends, from one stored template or carrying your own reference. `opened` and `clicked` split by engagement. `createdAfter` is inclusive and `createdBefore` exclusive. No total is returned.

## Query parameters

- `limit` (integer, optional, min > 0, max 100, default `20`) — How many items a page holds: 1 to 100, 20 by default.
- `cursor` (string, optional, 1–2,048 characters) — Where the page starts: the previous page's `meta.pagination.nextCursor`. Omit it for the first page, and send the same filters with it.
- `status` (string, optional, one of `queued`, `sending`, `skipped`, `sent`, `failed`, `delivered`, `bounced`, `complained`) — Only messages in this status.
- `channel` (string, optional, one of `email`, `push`) — Only messages on this channel.
- `recipient` (string, optional) — The exact address the message went to.
- `query` (string, optional, 1–100 characters) — Part of the recipient's email address or name, matched against the contact the message went to.
- `messageType` (string, optional, one of `marketing`, `transactional`) — Only messages of this type: `marketing` or `transactional`.
- `campaignId` (string, optional, uuid) — Only messages from this campaign.
- `journeyId` (string, optional, uuid) — Only messages from this journey.
- `stepId` (string, optional, pattern `^[a-z0-9][a-z0-9_-]{0,63}$`) — With `journeyId`, only messages from the journey's step with this id, in every version of its steps.
- `templateId` (string, optional, uuid) — Only transactional sends from this stored template; no other message records one.
- `correlationId` (string, optional, 1–255 characters) — Only the transactional sends that carried this `correlationId`.
- `sent` (string, optional, one of `true`, `false`) — `true`: handed to the provider, whatever happened after; `false`: never sent (queued, failed before sending or skipped).
- `delivered` (string, optional, one of `true`, `false`) — `true`: delivery was confirmed, even if a complaint or bounce came later; `false`: never confirmed.
- `bounced` (string, optional, one of `true`, `false`) — `true`: a hard bounce was recorded, even if a complaint came later; `false`: none. A test send's acted-out bounce is no recorded bounce.
- `complained` (string, optional, one of `true`, `false`) — `true`: a complaint was recorded; `false`: none. A test send's acted-out complaint is no recorded complaint.
- `opened` (string, optional, one of `true`, `false`) — `true`: opened (or clicked) at least once; `false`: never.
- `clicked` (string, optional, one of `true`, `false`) — `true`: a link clicked at least once; `false`: never.
- `createdAfter` (string, optional, date-time) — Only messages accepted at or after this time (inclusive).
- `createdBefore` (string, optional, date-time) — Only messages accepted before this time (exclusive).

## Header parameters

- `Kirora-Workspace` (string, optional, pattern `^[A-Za-z0-9._:-]{1,128}$`) — Partner keys only: the child workspace to act in, by the external id the partner gave it. Without it, a partner key acts in its own workspace. Any other key that sends it is refused with 403, and an unknown workspace is 404.

## Returns

Returns `200 OK` with a list of `OutboundMessage` objects in `data`, and the next page's cursor in `meta` (see [pagination](https://kirora.io/docs/api/reference/pagination)). Otherwise returns an error; [Errors](https://kirora.io/docs/api/reference/errors) lists every code and which to retry.

- `id` (string, required, uuid)
- `messageType` (string, required, one of `marketing`, `transactional`) — `marketing` (from `campaign` or `journey`) or `transactional` (neither).
- `channel` (string, required, one of `email`, `push`)
- `status` (string, required, one of `queued`, `sending`, `skipped`, `sent`, `failed`, `delivered`, `bounced`, `complained`)
- `campaign` (object | null, required) — The campaign it came from; null for any other message.
  - `id` (string, required, uuid)
  - `name` (string, required) — The campaign's current name.
- `journey` (object | null, required) — The journey it came from, deleted or not; null for any other message.
  - `id` (string | null, required, uuid) — Null once the journey is deleted.
  - `name` (string | null, required) — The journey's current name; null once it is deleted.
  - `deleted` (boolean, required) — The journey was deleted after sending this. Its messages and their counts stay; nothing names the journey any more.
  - `enrollmentId` (string, required, uuid) — The enrollment that sent it.
  - `stepId` (string, required) — The id of the step that sent it.
- `template` (object | null, required) — The stored template and published version it was sent from; null for inline content.
  - `id` (string, required, uuid)
  - `version` (integer, required, min > 0)
- `copy` (object | null, required) — Set on a `cc` or `bcc` copy; null on the message itself.
  - `kind` (string, required, one of `cc`, `bcc`)
  - `of` (string, required, uuid) — The message it is a copy of.
- `contact` (object | null, required) — Who it was for; null when the contact was deleted, or the message passed the retention period: it no longer names anyone.
  - `id` (string, required, uuid)
  - `externalId` (string | null, required)
  - `email` (string | null, required) — The contact's email now, which may differ from `recipientEmail`.
  - `firstName` (string | null, required) — The contact's first name now.
  - `lastName` (string | null, required) — The contact's last name now.
- `recipientEmail` (string | null, required) — The email address the message went to, frozen at the first attempt; null until then, and for push.
- `simulated` (boolean, required) — Sent to a reserved test address (`example.com` and the like): it passed every check a real send does but was never handed to the provider. Any outcome was simulated from the address, as `bounce@example.com` simulates a bounce.
- `correlationId` (string | null, required) — The reference a transactional send carried; null otherwise.
- `sendAt` (string | null, required, date-time) — When a timed transactional send is due; null for one sent at once.
- `sentAt` (string | null, required, date-time) — When it was handed to the provider.
- `deliveredAt` (string | null, required, date-time) — When the provider confirmed delivery.
- `openedAt` (string | null, required, date-time) — First open, when tracked: a person's first open or click, or a machine's open while only a machine has opened it.
- `openedByMachine` (boolean, required) — Only a machine has opened it so far: a mail app fetched its images with no reader (Apple Mail Privacy Protection, Gmail's prefetch on delivery) or a mail security scanner checked it. A click or a later open by a person clears it.
- `clickedAt` (string | null, required, date-time) — A person's first click, when tracked.
- `clickedByMachine` (boolean, required) — A machine followed one of its links: a mail security scanner checking it on delivery, a link preview. Such a click sets no `clickedAt` and sends no webhook.
- `complainedAt` (string | null, required, date-time) — When the recipient marked it as spam (the first complaint); null for a simulated one.
- `bounce` (object | null, required) — The hard bounce: set when `status` is `bounced`; null otherwise. A soft bounce is a `failure` with reason `transient_bounce`.
  - `occurredAt` (string, required, date-time) — When it happened.
  - `type` (string, required, one of `mailbox_not_found`, `mailbox_full`, `suppressed`, `general`) — Why: `mailbox_not_found`, `mailbox_full`, `suppressed` (the provider refuses the address), or `general` for any other reason.
  - `detail` (string | null, required) — The receiving server's reply, such as `smtp; 550 5.1.1 user unknown`; null for a simulated bounce.
- `failure` (object | null, required) — Set when `status` is `failed`; null otherwise.
  - `occurredAt` (string, required, date-time) — When it happened.
  - `reason` (string, required) — Why sending broke, such as `provider_rejected` (the provider refused the content) or `transient_bounce`. `not_sent`: a problem on Kirora's side stopped it; nothing in your content, audience or setup caused it, and Kirora is alerted. In rare cases the provider may have delivered it before the problem.
- `skip` (object | null, required) — Set when `status` is `skipped`; null otherwise.
  - `occurredAt` (string, required, date-time) — When it happened.
  - `reason` (string, required) — Which guard stopped it before sending, such as `suppressed`, `consent_missing` or `cancelled`; `fallback_not_needed`: a transactional `fallbackOrder` fallback whose first channel arrived; `plan_limit_reached`: a fallback the plan had no room for when it was due; `sending_review_required`: email from a sending domain Kirora has not approved yet.
  - `suppressionReason` (string | null, required, one of `manual`, `imported`, `unsubscribed`, `bounced`, `complained`, `complained_transactional`) — With `suppressed`: why the address is blocked: `unsubscribed`, `bounced`, `complained`, `complained_transactional`, `manual` or `imported`. Null otherwise.
- `createdAt` (string, required, date-time)

## Example

```bash
curl https://api.kirora.io/v1/messages \
  -H "Authorization: Bearer $KIRORA_API_KEY"
```

Response `200`:

```json
{
  "data": [
    {
      "id": "0192f3b0-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
      "messageType": "marketing",
      "channel": "email",
      "status": "delivered",
      "campaign": {
        "id": "0192f3d3-5e6f-7071-8c83-9e0f1a2b3c4d",
        "name": "October newsletter"
      },
      "journey": null,
      "template": null,
      "copy": null,
      "contact": {
        "id": "0192f3a1-7c2e-7d4b-9a10-3e5f6a7b8c9d",
        "externalId": "customer-4821",
        "email": "amaka@example.com",
        "firstName": "Amaka",
        "lastName": "Obi"
      },
      "recipientEmail": "amaka@example.com",
      "simulated": false,
      "correlationId": null,
      "sendAt": null,
      "sentAt": "2026-10-01T09:31:00.000Z",
      "deliveredAt": "2026-10-01T09:31:04.000Z",
      "openedAt": "2026-10-01T10:02:00.000Z",
      "openedByMachine": false,
      "clickedAt": null,
      "clickedByMachine": false,
      "complainedAt": null,
      "bounce": null,
      "failure": null,
      "skip": null,
      "createdAt": "2026-10-01T09:30:58.000Z"
    },
    {
      "id": "0192f3b0-4d5e-7f60-8b72-8d9e0f1a2b3c",
      "messageType": "transactional",
      "channel": "email",
      "status": "bounced",
      "campaign": null,
      "journey": null,
      "template": {
        "id": "0192f3f2-3c4d-7e5f-8061-7c8d9e0f1a2b",
        "version": 3
      },
      "copy": null,
      "contact": {
        "id": "0192f3a1-7c2e-7d4b-9a10-3e5f6a7b8c9d",
        "externalId": "customer-4821",
        "email": "amaka@example.com",
        "firstName": "Amaka",
        "lastName": "Obi"
      },
      "recipientEmail": "amaka@example.com",
      "simulated": false,
      "correlationId": "order-1001",
      "sendAt": null,
      "sentAt": "2026-10-01T09:31:00.000Z",
      "deliveredAt": null,
      "openedAt": null,
      "openedByMachine": false,
      "clickedAt": null,
      "clickedByMachine": false,
      "complainedAt": null,
      "bounce": {
        "occurredAt": "2026-10-01T09:31:05.000Z",
        "type": "mailbox_not_found",
        "detail": "smtp; 550 5.1.1 user unknown"
      },
      "failure": null,
      "skip": null,
      "createdAt": "2026-10-01T09:30:58.000Z"
    }
  ],
  "meta": {
    "pagination": {
      "limit": 20,
      "nextCursor": null
    }
  }
}
```
