openapi: 3.0.3 info: title: Aptly App Email API version: '1.0' description: 'The Aptly API lets you read and write cards on any Aptly board from external systems. All requests require an API key passed as the `x-token` header. API keys are scoped to your company and work across all boards. ' servers: - url: https://core-api.getaptly.com description: Production security: - ApiKeyHeader: [] tags: - name: Email paths: /api/email/create-draft: post: summary: Create an email draft description: "Creates a new outbound email discussion (stream) with a single draft entry, scoped to\nyour company. Returns the `streamId` and `draftUuid` needed to send via\n`POST /api/email/send`.\n\nRecipient fields (`to`, `cc`, `bcc`) accept an array of `{ value, label? }` objects\nwhere `value` is the email address.\n\n### Adding attachments\n\nAttachments must be uploaded to storage **before** you reference them here — you\ncannot post file bytes to this endpoint. Upload each file with the three-step direct\nupload flow, then attach it:\n\n1. **Get an upload URL** — `POST /api/files/upload-url` with `attachEntityType: \"channel\"`,\n `attachEntityId` set to the same `channelId` you're emailing from, and the file's\n `name`, `extension`, `size`, and `contentType`. It returns `{ fileId, url, fields }`.\n2. **Upload to S3** — send a `multipart/form-data` `POST` to `url`, appending every\n entry from `fields` first and the file bytes last as a `file` field. S3 returns `204`.\n3. **Mark complete** — `POST /api/files/upload-complete` with the `fileId` from step 1.\n\nThen, on this request:\n- **Regular attachment** — add the `fileId`(s) to `attachmentIds`.\n- **Inline image** — embed an `` in the HTML `body` whose `src` is the file's\n download URL (`.../cdn/storage/AptlyFiles//original/...`, returned by step 3).\n It is auto-detected and registered; do **not** also list it in `attachmentIds`.\n\nRepeat steps 1–3 per file. Each `fileId` must reference a finished upload or the\nrequest is rejected.\n" operationId: createEmailDraft tags: - Email security: - ApiKeyHeader: [] - DelegateToken: [] - PartnerBearer: [] requestBody: required: true content: application/json: schema: type: object required: - userId - channelId properties: userId: type: string description: User to record as the draft creator/sender. channelId: type: string description: '`channel.meta.id` of the outbound email channel.' discussionId: type: string description: 'When set, append the draft as a reply to this existing thread (stream ID) instead of starting a new discussion. The thread must belong to the same company and channel. The thread''s subject is kept and the draft is threaded onto the thread''s last message. ' 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 `` whose `src` is the file''s download URL from `POST /api/files/upload-complete` (`.../cdn/storage/AptlyFiles//original/...`). Inline images are auto-detected, tagged with `data-inline-image-id`, and registered on the draft — do not also list them in `attachmentIds`. ' attachmentIds: type: array items: type: string description: 'File ids to include as regular attachments. 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. When set, sending the draft logs the email as an activity on the card and tags the discussion with it. ' example: userId: user_abc channelId: channel_abc to: - value: tenant@example.com label: Jane Tenant subject: Your lease renewal body: Please review the attached renewal terms. aptletInstanceId: card_abc responses: '200': description: Draft stream created. content: application/json: schema: type: object properties: streamId: type: string description: Discussion/stream ID to pass to `POST /api/email/send`. draftUuid: type: string description: Draft UUID to pass to `POST /api/email/send`. '401': description: Invalid or missing API key. content: application/json: schema: $ref: '#/components/schemas/Error' /api/email/send: post: summary: Send an email description: "Sends an outbound email scoped to your company. Two usage patterns:\n\n**From an existing draft** — provide `discussionId` and `uuid` returned by\n`POST /api/email/create-draft`. The draft's stored recipients, subject, and body are\nused.\n\n**On-the-fly** — omit `uuid` and provide `userId`, `channelId`, recipients, subject,\nand body. A draft is created automatically before sending. Supplying `discussionId`\n(without `uuid`) sends the message as a reply into that existing thread — the\nthread's subject is kept; omitting both `discussionId` and `uuid` starts a new thread.\n\n### Adding attachments\n\nAttachments apply to the **on-the-fly** pattern (for an existing draft, attach files\nwhen you call `POST /api/email/create-draft`). Upload each file with the three-step\ndirect upload flow first — you cannot post file bytes to this endpoint:\n\n1. **Get an upload URL** — `POST /api/files/upload-url` with `attachEntityType: \"channel\"`,\n `attachEntityId` set to the same `channelId` you're sending from, and the file's\n `name`, `extension`, `size`, and `contentType`. Returns `{ fileId, url, fields }`.\n2. **Upload to S3** — `multipart/form-data` `POST` to `url`, all `fields` first then the\n file bytes last as a `file` field. S3 returns `204`.\n3. **Mark complete** — `POST /api/files/upload-complete` with the `fileId`.\n\nThen, on this request:\n- **Regular attachment** — add the `fileId`(s) to `attachmentIds`.\n- **Inline image** — embed an `` in the HTML `body` whose `src` is the file's\n download URL (`.../cdn/storage/AptlyFiles//original/...`). Auto-detected and\n registered; do **not** also list it in `attachmentIds`.\n\nEach `fileId` must reference a finished upload.\n" operationId: sendEmail tags: - Email security: - ApiKeyHeader: [] - DelegateToken: [] - PartnerBearer: [] requestBody: required: true content: application/json: schema: type: object 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. 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 `` whose `src` is the file''s download URL from `POST /api/files/upload-complete` (`.../cdn/storage/AptlyFiles//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: 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 '401': description: Invalid or missing API key. content: application/json: schema: $ref: '#/components/schemas/Error' components: schemas: Error: type: object properties: error: type: string message: type: string EmailRecipient: type: object required: - value properties: value: type: string description: Email address. label: type: string description: Optional display name for the recipient. 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 `' PartnerBearer: type: http scheme: bearer description: 'Partner token. Format: `Authorization: Bearer `'