Newsletters are email campaigns broadcast to your subscribers. A newsletter starts as a **draft**, can be edited freely, and is sent in the background once you call the send endpoint. Its `status` moves through `draft` → `sending` → `sent`, or `failed` if delivery breaks down. Newsletters can also be `scheduled` for a future send time. Failed newsletters can be reset to draft and retried. Manage the subscriber list itself via the [Subscribers API](/docs/api-reference/subscribers).

## List Newsletters

```
GET /api/v1/newsletters
```

Returns newsletter campaigns, newest first.

### Query Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `limit` | `number` | 50 | Newsletters per page (1-100) |
| `offset` | `number` | 0 | Newsletters to skip |
| `status` | `string` | – | Filter by status: `draft`, `scheduled`, `sending`, `sent`, or `failed` |

```bash
curl \
  -H "Authorization: Bearer your_api_key" \
  "https://your-store.yns.store/api/v1/newsletters?status=sent"
```

### Response (200)

```json
{
  "data": [
    {
      "id": "0191abc0-1234-7def-8000-000000000001",
      "subject": "Spring Sale – 20% off everything",
      "content": "Our spring sale is live. Use code SPRING20 at checkout.",
      "status": "sent",
      "audience": "all_active",
      "products": [],
      "scheduledAt": null,
      "recipientCount": 1240,
      "sentAt": "2024-06-15T12:00:00.000Z",
      "failedAt": null,
      "errorMessage": null,
      "createdAt": "2024-06-14T09:00:00.000Z",
      "updatedAt": "2024-06-15T12:00:00.000Z"
    }
  ],
  "meta": {
    "count": 1,
    "offset": 0,
    "limit": 50
  }
}
```

---

## Create Newsletter

```
POST /api/v1/newsletters
```

Creates a draft campaign. Use the [send endpoint](#send-newsletter) to broadcast it once you're ready.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `subject` | `string` | Yes | Email subject line |
| `content` | `string` | Yes | Plain-text email body |
| `audience` | `string` | No | Preset recipient audience, resolved live at send time. One of: `all_active` (default), `customers`, `repeat_customers`, `never_purchased`, `recent_signups` |
| `products` | `array` | No | Featured product cards rendered in the email, in display order (max 6). Each entry is an object with `productId` (required, UUID) and optional `variantId` (UUID – defaults to cheapest variant). Prices are resolved at send time. |

```bash
curl -X POST \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "Spring Sale – 20% off everything",
    "content": "Our spring sale is live. Use code SPRING20 at checkout.",
    "audience": "customers",
    "products": [
      { "productId": "0191abc0-1234-7def-8000-000000000010" },
      { "productId": "0191abc0-1234-7def-8000-000000000011", "variantId": "0191abc0-1234-7def-8000-000000000020" }
    ]
  }' \
  https://your-store.yns.store/api/v1/newsletters
```

### Response (201)

```json
{
  "id": "0191abc0-1234-7def-8000-000000000001",
  "subject": "Spring Sale – 20% off everything",
  "content": "Our spring sale is live. Use code SPRING20 at checkout.",
  "status": "draft",
  "audience": "customers",
  "products": [
    { "productId": "0191abc0-1234-7def-8000-000000000010" },
    { "productId": "0191abc0-1234-7def-8000-000000000011", "variantId": "0191abc0-1234-7def-8000-000000000020" }
  ],
  "scheduledAt": null,
  "recipientCount": 0,
  "sentAt": null,
  "failedAt": null,
  "errorMessage": null,
  "createdAt": "2024-06-14T09:00:00.000Z",
  "updatedAt": "2024-06-14T09:00:00.000Z"
}
```

---

## Get Newsletter

```
GET /api/v1/newsletters/{id}
```

Returns a single newsletter by UUID, including `status`, `recipientCount`, and the sent/failed timestamps.

### Response (200)

```json
{
  "id": "0191abc0-1234-7def-8000-000000000001",
  "subject": "Spring Sale – 20% off everything",
  "content": "Our spring sale is live. Use code SPRING20 at checkout.",
  "status": "draft",
  "audience": "all_active",
  "products": [],
  "scheduledAt": null,
  "recipientCount": 0,
  "sentAt": null,
  "failedAt": null,
  "errorMessage": null,
  "createdAt": "2024-06-14T09:00:00.000Z",
  "updatedAt": "2024-06-14T09:00:00.000Z"
}
```

### Not Found (404)

```json
{
  "error": "Newsletter not found"
}
```

---

## Update Newsletter

```
PATCH /api/v1/newsletters/{id}
```

Edits the subject and/or content of a **draft** newsletter. Only drafts can be edited – editing a newsletter in any other status returns `409`.

### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `subject` | `string` | No | Email subject line |
| `content` | `string` | No | Plain-text email body |

```bash
curl -X PATCH \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"subject": "Spring Sale – 25% off this weekend"}' \
  https://your-store.yns.store/api/v1/newsletters/0191abc0-1234-7def-8000-000000000001
```

### Response (200)

Returns the updated newsletter.

### Not a Draft (409)

```json
{
  "error": "Only draft newsletters can be edited"
}
```

---

## Delete Newsletter

```
DELETE /api/v1/newsletters/{id}
```

Deletes a **draft** newsletter by UUID. Only drafts can be deleted – deleting a newsletter in any other status returns `409`.

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

### Response (200)

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

### Not a Draft (409)

```json
{
  "error": "Only draft newsletters can be deleted"
}
```

---

## Send Newsletter

```
POST /api/v1/newsletters/{id}/send
```

Broadcasts the draft to all active subscribers in the background. Before dispatching, YNS validates several preconditions: a verified sending email domain with outbound email enabled, a configured store address, at least one active subscriber, and remaining capacity in your monthly email quota. If any check fails the send is rejected with the appropriate status (`400`, `403`, `404`, or `409`) and no emails go out.

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

### Response (202)

```json
{
  "success": true,
  "recipientCount": 1240,
  "message": "Newsletter send started; emails are sending in the background."
}
```

### Errors

| Status | Meaning |
|--------|---------|
| `400` | Precondition not met (email config, store address, or no active subscribers) |
| `403` | Monthly email quota would be exceeded |
| `404` | Newsletter not found |
| `409` | Newsletter is not a draft |

---

## Retry Newsletter

```
POST /api/v1/newsletters/{id}/retry
```

Resets a **failed** newsletter back to draft so it can be edited and re-sent. Returns `409` if the newsletter is not in the `failed` status.

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

### Response (200)

Returns the newsletter reset to `draft` status.

```json
{
  "id": "0191abc0-1234-7def-8000-000000000001",
  "subject": "Spring Sale – 20% off everything",
  "content": "Our spring sale is live. Use code SPRING20 at checkout.",
  "status": "draft",
  "audience": "all_active",
  "products": [],
  "scheduledAt": null,
  "recipientCount": 0,
  "sentAt": null,
  "failedAt": null,
  "errorMessage": null,
  "createdAt": "2024-06-14T09:00:00.000Z",
  "updatedAt": "2024-06-15T13:00:00.000Z"
}
```

### Not Failed (409)

```json
{
  "error": "Only failed newsletters can be retried"
}
```