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

# Send Email

> A polymorphic endpoint for sending email messages to one or more recipients.
The endpoint is designed to feel natural in use:  
- Start simple with a basic email to a single 
  address.  
- Gradually add data to enable complex 
  communications to multiple contacts.
- Optionally supply a template, upsert contacts with 
  profile data, provide merge data at either the template 
  or contact level, add CC/BCC, attach files, and more.

The response includes a `request_id` and an array of per-recipient 
  `EmailMessageResult` objects describing each message created in the process.

Check out the examples with their responses for common use cases, 
  and browse the full input schema to see everything that's possible with the Send API.




## OpenAPI

````yaml POST /send/email
openapi: 3.1.1
info:
  title: BetterMail API
  version: 2.0.15
  description: >
    The BetterMail API delivers a polymorphic, omni-channel REST interface that
      unifies  email and SMS sending, contact and list management, and event
      tracking. 

    Designed by developers for developers, it grows with you: start with a 
    simple two-line JSON payload and expand effortlessly as your product  scales
    and new integration challenges emerge.
  termsOfService: https://bettermail.com/terms
  license:
    name: Proprietary
    url: https://bettermail.com/terms
  contact:
    name: BetterMail Support
    url: https://support.bettermail.com
    email: support@bettermail.com
  x-logo:
    url: https://bettermail.com/assets/logo_circle.svg
    altText: BetterMail Logo
servers:
  - url: https://api.bettermail.com/v2
    description: Production API
security:
  - BearerAuth: []
tags:
  - name: Send API
    description: Endpoints for sending product communications
  - name: Miscellaneous
    description: Utility endpoints for health checks and monitoring
paths:
  /send/email:
    post:
      tags:
        - Send API
      summary: Send Email Messages
      description: >
        A polymorphic endpoint for sending email messages to one or more
        recipients.

        The endpoint is designed to feel natural in use:  

        - Start simple with a basic email to a single 
          address.  
        - Gradually add data to enable complex 
          communications to multiple contacts.
        - Optionally supply a template, upsert contacts with 
          profile data, provide merge data at either the template 
          or contact level, add CC/BCC, attach files, and more.

        The response includes a `request_id` and an array of per-recipient 
          `EmailMessageResult` objects describing each message created in the process.

        Check out the examples with their responses for common use cases, 
          and browse the full input schema to see everything that's possible with the Send API.
      operationId: sendEmail
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendEmailRequest'
            examples:
              single:
                summary: Single recipient
                value:
                  campaign: WELCOME_EMAIL
                  to: emily.carter@cbroasters.com
              multiple:
                summary: Multiple recipients
                value:
                  campaign: BILL_V2
                  to:
                    - emily.carter@cbroasters.com
                    - jordan.taylor@cbroasters.com
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/SendEmailRequestMultipart'
            encoding:
              json:
                contentType: application/json
            examples:
              mp-single:
                summary: Multipart with various attachments
                value:
                  json: |
                    {
                      "campaign": "WELCOME_EMAIL",
                      "attachments": [
                        {
                          "attachment_type": "file",
                          "file_name": "logo.png",
                          "cid": "logo",
                          "file_ref": "01996409-bb35-7e20-b81a-090e59792f68"
                        },
                        {
                          "attachment_type": "file",
                          "file_name": "bean.png",
                          "cid": "bean",
                          "data": "iVBORw0KGgoAAA......."
                        }
                      ],
                      "to": {
                        "contact_key": "emily.carter@cbroasters.com",
                        "attachments": [
                          {
                            "attachment_type": "url",
                            "file_name": "flyer.pdf",
                            "data": "https://example.com/flyer.pdf"
                          }
                        ]
                      }
                    }
      responses:
        '202':
          description: Request accepted; message(s) queued for delivery
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendEmailAck'
              examples:
                single:
                  summary: Single recipient response
                  value:
                    request_id: 01996409-bb35-7e20-b81a-090e59792f68
                    messages:
                      - message_id: HPPHRx2JceswMGobIv
                        contact_key:
                          email: emily.carter@cbroasters.com
                multiple:
                  summary: Multiple recipients response
                  value:
                    request_id: 01996409-dedf-72b7-98ba-2bb329404b8a
                    messages:
                      - message_id: Lj0smkgXSROzEhJHBj
                        contact_key:
                          email: emily.carter@cbroasters.com
                      - message_id: yU1kr8Dcbs3rPLdfOw
                        contact_key:
                          email: jordan.taylor@cbroasters.com
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
      security:
        - BearerAuth: []
