## GET /api/v2/me/mail-relay/messages

**List outbound mail relay messages**

Lists the messages one sender passed through the outbound mail relay in the last 30 days, with how each delivery went. Choose the sender with `scope`. Use `hosting` with `domain` for a domain on your web hosting. Use `vps` with `vpsId` for a VPS that sends through relay.hostup.se. For a VPS, mail from all its public IP addresses is listed. Each row has the sender, recipients, subject, status and a timeline of the delivery. For counts per status, use `GET /api/v2/me/mail-relay/summary`.

### Related Endpoints

- `GET /api/v2/me/mail-relay/blocks`: List paused mail relay senders
- `GET /api/v2/me/mail-relay/summary`: Count outbound mail relay messages by status
- `POST /api/v2/me/mail-relay/blocks/unblock`: Lift a paused mail relay sender

### Headers

- `Authorization`: Bearer $HOSTUP_API_KEY
- Scope (one of): `read:hosting` (Only when `scope` is `hosting`.), `read:vm` (Only when `scope` is `vps`.)

### Parameters

- `scope` (query, string, required): Which sender to list. `hosting` takes `domain` and needs the scope `read:hosting`. `vps` takes `vpsId` and needs `read:vm`. Example: `hosting`
  Allowed values: hosting, vps
- `domain` (query, string): A domain on one of your web hosting accounts (main, addon, parked or subdomain). Required when `scope` is `hosting`. Example: `example.se`
- `vpsId` (query, string): The VPS ID (`vps_…`). Required when `scope` is `vps`. Example: `vps_01hxa3b4c5d6e7f8g9h0j1k2m3`
- `status` (query, string): Only messages in this status. The values are explained under `status` in the response. Example: `bounced`
  Allowed values: queued, sending, delivered, deferred, bounced, rejected
- `since` (query, string): Only messages the relay received at or after this time (ISO 8601). Default and earliest: 30 days ago. Example: `2026-10-01T00:00:00Z`
- `limit` (query, integer) [min: 1, max: 100]: Messages per page, 1 to 100. Default 20. A higher value returns 100. A value below 1 returns 400 `invalid_request`.
- `cursor` (query, string): The `nextCursor` from the previous page.

### Request Example

```bash
curl "https://cloud.hostup.se/api/v2/me/mail-relay/messages?scope=hosting" \
  -H "Authorization: Bearer $HOSTUP_API_KEY"
```

### Response Schema

- `data` (array<object>, required)
- `data[].id` (string, required): Message ID in the relay's log. Example: `3f0c1d2e-9b7a-4c55-8e21-0c4a5b6d7e8f`
- `data[].receivedAt` (string, required): When the relay accepted the message from your server. Example: `2026-10-08T09:58:12.000Z`
- `data[].from` (string, required, nullable): The sender address. Example: `info@example.se`
- `data[].to` (array<string>, required): The recipient addresses. Example: `["kund@example.com"]`
- `data[].subject` (string, required, nullable): The subject line. Example: `Faktura 1234`
- `data[].status` (string, required): `queued`: waiting at the relay, also while the sender is paused. `sending`: on its way out. `delivered`: the receiving server accepted it. `deferred`: the receiving server asked to try again later. `bounced`: the receiving server refused it or delivery was given up. `rejected`: the relay refused it. Example: `delivered`
  Allowed values: queued, sending, delivered, deferred, bounced, rejected
- `data[].lastReply` (string, required, nullable): The receiving server's last reply, for example on a deferred or bounced message. Example: `null`
- `data[].deliveredAt` (string, required, nullable): When the receiving server accepted the message. Example: `2026-10-08T09:58:20.000Z`
- `data[].rejectedReason` (string, required, nullable): Why the relay refused the message. Only set when `status` is `rejected`. Example: `null`
- `data[].verdictCategory` (string, required, nullable): The content classification, for example `clean`, `spam`, `phishing`, `scam` or `malware`. Example: `clean`
- `data[].reviewState` (string, required, nullable): On a rejected message, the result of the second review: `confirmed` (the rejection stands), `overturned` (it was reversed) or `pending` (not reviewed yet). Null on other statuses. Example: `null`
  Allowed values: confirmed, overturned, pending
- `data[].guard` (string, required, nullable): Name of the relay rule that decided how a flagged message was handled. Informational only. Example: `null`
- `data[].events` (array<object>, required): What happened to the message, step by step.
- `data[].events[].at` (string, required): When the step happened. Example: `2026-10-08T09:58:20.000Z`
- `data[].events[].kind` (string, required): What happened, for example `accepted`, `queued`, `sent`, `deferred`, `bounced` or `rejected`. Example: `sent`
- `data[].events[].detail` (string, required, nullable): The receiving server's reply or the relay's note for this step. Example: `250 2.0.0 OK`
- `hasMore` (boolean, required): True when there is another page. Example: `false`
- `nextCursor` (string, required, nullable): Send as `cursor` to get the next page. Null on the last page. Example: `null`

