> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lettr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a campaign

> 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.

Creates a draft campaign from `html_content` or a `template_id`. Send it with [send a campaign now](/api-reference/campaigns/send-a-campaign-now) or [schedule a campaign](/api-reference/campaigns/schedule-a-campaign). Requires the `campaigns:write` scope. Not available to sandbox keys.


## OpenAPI

````yaml POST /campaigns
openapi: 3.1.0
info:
  title: Lettr API
  version: 1.0.0
  description: >-
    Lettr Email API - Send transactional emails with tracking, attachments, and
    personalization.
  contact:
    name: Lettr Support
    url: https://lettr.com
  license:
    name: Proprietary
    url: https://lettr.com/terms
servers:
  - url: https://app.lettr.com/api
    description: Production
security: []
tags:
  - name: Emails
    description: Email sending operations
  - name: Templates
    description: Email template management operations
  - name: Domains
    description: Domain management operations
  - name: Webhooks
    description: Webhook management operations
  - name: Audience
    description: 'Audience management: lists, contacts, topics, properties, and segments'
  - name: Campaigns
    description: >-
      Campaign operations: listing, stats, engagement events, and
      dispatch/scheduling
paths:
  /campaigns:
    post:
      tags:
        - Campaigns
      summary: Create a campaign
      description: >-
        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.
      operationId: createCampaign
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StoreCampaignRequest'
      responses:
        '201':
          description: Campaign created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateCampaignResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '422':
          description: >-
            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.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorResponse'
        '500':
          $ref: '#/components/responses/ServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailableError'
      security:
        - bearerAuth: []
