Contact messages cover two surfaces: a **contact form** endpoint for visitors to submit messages, and a **customer inbox** for store owners to browse, read, and reply to those messages.

The inbox merges two sources: **email messages** (sent and received via Resend) and **contact-form submissions** (from the storefront contact form). Every message in the list and detail responses includes a `source` field – `"email"` or `"contact"` – so you can distinguish them. Contact-form messages are always `direction: "inbound"` and have `to`, `cc`, `bcc`, `replyTo`, `subject`, `html`, and `reason` set to `null`.

## Create Contact Message

```
POST /api/v1/contact-messages
```

Saves a contact form message and notifies the merchant by email. The email notification is sent in the background, so the response returns as soon as the message is stored.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `email` | `string` | Yes | Sender email address |
| `message` | `string` | Yes | Message content (1-5000 characters) |
| `routingKey` | `string` | No | Opaque label assigned by the storefront form (max 100 chars). The merchant maps labels to recipient mailboxes via `settings.contactRoutingEmails`; unmatched or missing keys fall back to `notificationEmail`. |

```bash
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane@example.com", "message": "Do you ship internationally?", "routingKey": "sales"}' \
  https://your-store.yns.store/api/v1/contact-messages
```

### Response (201)

```json
{
  "id": "0191abc0-1234-7def-8000-000000000001",
  "storeId": "0191abc0-0000-7000-8000-000000000abc",
  "email": "jane@example.com",
  "message": "Do you ship internationally?",
  "readAt": null,
  "createdAt": "2024-06-15T10:30:00.000Z",
  "updatedAt": "2024-06-15T10:30:00.000Z"
}
```

A `400` is returned with `{ error, details }` when the email is malformed or the message is empty or over 5000 characters.

---

## List Messages

```
GET /api/v1/messages
```

Returns a paginated list of inbound and outbound messages in the store's inbox.

### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `limit` | `number` | 20 | Messages per page (1-100) |
| `offset` | `number` | 0 | Messages to skip |
| `query` | `string` | – | Search by sender address or subject |
| `filter` | `string` | `all` | Message status: `all`, `unread`, or `read` |
| `direction` | `string` | – | Filter by `inbound` or `outbound` |

```bash
curl -H "Authorization: Bearer your_api_key" \
  "https://your-store.yns.store/api/v1/messages?filter=unread&limit=10"
```

### Response (200)

```json
{
  "data": [
    {
      "id": "0191abc0-1234-7def-8000-000000000001",
      "source": "email",
      "direction": "inbound",
      "from": "jane@example.com",
      "to": "store@yns.store",
      "cc": null,
      "bcc": null,
      "replyTo": null,
      "subject": "Shipping question",
      "text": "Do you ship internationally?",
      "html": null,
      "reason": null,
      "readAt": null,
      "createdAt": "2024-06-15T10:30:00.000Z",
      "updatedAt": "2024-06-15T10:30:00.000Z"
    },
    {
      "id": "0191abc0-5678-7def-8000-000000000002",
      "source": "contact",
      "direction": "inbound",
      "from": "alex@example.com",
      "to": null,
      "cc": null,
      "bcc": null,
      "replyTo": null,
      "subject": null,
      "text": "I'd like to place a bulk order.",
      "html": null,
      "reason": null,
      "readAt": null,
      "createdAt": "2024-06-15T09:15:00.000Z",
      "updatedAt": "2024-06-15T09:15:00.000Z"
    }
  ],
  "meta": {
    "count": 2,
    "offset": 0,
    "limit": 20
  }
}
```

When `direction=outbound` is set, contact-form messages are excluded automatically since they are always inbound.

---

## Get Message

```
GET /api/v1/messages/:id
```

Returns a single message by UUID.

```bash
curl -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/messages/0191abc0-1234-7def-8000-000000000001
```

Returns `404` if the message does not exist.

---

## Mark as Read

```
POST /api/v1/messages/:id/read
```

Marks an inbox message as read (sets `readAt` to the current time). Returns the updated message.

```bash
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/messages/0191abc0-1234-7def-8000-000000000001/read
```

---

## Reply to Message

```
POST /api/v1/messages/:id/reply
```

Sends a reply email to the customer and records the outbound message in the inbox. Works for both email and contact-form messages – for contact-form submissions the default subject is `"Re: your message"`. Only inbound messages can be replied to.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `body` | `string` | Yes | Plain-text reply (1-50000 chars). Blank lines become paragraphs. |
| `subject` | `string` | No | Override subject line (defaults to `Re: <original subject>`) |

```bash
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"body": "Yes, we ship to over 50 countries!"}' \
  https://your-store.yns.store/api/v1/messages/0191abc0-1234-7def-8000-000000000001/reply
```

### Response (201)

```json
{
  "ok": true,
  "sent": {
    "to": "jane@example.com",
    "subject": "Re: Shipping question",
    "emailId": "msg_abc123"
  }
}
```

| Status | Description |
|--------|-------------|
| `409` | Cannot reply to outbound messages |
| `502` | Email delivery failed |

---

## Delete Message

```
DELETE /api/v1/messages/:id
```

Permanently deletes a message from the inbox.

```bash
curl -X DELETE \
  -H "Authorization: Bearer your_api_key" \
  https://your-store.yns.store/api/v1/messages/0191abc0-1234-7def-8000-000000000001
```

### Response (200)

```json
{
  "ok": true,
  "deleted": 1
}
```