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

# Send an email

> Sends an outbound email scoped to your company. Two usage patterns:

`userId` and `channelId` are required on every call.

**From an existing draft** — also provide `discussionId` and `uuid` returned by
`POST /api/email/create-draft`. The draft's stored recipients, subject, and body are
used.

**On-the-fly** — omit `uuid` and provide `userId`, `channelId`, recipients, subject,
and body. A draft is created automatically before sending. Supplying `discussionId`
(without `uuid`) sends the message as a reply into that existing thread — the
thread's subject is kept; omitting both `discussionId` and `uuid` starts a new thread.

### Adding attachments

Attachments apply to the **on-the-fly** pattern (for an existing draft, attach files
when you call `POST /api/email/create-draft`). Upload each file with the three-step
direct upload flow first — you cannot post file bytes to this endpoint:

1. **Get an upload URL** — `POST /api/files/upload-url` with `attachEntityType: "channel"`,
   `attachEntityId` set to the same `channelId` you're sending from, and the file's
   `name`, `extension`, `size`, and `contentType`. Returns `{ fileId, url, fields }`.
2. **Upload to S3** — `multipart/form-data` `POST` to `url`, all `fields` first then the
   file bytes last as a `file` field. S3 returns `204`.
3. **Mark complete** — `POST /api/files/upload-complete` with the `fileId`.

Then, on this request:
- **Regular attachment** — add the `fileId`(s) to `attachmentIds`.
- **Inline image** — embed an `<img>` in the HTML `body` whose `src` is the file's
  download URL (`.../cdn/storage/AptlyFiles/<fileId>/original/...`). Auto-detected and
  registered; do **not** also list it in `attachmentIds`.

Each `fileId` must reference a finished upload.




## OpenAPI

````yaml /openapi.yaml post /api/email/send
openapi: 3.0.3
info:
  title: Aptly API
  version: '1.1'
  description: |
    The Aptly API lets external systems work with boards, contacts, inboxes,
    tasks, files, and other Aptly resources.

    Most endpoints accept an API key in the `x-token` header. Some endpoints
    also accept a delegate token or partner bearer token, as shown in each
    operation's security requirements, and explicitly public endpoints require
    no credential. API keys are scoped to a company and may be restricted to
    specific boards and read, insert, or update permissions; requests outside
    those restrictions receive a 403 `FORBIDDEN` response.
servers:
  - url: https://core-api.getaptly.com
    description: Production
security:
  - ApiKeyHeader: []