components:
  schemas:
    SendEmailRequest:
      type: object
      required:
        - campaign
        - to
      additionalProperties: false
      properties:
        campaign:
          oneOf:
            - $ref: '#/components/schemas/CampaignSlug'
            - $ref: '#/components/schemas/CampaignReference'
        to:
          $ref: '#/components/schemas/EmailRecipients'
        profile_data:
          $ref: '#/components/schemas/ProfileData'
        message:
          $ref: '#/components/schemas/EmailMessageDetails'
        attachments:
          type: array
          description: |
            Attachments applied to **all recipients**.
              Recipient-level `attachments` (if any) are **additive**.

              **Size limit:** The **sum of all attachments for each message** (request-level +
              recipient-level + any unreferenced multipart binaries auto-attached) **must not exceed
              10 MB**. For base64 data, the decoded byte size is used; for multipart
              files, the uploaded part size is used; for URLs, the fetched content size is used.

              In `multipart/form-data` requests, streamed files can be referenced via `file_ref`.
              Any **unreferenced** binary parts are auto-attached to **each**
              email message using their filename and content type.
          items:
            $ref: '#/components/schemas/Attachment'
          minItems: 1
          maxItems: 10
    SendEmailRequestMultipart:
      type: object
      required:
        - json
      properties:
        json:
          type: string
          description: |
            The full `SendEmailRequest` JSON payload as a string.

              **Multipart modes:**
              - **Explicit mapping:** Include `attachments` at the request and/or recipient level.
                Streamed files MUST be referenced via `file_ref` (the form-data part name, typically a GUID).
                Omit `data` for those items.
              - **Unreferenced parts are auto-attached:** Any multipart **binary** parts
                that are **not** referenced by any `attachments[*].file_ref` are still attached to **each**
                email message (request-level), using their supplied **filename** and **Content-Type**.
                This applies whether or not `attachments` are present in the JSON.
                Inline CIDs cannot be set for these auto-attached files (use explicit mapping if needed).
                SMS channel ignores attachments.
      additionalProperties:
        type: string
        format: binary
    SendEmailAck:
      type: object
      required:
        - request_id
        - messages
      additionalProperties: false
      properties:
        request_id:
          type: string
          format: uuid
          description: |
            A system-generated UUID v7 identifier for the send request.
          example: 018e2c68-81c2-7c5f-bf5e-8f26b8a0d2a4
        messages:
          type: array
          description: An array of objects describing each message enqueued.
          items:
            $ref: '#/components/schemas/EmailMessageResult'
    CampaignSlug:
      type: string
      description: >
        User-defined, human-friendly key that uniquely identifies a campaign
        within an account. You can create this key when launching a
        transactional campaign.
      pattern: ^[A-Za-z0-9-_]{3,40}$
      examples:
        - WELCOME_EMAIL
        - BILL_V2
        - PASSWORD_REMINDER
        - OTP_CODE
    CampaignReference:
      description: |
        Identifies a campaign. You must supply only one of `id` or `slug`.
      oneOf:
        - $ref: '#/components/schemas/IdReference'
        - $ref: '#/components/schemas/SlugReference'
      additionalProperties: false
    EmailRecipients:
      description: >-
        Single recipient object, an RFC 5322 email address string, or an array
        containing both.
      oneOf:
        - $ref: '#/components/schemas/EmailValue'
        - $ref: '#/components/schemas/ContactKey'
        - $ref: '#/components/schemas/EmailMessageRecipient'
        - type: array
          description: >
            An array of RFC 5322 email addresses and/or `EmailMessageRecipient`
            objects.
          items:
            oneOf:
              - $ref: '#/components/schemas/EmailValue'
              - $ref: '#/components/schemas/ContactKey'
              - $ref: '#/components/schemas/EmailMessageRecipient'
          minItems: 1
          maxItems: 100
          uniqueItems: true
    ProfileData:
      type: object
      description: >
        Arbitrary key-value pairs to upsert/merge into the recipient's profile
        and to use for template personalization.
      additionalProperties:
        $ref: '#/components/schemas/JsonValue'
      example:
        first_name: Emily
        loyalty:
          level: gold
          points: 1200
        marketing_opt_in: true
    EmailMessageDetails:
      type: object
      description: |
        Unified per-message override for email, layered on top of your
          transactional campaign's defaults. Use this object to override corresponding campaign parts
          (e.g., from/reply-to/subject/bodies) without changing the campaign itself.

          **Precedence & merge rules**          
          - Request level applies to all recipients.  
          - Recipient level, when provided, overrides request level.  
          - Unset fields fall back to the campaign template values.  
          - Arrays (`cc`, `bcc`) **replace** (no merge).  
          - `headers` are **merged**, with recipient-level keys winning on conflict.  
          - Restricted/system headers (From/To/Cc/Bcc/Subject/Reply-To/In-Reply-To,
            Content-Type, MIME-Version) must not be set here; they will be ignored.

          **Bodies & formats**           
          - `text` is the plain-text email body.  
          - `html` is rich content and supports BML.
      additionalProperties: false
      properties:
        reply_to:
          $ref: '#/components/schemas/EmailValue'
        cc:
          type: array
          description: Carbon-copy recipients.
          items:
            $ref: '#/components/schemas/EmailValue'
          uniqueItems: true
          minItems: 0
          maxItems: 10
        bcc:
          type: array
          description: Blind carbon-copy recipients (kept hidden from other recipients).
          items:
            $ref: '#/components/schemas/EmailValue'
          uniqueItems: true
          minItems: 0
          maxItems: 10
        subject:
          type: string
          description: Message subject line (no CR/LF).
          maxLength: 255
          pattern: ^[^\r\n]*$
          example: Your September statement
        text:
          type: string
          description: Plain-text body.
          example: |
            Hi Emily, Your statement is attached.
        html:
          type: string
          description: Rich body content in HTML **or** BML.
          example: <p>Hi Emily,</p><p>Your statement is attached.</p>
        in_reply_to:
          type: string
          description: RFC 5322 Message-ID being replied to (typically angle-bracketed).
          pattern: ^<[^<>]+>$
          example: <message-id-123@example.com>
        headers:
          type: object
          description: >
            Additional email headers. System/restricted headers will be ignored.
            Header names should be ASCII token characters; values are strings.
          additionalProperties:
            type:
              - string
              - 'null'
          minProperties: 1
    Attachment:
      description: >
        Union of supported attachment types. In `multipart/form-data` requests,
        any **unreferenced** binary parts are auto-attached to each email
        message using their filename and content type.
      oneOf:
        - $ref: '#/components/schemas/AttachmentFileData'
        - $ref: '#/components/schemas/AttachmentFileRef'
        - $ref: '#/components/schemas/AttachmentUrl'
    EmailMessageResult:
      type: object
      description: |
        Server receipt for each enqueued message.
      required:
        - message_id
        - contact_key
      properties:
        message_id:
          type: string
          description: Server-generated message ID.
          pattern: ^[A-Za-z0-9]{18}$
        contact_key:
          $ref: '#/components/schemas/ContactKey'
          description: Contact reference.
        external_message_id:
          description: >-
            Echoes the `external_message_id` supplied in the request, if
            provided.
          type: string
      additionalProperties: false
    Error:
      type: object
    IdReference:
      type: object
      description: Identify an object using its system ID.
      required:
        - id
      additionalProperties: false
      properties:
        id:
          oneOf:
            - $ref: '#/components/schemas/CampaignId'
    SlugReference:
      type: object
      description: Identify an object using its human-friendly key.
      required:
        - slug
      additionalProperties: false
      properties:
        slug:
          oneOf:
            - $ref: '#/components/schemas/CampaignSlug'
      examples:
        - slug: WELCOME_EMAIL
    EmailValue:
      description: A plain email string or an `EmailAddress` object.
      oneOf:
        - $ref: '#/components/schemas/EmailString'
        - $ref: '#/components/schemas/EmailAddress'
    ContactKey:
      type: object
      description: >
        A **single-property** object where the property **name** is the contact
        field
          (e.g., `email`, `sms`, `id`, `CrmContactId`) and the property **value**
          is that field's identifier. Exactly one property must be present.
      minProperties: 1
      maxProperties: 1
      additionalProperties: false
      patternProperties:
        ^email$:
          $ref: '#/components/schemas/EmailString'
        ^sms$:
          $ref: '#/components/schemas/SmsNumberE164'
        ^id$:
          $ref: '#/components/schemas/ContactId'
        ^[A-Za-z][A-Za-z0-9_-]{1,44}$:
          type: string
          description: Custom contact key value.
      examples:
        - email: emily.carter@cbroasters.com
        - sms: '+642112345678'
        - id: VqYYzmVXYTwREzg2m1
        - CrmContactId: C12345
    EmailMessageRecipient:
      type: object
      description: Defines the recipient for a `SendEmailMessage` request.
      required:
        - contact_key
      properties:
        contact_key:
          oneOf:
            - $ref: '#/components/schemas/ContactKey'
            - $ref: '#/components/schemas/EmailValue'
        email_address:
          $ref: '#/components/schemas/EmailValue'
        external_message_id:
          description: >
            Optional client-supplied identifier for the message.   Use this
            field if you want to assign your own message ID for tracking or
            querying.   Must be globally unique within your BetterMail account.
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[A-Za-z0-9._~-]+$
        profile_data:
          $ref: '#/components/schemas/ProfileData'
        message:
          $ref: '#/components/schemas/EmailMessageDetails'
        attachments:
          type: array
          description: >
            Attachments for **this recipient only**. Combined with request-level
            `attachments`. 

            **Size limit:** The **sum of all attachments for this message**
            (request-level + recipient-level + any unreferenced multipart
            binaries auto-attached) **must not exceed 10 MB**. Decoded byte size
            is used for base64; multipart uses the part size; URLs use fetched
            content size.
          items:
            $ref: '#/components/schemas/Attachment'
          minItems: 1
          maxItems: 10
      additionalProperties: false
    JsonValue:
      description: Any JSON value.
      anyOf:
        - type: 'null'
        - type: boolean
        - type: string
        - type: number
        - type: object
          additionalProperties:
            $ref: '#/components/schemas/JsonValue'
        - type: array
          items:
            $ref: '#/components/schemas/JsonValue'
    AttachmentFileData:
      type: object
      description: >
        File attachment for the **email** channel using **base64** data (JSON
        mode).
      required:
        - attachment_type
        - file_name
        - data
      additionalProperties: false
      properties:
        attachment_type:
          type: string
          enum:
            - file
        file_name:
          $ref: '#/components/schemas/FileName'
        data:
          type: string
          description: Base64-encoded file content.
          contentEncoding: base64
          contentMediaType: application/octet-stream
        cid:
          $ref: '#/components/schemas/ContentIdHeader'
        metadata:
          $ref: '#/components/schemas/AttachmentMetadata'
    AttachmentFileRef:
      type: object
      description: >
        File attachment for the **email** channel streamed via
        `multipart/form-data`.
          Put the full JSON request in the `json` form part; stream file bytes in a separate
          form part whose **name is a GUID**, and reference that part here via `file_ref`.

          If `file_name` is omitted here, BetterMail uses the multipart part's
          `Content-Disposition` filename.

          **Note:** Any unreferenced multipart **binary** parts are
          auto-attached to each email message using their filename and content type.
      required:
        - file_ref
      additionalProperties: false
      properties:
        file_ref:
          type: string
          format: uuid
          description: The **form-data part name (GUID)** that carries the file bytes.
        file_name:
          type: string
          description: Optional; taken from the multipart file part if omitted.
          minLength: 1
          maxLength: 255
          pattern: ^[^/\\\r\n]{1,255}$
        cid:
          type: string
          description: >
            Content-ID for inline usage (email only). Reference from HTML as
            `cid:<value>`.
          maxLength: 128
          pattern: ^[A-Za-z0-9._~-]+$
          example: logo
        metadata:
          $ref: '#/components/schemas/AttachmentMetadata'
    AttachmentUrl:
      type: object
      description: >
        URL-based attachment for the **email** channel. BetterMail fetches
        content over HTTPS.
      required:
        - attachment_type
        - file_name
        - data
      additionalProperties: false
      properties:
        attachment_type:
          type: string
          enum:
            - url
        file_name:
          $ref: '#/components/schemas/FileName'
        data:
          type: string
          format: uri
          description: HTTPS URL to fetch.
          pattern: ^https://
          minLength: 20
          maxLength: 2048
          example: https://cdn.example.com/docs/terms-v3.pdf
        cid:
          $ref: '#/components/schemas/ContentIdHeader'
        metadata:
          $ref: '#/components/schemas/AttachmentMetadata'
    CampaignId:
      type: string
      description: >
        A system-generated unique identifier for a campaign. While this ID can
        always be used, developers often prefer `CampaignSlug` in the Send API
        for improved code readability and maintainability.
      pattern: ^[A-Za-z0-9]{18}$
      example: 4Y5E0nhdzOVKXBhFK4
    EmailString:
      type: string
      description: A plain email string.
      format: email
    EmailAddress:
      type: object
      description: An email address with an optional display name.
      required:
        - email
      properties:
        email:
          type: string
          format: email
          description: Email address part.
        name:
          type: string
          description: Display name part.
      additionalProperties: false
    FileName:
      type: string
      description: Filename as shown to the recipient (no paths).
      minLength: 1
      maxLength: 255
      pattern: ^[^/\\\r\n]{1,255}$
      example: terms.pdf
    ContentIdHeader:
      type: string
      description: >
        Content-ID for inline usage (email only). Reference in HTML (e.g., `<img
        src="cid:logo">`).
      maxLength: 128
      pattern: ^[A-Za-z0-9._~-]+$
      example: logo
    AttachmentMetadata:
      type: object
      description: >
        Experimental, non-contractual key-value bag. Keys MAY be ignored; common
        patterns may be promoted to typed properties in future versions. Keys
        MUST be namespaced with `x-` or `bm-`. Avoid long-lived secrets; prefer
        pre-signed URLs.
      additionalProperties:
        $ref: '#/components/schemas/JsonValue'
      propertyNames:
        type: string
        maxLength: 64
        pattern: ^(x-|bm-)[A-Za-z0-9._-]+$
      maxProperties: 32
      example:
        x-url-query:
          download: '1'
        x-url-headers:
          Accept: application/pdf
  responses:
    BadRequest:
      description: Bad request
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Error'
    UnauthorizedError:
      description: Unauthorized
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Error'
    UnprocessableEntity:
      description: Validation failed
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Too many requests
      headers:
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
          description: Unix epoch seconds
        Retry-After:
          description: Seconds to wait before retrying
          schema:
            type: integer
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Error'
    ServerError:
      description: Server error
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Opaque API Key
      description: >
        Provide your BetterMail API key as a Bearer token: `Authorization:
        Bearer YOUR_API_KEY`. You can generate and manage  API keys in the
        Developer Centre [https://bettermail.app/developer-centre/api] in your
        BetterMail account.

````