---
title: "Send a reply from the connected mailbox"
method: POST
path: "/api/v1/mail/threads/{id}/send"
tags: ["Desk"]
---

# Send a reply from the connected mailbox

`POST /api/v1/mail/threads/{id}/send`

## Request

- Base URL: `http://localhost:5180`
- URL: `http://localhost:5180/api/v1/mail/threads/{id}/send`
- Auth: API key in cookie `better-auth.session_token`

Writes the reply on the conversation and sends it from the mailbox the conversation's mail arrived in, to the contact or to the address that last wrote: a connected mailbox signs in to its own SMTP server with its stored password, and the team's own address at the Dardaris inbox domain sends through Cloudflare. It answers at once with the reply `scheduled`; read it back with GET /mail/messages/{id} to see it `sent` or `failed`. A person sends from here, and so does a mail run, only with a token minted for that one reply when the owner's settings let it go and the sender's address was proved.

## Path parameters

- `id` string, required

## Request body

- object
  - `messageId` string, required: The id the reply is written under. A draft or a reply that could not be sent is sent again under its own id; one already on its way is left as it is.
  - `body` string, required: The reply as the editor writes it: paragraphs, `**bold**`, `_italic_`, `~~struck~~`, `++underlined++`, `[a link](https://…)`, lists and quotes.
  - `attachments` object[]: Files uploaded first with PUT /mail/threads/{id}/attachments/{attachmentId}?message={messageId}, sent with the reply.
    - `id` string, required: Stable record id, e.g. `lse_012`.
    - `name` string, required
    - `size` integer, required
    - `kind` string, required

## Response `200`

The reply is written and on its way.

- object
  - `ok` unknown, required
  - `data` object, required
    - `message` object, required: The reply, `scheduled` until the mailbox has taken it, then `sent`, or `failed` with the reason in `entitiesExtracted.mailError`.
      - `id` string, required: Stable record id, e.g. `lse_012`.
      - `threadId` string, required: Stable record id, e.g. `lse_012`.
      - `direction` 'inbound' | 'outbound', required
      - `channel` 'email' | 'sms' | 'whatsapp' | 'phone' | 'portal' | 'post', required
      - `fromContactId` string: Stable record id, e.g. `lse_012`.
      - `fromAddress` string: The address an inbound message came from, where the sender is nobody in the address book.
      - `fromActor` object: Set on an outbound message, naming who or what sent it.
        - `kind` 'owner' | 'ai' | 'tenant' | 'contractor' | 'system' | 'channel', required: `ai` is Dardaris acting on the owner’s behalf. Everything it touches is marked.
        - `id` string: Stable record id, e.g. `lse_012`.
        - `name` string, required
      - `toContactIds` string[], required
      - `sentAt` string, required: ISO 8601 timestamp.
      - `subject` string
      - `body` string, required
      - `language` string, required: An ISO 639-1 code. Wider than the four the product authors in, because it is what the reader reads.
      - `attachments` object[], required
        - `id` string, required: Stable record id, e.g. `lse_012`.
        - `name` string, required
        - `size` number, required
        - `kind` string, required
      - `intent` string: What the message was about, as classified on arrival. It is what the engine acted on.
      - `intentConfidence` number
      - `workflowRunId` string: Outbound only: the automated run that produced this.
      - `templateId` string: Stable record id, e.g. `lse_012`.
      - `status` 'draft' | 'discarded' | 'pending_approval' | 'scheduled' | 'filed' | 'sent' | 'delivered' | 'read' | 'failed' | 'received', required: `draft` is a reply waiting for the owner to read, `discarded` is one they threw away, and `filed` is on the thread and not sent: the product has no way to send from the owner’s address yet.
      - `deliveryProof` object: Proof of delivery, which some dunning steps legally require.
        - `method` 'registered_post' | 'read_receipt' | 'delivery_receipt', required
        - `reference` string, required
        - `at` string, required: ISO 8601 timestamp.
      - `readAt` string: ISO 8601 timestamp.
      - `provenance` object
        - `actor` object, required
          - `kind` 'owner' | 'ai' | 'tenant' | 'contractor' | 'system' | 'channel', required: `ai` is Dardaris acting on the owner’s behalf. Everything it touches is marked.
          - `id` string: Stable record id, e.g. `lse_012`.
          - `name` string, required
        - `at` string, required: ISO 8601 timestamp.
        - `workflowRunId` string: Set when an automated run produced this, linking the record back to the run.
        - `reasoning` string: The rationale the AI recorded at the moment it acted.
    - `thread` object, required: The conversation with the reply on it.
      - `id` string, required: Stable record id, e.g. `lse_012`.
      - `subject` string, required
      - `channel` 'email' | 'sms' | 'whatsapp' | 'phone' | 'portal' | 'post', required
      - `participantIds` string[], required
      - `unitId` string: Stable record id, e.g. `lse_012`.
      - `buildingId` string: Set where the conversation is about a whole building rather than a lot in it.
      - `leaseId` string: Stable record id, e.g. `lse_012`.
      - `bookingId` string: Stable record id, e.g. `lse_012`.
      - `issueId` string: Stable record id, e.g. `lse_012`.
      - `lastMessageAt` string, required: ISO 8601 timestamp.
      - `messageIds` string[], required
      - `status` 'open' | 'waiting_on_tenant' | 'waiting_on_us' | 'snoozed' | 'closed', required
      - `dueBy` string: When a reply is owed on our side.
      - `snoozedUntil` string: ISO 8601 timestamp.
      - `unread` boolean, required
      - `pinned` boolean, required
      - `labels` string[], required
      - `aiSummary` string: A rolling summary, so a long thread is readable at a glance.
      - `sentiment` 'positive' | 'neutral' | 'frustrated' | 'angry'
      - `escalationRisk` 'none' | 'low' | 'medium' | 'high': How likely this is to turn into a dispute if it is left alone.
  - `meta` object, required
    - `requestId` string, required: Echoed in the x-request-id header. Quote it in a bug report.

## Other responses

- `404`: No such conversation on this team.
- `409`: That id is already a different message, or the conversation changed while this was written.
- `422`: This reply cannot be sent from here: the conversation is not email, no mailbox it arrived in can send, nobody has an address to send it to, or this deployment has no mail worker (`details.reason`).

---

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