paths:
  /api/email/send:
    post:
      tags:
        - Email
      summary: Send an email
      description: >
        Sends an outbound email scoped to your company. Two usage patterns:


        `userId` and `channelId` are required on every call.


        **From an existing draft** — also provide `discussionId` and `uuid`
        returned by

        `POST /api/email/create-draft`. The draft's stored recipients, subject,
        and body are

        used.


        **On-the-fly** — omit `uuid` and provide `userId`, `channelId`,
        recipients, subject,

        and body. A draft is created automatically before sending. Supplying
        `discussionId`

        (without `uuid`) sends the message as a reply into that existing thread
        — the

        thread's subject is kept; omitting both `discussionId` and `uuid` starts
        a new thread.


        ### Adding attachments


        Attachments apply to the **on-the-fly** pattern (for an existing draft,
        attach files

        when you call `POST /api/email/create-draft`). Upload each file with the
        three-step

        direct upload flow first — you cannot post file bytes to this endpoint:


        1. **Get an upload URL** — `POST /api/files/upload-url` with
        `attachEntityType: "channel"`,
           `attachEntityId` set to the same `channelId` you're sending from, and the file's
           `name`, `extension`, `size`, and `contentType`. Returns `{ fileId, url, fields }`.
        2. **Upload to S3** — `multipart/form-data` `POST` to `url`, all
        `fields` first then the
           file bytes last as a `file` field. S3 returns `204`.
        3. **Mark complete** — `POST /api/files/upload-complete` with the
        `fileId`.


        Then, on this request:

        - **Regular attachment** — add the `fileId`(s) to `attachmentIds`.

        - **Inline image** — embed an `<img>` in the HTML `body` whose `src` is
        the file's
          download URL (`.../cdn/storage/AptlyFiles/<fileId>/original/...`). Auto-detected and
          registered; do **not** also list it in `attachmentIds`.

        Each `fileId` must reference a finished upload.
      operationId: sendEmail
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - userId
                - channelId
              properties:
                discussionId:
                  type: string
                  description: >
                    Stream ID from `POST /api/email/create-draft` (required if
                    `uuid` is set).

                    When supplied without `uuid`, the on-the-fly draft is
                    appended as a reply

                    into this existing thread (same company + channel) instead
                    of starting a

                    new discussion; the thread's subject is kept.
                uuid:
                  type: string
                  description: >-
                    Draft UUID from `POST /api/email/create-draft`. Required if
                    `discussionId` is set.
                failOnCreateNewThread:
                  type: boolean
                  default: true
                  description: >
                    If `true` (default), the send fails when the email would
                    create a new

                    discussion thread rather than reply to an existing one.
                userId:
                  type: string
                  description: Required when sending on-the-fly (no `discussionId`/`uuid`).
                channelId:
                  type: string
                  description: Required when sending on-the-fly.
                companyId:
                  type: string
                  description: >-
                    Company ID. Required only when authenticating with a partner
                    token; resolved automatically for API key and delegate token
                    auth.
                to:
                  type: array
                  items:
                    $ref: '#/components/schemas/EmailRecipient'
                cc:
                  type: array
                  items:
                    $ref: '#/components/schemas/EmailRecipient'
                bcc:
                  type: array
                  items:
                    $ref: '#/components/schemas/EmailRecipient'
                subject:
                  type: string
                body:
                  type: string
                  description: >
                    Email body (plain text or HTML). To embed an uploaded image
                    inline, add

                    an `<img>` whose `src` is the file's download URL from

                    `POST /api/files/upload-complete`

                    (`.../cdn/storage/AptlyFiles/<fileId>/original/...`). Inline
                    images are

                    auto-detected, tagged with `data-inline-image-id`, and
                    registered on the

                    draft — do not also list them in `attachmentIds`. Applies
                    when the draft

                    is created on-the-fly (no `discussionId`/`uuid`).
                attachmentIds:
                  type: array
                  items:
                    type: string
                  description: >
                    File ids to include as regular attachments (applies when the
                    draft is

                    created on-the-fly). Upload each file first (`POST
                    /api/files/upload-url`

                    with attachEntityType `channel` and the same channelId →
                    upload to S3 →

                    `POST /api/files/upload-complete`), then pass the resulting
                    `fileId`s here.

                    Each must reference a finished upload.
                aptletInstanceId:
                  type: string
                  description: >
                    Card to link the outbound to. Logs the email as an activity
                    on the

                    card and tags the discussion. Works whether the draft was
                    pre-created

                    or is created on-the-fly.
            examples:
              from_draft:
                summary: Send from an existing draft
                value:
                  userId: user_abc
                  channelId: channel_abc
                  discussionId: stream_abc
                  uuid: draft_xyz
              on_the_fly:
                summary: On-the-fly send
                value:
                  userId: user_abc
                  channelId: channel_abc
                  to:
                    - value: tenant@example.com
                  subject: Your lease renewal
                  body: Please review the attached renewal terms.
                  aptletInstanceId: card_abc
      responses:
        '200':
          description: Email sent successfully.
          content:
            application/json:
              schema:
                type: object
        '400':
          description: >-
            The request body failed validation, for example a recipient that is
            not an email address, or a missing `userId` or `channelId`. See
            `meta.issues` for each field.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
        '401':
          description: Invalid or missing API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - ApiKeyHeader: []
        - DelegateToken: []
        - PartnerBearer: []
components:
  schemas:
    EmailRecipient:
      type: object
      required:
        - value
      properties:
        value:
          type: string
          description: Email address.
        label:
          type: string
          description: Optional display name for the recipient.
    ValidationError:
      type: object
      description: >
        Returned with HTTP 400 when the request body fails shape validation
        (wrong type,

        missing required field, malformed email/phone number, value out of
        bounds).

        `meta.issues` lists every offending field.
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - VALIDATION_ERROR
            message:
              type: string
              example: One or more fields are invalid
            meta:
              type: object
              properties:
                issues:
                  type: array
                  items:
                    type: object
                    properties:
                      field:
                        type: string
                        description: >-
                          Dotted path to the offending property (for example
                          `to.0.value`); empty for a problem with the body as a
                          whole.
                        example: to.0.value
                      code:
                        type: string
                        description: >-
                          Machine-readable issue code (for example
                          `invalid_type`, `invalid_format`, `too_small`).
                        example: invalid_format
                      message:
                        type: string
                        example: must be a valid email address
        requestId:
          type: string
    Error:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: x-token
    DelegateToken:
      type: apiKey
      in: header
      name: Authorization
      description: 'Delegate token issued by the platform. Format: `DelegateToken <token>`'
    PartnerBearer:
      type: http
      scheme: bearer
      description: 'Partner token. Format: `Authorization: Bearer <token>`'

````

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