### Responses

#### 200 - One page of messages, newest first.
```json
{
  "data": [
    {
      "id": "3f0c1d2e-9b7a-4c55-8e21-0c4a5b6d7e8f",
      "receivedAt": "2026-10-08T09:58:12.000Z",
      "from": "info@example.se",
      "to": [
        "kund@example.com"
      ],
      "subject": "Faktura 1234",
      "status": "delivered",
      "lastReply": null,
      "deliveredAt": "2026-10-08T09:58:20.000Z",
      "rejectedReason": null,
      "verdictCategory": "clean",
      "reviewState": null,
      "guard": null,
      "events": [
        {
          "at": "2026-10-08T09:58:12.000Z",
          "kind": "accepted",
          "detail": null
        },
        {
          "at": "2026-10-08T09:58:20.000Z",
          "kind": "sent",
          "detail": "250 2.0.0 OK"
        }
      ]
    },
    {
      "id": "8a1b2c3d-4e5f-4a6b-9c7d-0e1f2a3b4c5d",
      "receivedAt": "2026-10-07T14:21:03.000Z",
      "from": "info@example.se",
      "to": [
        "old.address@example.com"
      ],
      "subject": "Orderbekräftelse",
      "status": "bounced",
      "lastReply": "550 5.1.1 The email account that you tried to reach does not exist",
      "deliveredAt": null,
      "rejectedReason": null,
      "verdictCategory": "clean",
      "reviewState": null,
      "guard": null,
      "events": [
        {
          "at": "2026-10-07T14:21:03.000Z",
          "kind": "accepted",
          "detail": null
        },
        {
          "at": "2026-10-07T14:21:09.000Z",
          "kind": "bounced",
          "detail": "550 5.1.1 The email account that you tried to reach does not exist"
        }
      ]
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

#### 400 - `invalid_request`: the query is wrong. For example `scope` is missing or unknown, `domain` or `vpsId` is missing for the chosen scope, `limit` is below 1 or not a number, `cursor` is longer than 512 characters, or `status` or `since` is not a valid value. Each entry in `errors` names the parameter.
```json
{
  "type": "https://developer.hostup.se/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "`limit` must be an integer between 1 and 100.",
  "code": "invalid_request",
  "instance": "/api/v2/me/mail-relay/messages",
  "errors": [
    {
      "pointer": "/limit",
      "detail": "`limit` must be an integer between 1 and 100.",
      "code": "invalid_pagination_limit"
    }
  ]
}
```

#### 401 - Unauthorized. Authentication is required.
```json
{
  "type": "https://developer.hostup.se/errors/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication is required.",
  "code": "unauthorized",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 403 - Forbidden. The caller lacks a required scope or does not own the resource.
```json
{
  "type": "https://developer.hostup.se/errors/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "The caller lacks a required scope or does not own the resource.",
  "code": "forbidden",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 404 - `domain_not_found`: the domain is not on any of your web hosting accounts. `vps_not_found`: the VPS is not on your account.

#### 429 - Rate limited. Retry after the limit resets. 429 responses include `Retry-After` seconds plus `X-RateLimit-*` headers.
```json
{
  "type": "https://developer.hostup.se/errors/rate_limit_exceeded",
  "title": "Too many requests",
  "status": 429,
  "detail": "Rate limit exceeded. Please try again later.",
  "code": "rate_limit_exceeded",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 500 - Internal error. Retry later or contact support if the issue persists.
```json
{
  "type": "https://developer.hostup.se/errors/internal_error",
  "title": "Internal server error",
  "status": 500,
  "detail": "An unexpected error occurred. Retry later or contact support if the issue persists.",
  "code": "internal_error",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 503 - `upstream_unavailable`: the message log did not answer within 5 seconds. Try again after the `Retry-After` seconds (30).
```json
{
  "type": "https://developer.hostup.se/errors/upstream_unavailable",
  "title": "Upstream unavailable",
  "status": 503,
  "detail": "The message history could not be loaded right now. Try again shortly.",
  "code": "upstream_unavailable",
  "instance": "/api/v2/me/mail-relay/messages",
  "requestId": "7f3e9c0a-2b1d-4e5f-8a6b-9c0d1e2f3a4b",
  "timestamp": "2026-10-08T10:00:00.000Z"
}
```