components:
  schemas:
    StoreCampaignRequest:
      type: object
      properties:
        name:
          type: string
          maxLength: 255
          description: >-
            Campaign name. Required unless `subject` is given, in which case the
            subject is used.
          example: October newsletter
        subject:
          type: string
          nullable: true
          maxLength: 998
          example: News for October
        from_email:
          type: string
          format: email
          nullable: true
          maxLength: 255
          example: news@example.com
        from_name:
          type: string
          nullable: true
          maxLength: 255
          example: Example
        reply_to:
          type: string
          format: email
          nullable: true
          maxLength: 255
        html_content:
          type: string
          nullable: true
          description: >-
            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.
          example: <p>Hello {{first_name}}</p>
        json_content:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Editor JSON stored next to `html_content` so the campaign can be
            opened in the editor.
        template_id:
          type: integer
          nullable: true
          description: >-
            Template from one of your projects. Its HTML is copied onto the
            campaign. Cannot be combined with `html_content`.
        audience_config:
          type: object
          nullable: true
          description: >-
            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.
          additionalProperties: false
          properties:
            all_contacts:
              type: boolean
              nullable: true
            list_ids:
              type: array
              nullable: true
              items:
                type: string
            segment_ids:
              type: array
              nullable: true
              items:
                type: string
            topic_id:
              type: string
              nullable: true
        language_overrides:
          type: object
          nullable: true
          description: Per-language sender and subject overrides, keyed by language code.
          additionalProperties:
            type: object
            properties:
              subject:
                type: string
                nullable: true
                maxLength: 998
              from_email:
                type: string
                format: email
                nullable: true
                maxLength: 255
              from_email_domain:
                type: string
                nullable: true
                maxLength: 253
                description: >-
                  Verified sending domain to send this language from, keeping
                  the local part of `from_email`.
              from_name:
                type: string
                nullable: true
                maxLength: 255
              reply_to:
                type: string
                format: email
                nullable: true
                maxLength: 255
    CreateCampaignResponse:
      type: object
      required:
        - message
        - data
      properties:
        message:
          type: string
          example: Campaign created successfully.
        data:
          $ref: '#/components/schemas/CampaignDetail'
    ValidationErrorResponse:
      type: object
      required:
        - message
        - error_code
        - errors
      properties:
        message:
          type: string
          description: Human-readable error message
          example: Validation failed.
        error_code:
          type: string
          description: Error code (always `validation_error` for 422 responses)
          const: validation_error
          example: validation_error
        errors:
          type: object
          description: Field-specific validation errors
          additionalProperties:
            type: array
            items:
              type: string
          example:
            from:
              - The sender email address is required.
            to:
              - At least one recipient email address is required.
    CampaignDetail:
      allOf:
        - $ref: '#/components/schemas/CampaignSummary'
        - type: object
          properties:
            html_content:
              type:
                - string
                - 'null'
              description: Rendered HTML content of the campaign email
            language_overrides:
              type: object
              description: >-
                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.
              additionalProperties:
                type: object
                properties:
                  subject:
                    type:
                      - string
                      - 'null'
                  from_email:
                    type:
                      - string
                      - 'null'
                    format: email
                  from_name:
                    type:
                      - string
                      - 'null'
                  reply_to:
                    type:
                      - string
                      - 'null'
                    format: email
      description: Full campaign representation including rendered HTML content.
    UnauthorizedResponse:
      type: object
      required:
        - message
      description: Error response for authentication failures (401).
      properties:
        message:
          type: string
          description: Human-readable error message
          example: API key is required.
    ErrorResponse:
      type: object
      required:
        - message
        - error_code
      description: Error response for non-validation errors (400, 409, 500, 502).
      properties:
        message:
          type: string
          description: Human-readable error message
          example: The sender domain could not be determined from the email address.
        error_code:
          $ref: '#/components/schemas/ErrorCode'
    CampaignSummary:
      type: object
      description: Campaign summary with embedded engagement stats.
      required:
        - id
        - name
        - status
        - sent_count
        - created_at
        - stats
      properties:
        id:
          type: string
          format: uuid
          example: 0193e6a8-1f3a-7c2a-b9e2-1aa1d2e5d3f0
        name:
          type: string
          example: Spring Sale
        subject:
          type:
            - string
            - 'null'
          description: Email subject line
        from_email:
          type:
            - string
            - 'null'
          description: Sender email address
          format: email
        from_name:
          type:
            - string
            - 'null'
          description: Sender display name
        reply_to:
          type:
            - string
            - 'null'
          description: Reply-to address
          format: email
        status:
          type: string
          enum:
            - draft
            - scheduled
            - preparing
            - in_review
            - sending
            - sent
            - failed
          example: sent
        scheduled_at:
          type:
            - string
            - 'null'
          description: Scheduled delivery time (ISO 8601)
          format: date-time
        total_recipients:
          type:
            - integer
            - 'null'
          description: Total recipients resolved for the send
        sent_count:
          type: integer
          minimum: 0
          example: 124
        sent_at:
          type:
            - string
            - 'null'
          description: When the campaign finished sending (ISO 8601)
          format: date-time
        created_at:
          type: string
          format: date-time
          example: '2026-05-01T09:00:00+00:00'
        stats:
          $ref: '#/components/schemas/CampaignStats'
    ErrorCode:
      type: string
      description: >-
        Error codes returned by the API.


        | Code | HTTP Status | Description |

        |------|-------------|-------------|

        | `validation_error` | 422 | Request validation failed. Check the
        `errors` object for field-specific messages. |

        | `invalid_domain` | 400 | The sender domain could not be determined
        from the email address. |

        | `unconfigured_domain` | 400 | The sender domain is not configured or
        approved for sending. |

        | `send_error` | 400, 500 | General error during email send preparation
        or domain registration. |

        | `retrieval_error` | 400, 500 | Failed to retrieve resources (e.g.,
        sandbox key without associated user, upstream error). |

        | `transmission_failed` | 502 | Email transmission to the upstream
        provider failed. |

        | `resource_already_exists` | 409 | The resource (e.g., domain) already
        exists. |

        | `not_found` | 404 | The specified resource was not found. |

        | `template_not_found` | 404 | The specified template, project, or
        template version was not found. |

        | `insufficient_scope` | 403 | The API key does not have the required
        scope for this endpoint. |

        | `schedule_cancellation_failed` | 409, 500 | The scheduled email can no
        longer be cancelled (already sent, already cancelled, or being injected
        right now), or the cancellation itself failed. |

        | `quota_exceeded` | 429 | Monthly sending quota exceeded. Upgrade your
        plan to continue sending. |

        | `daily_quota_exceeded` | 429 | Daily sending quota exceeded. Try again
        tomorrow. |

        | `campaign_not_sendable` | 422 | The campaign cannot be sent in its
        current state (not a draft, or missing subject/sender/content). |

        | `campaign_not_scheduled` | 422 | The campaign is not scheduled, so it
        cannot be unscheduled. |
      enum:
        - validation_error
        - idempotency_key_conflict
        - idempotency_in_progress
        - invalid_domain
        - unconfigured_domain
        - send_error
        - retrieval_error
        - transmission_failed
        - resource_already_exists
        - not_found
        - template_not_found
        - insufficient_scope
        - schedule_cancellation_failed
        - quota_exceeded
        - daily_quota_exceeded
        - campaign_not_sendable
        - campaign_not_scheduled
      example: invalid_domain
    CampaignStats:
      type: object
      description: Aggregated engagement statistics for a campaign.
      required:
        - injections
        - deliveries
        - bounces
        - spam_complaints
        - opens
        - unique_opens
        - clicks
        - unique_clicks
        - unsubscribes
      properties:
        injections:
          type: integer
          minimum: 0
          example: 0
        deliveries:
          type: integer
          minimum: 0
          example: 0
        bounces:
          type: integer
          minimum: 0
          example: 0
        spam_complaints:
          type: integer
          minimum: 0
          example: 0
        opens:
          type: integer
          minimum: 0
          example: 0
        unique_opens:
          type: integer
          minimum: 0
          example: 0
        clicks:
          type: integer
          minimum: 0
          example: 0
        unique_clicks:
          type: integer
          minimum: 0
          example: 0
        unsubscribes:
          type: integer
          minimum: 0
          example: 0
  responses:
    UnauthorizedError:
      description: Unauthorized - Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UnauthorizedResponse'
          examples:
            missing_key:
              summary: Missing API key
              value:
                message: API key is required.
            invalid_key:
              summary: Invalid API key
              value:
                message: Invalid API key.
    ForbiddenError:
      description: Forbidden - API key does not have the required scope
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            message: >-
              Your API key does not have the required permissions for this
              action.
            error_code: insufficient_scope
    ServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            message: An unexpected error occurred. Please try again later.
            error_code: send_error
    ServiceUnavailableError:
      description: >-
        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.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
            example: 5
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            message: Unable to verify your API key right now. Please retry shortly.
            error_code: auth_unavailable
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key for authentication

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.