---
title: "Create a campaign"
method: POST
path: "/campaigns"
tags: ["Campaigns"]
---

# Create a campaign

`POST /campaigns`

## Request

- Base URL: `https://app.lettr.com/api`
- URL: `https://app.lettr.com/api/campaigns`
- Auth: HTTP bearer

Creates a draft campaign. Give the content as `html_content` (optionally with the editor's `json_content`) or as a `template_id`, whose HTML is copied onto the campaign. Every list, segment, topic and template must belong to your team. The sender domain is checked when the campaign is sent, not here; language override senders are checked here and must use a verified sending domain. Send or schedule the draft with the send and schedule endpoints. Requires the `campaigns:write` scope. Not available to sandbox keys.

## Request body

- StoreCampaignRequest
  - `name` string: Campaign name. Required unless `subject` is given, in which case the subject is used.
  - `subject` string, nullable
  - `from_email` string, email, nullable
  - `from_name` string, nullable
  - `reply_to` string, email, nullable
  - `html_content` string, nullable: Email HTML. Merge tags such as `{{first_name}}` are filled from contact properties. Cannot be combined with `template_id`; required when `json_content` is given.
  - `json_content` object, nullable: Editor JSON stored next to `html_content` so the campaign can be opened in the editor.
  - `template_id` integer, nullable: Template from one of your projects. Its HTML is copied onto the campaign. Cannot be combined with `html_content`.
  - `audience_config` object, nullable: Who receives the campaign. Lists and segments are combined (a contact in any of them receives it); `topic_id` further limits recipients to contacts subscribed to that topic. With no lists or segments, every subscribed contact receives it.
    - `all_contacts` boolean, nullable
    - `list_ids` string[], nullable
    - `segment_ids` string[], nullable
    - `topic_id` string, nullable
  - `language_overrides` object, nullable: Per-language sender and subject overrides, keyed by language code.

## Response `201`

Campaign created successfully.

- CreateCampaignResponse
  - `message` string, required
  - `data` CampaignDetail, required: Full campaign representation including rendered HTML content.
    - `id` string, uuid, required
    - `name` string, required
    - `subject` string, nullable: Email subject line
    - `from_email` string, email, nullable: Sender email address
    - `from_name` string, nullable: Sender display name
    - `reply_to` string, email, nullable: Reply-to address
    - `status` 'draft' | 'scheduled' | 'preparing' | 'in_review' | 'sending' | 'sent' | 'failed', required
    - `scheduled_at` string, date-time, nullable: Scheduled delivery time (ISO 8601)
    - `total_recipients` integer, nullable: Total recipients resolved for the send
    - `sent_count` integer, required
    - `sent_at` string, date-time, nullable: When the campaign finished sending (ISO 8601)
    - `created_at` string, date-time, required
    - `stats` CampaignStats, required: Aggregated engagement statistics for a campaign.
      - `injections` integer, required
      - `deliveries` integer, required
      - `bounces` integer, required
      - `spam_complaints` integer, required
      - `opens` integer, required
      - `unique_opens` integer, required
      - `clicks` integer, required
      - `unique_clicks` integer, required
      - `unsubscribes` integer, required
    - `html_content` string, nullable: Rendered HTML content of the campaign email
    - `language_overrides` object: Per-language subject and sender overrides for a multi-language template, keyed by the template's language key. Absent fields fall back to the campaign's primary values.

## Other responses

- `401`: Unauthorized - Missing or invalid API key
- `403`: Forbidden - API key does not have the required scope
- `422`: Validation error. Also returned when a list, segment, topic or template is not in your team, when both `html_content` and `template_id` are given, or when a language override sender uses an unverified domain.
- `500`: Internal server error
- `503`: Service unavailable - the API key could not be verified, so the request was not processed. This does not mean the key is invalid; retry after the delay given in the Retry-After header.

---

[API](https://skmtc.dev/lettr/apis/lettr-api.md) · [All operations](https://skmtc.dev/lettr/apis/lettr-api/llms.txt) · [OpenAPI document](https://skmtc.dev/lettr/apis/lettr-api/revisions/48ef0dac1ebd?raw)
