# Send an email now

Source: https://docs.emelia.io/docs/inbox-api/send-email-now

Guide: [Inbox API](https://docs.emelia.io/docs/inbox-api.md)

`POST https://api.emelia.io/inbox/api/v1/drafts/send`

Sends an email immediately from one of the workspace mailboxes. The only irreversible action of the API: it reaches a third party.

To reply in a thread: send from the `mailboxId` of the original, set `inReplyTo` to its `messageId`, `references` to its `references` followed by its `messageId`, and prefix the subject with `Re: `.

Signatures: inserted by default, exactly as for `POST /drafts` (`"default"`, `"none"` or a signature id), resolved on the `from` alias when given, otherwise on the mailbox address. Your HTML is sent as is.

Always send an `Idempotency-Key`. Known issue: if the message left but a later step failed (copy to the Sent folder, disconnection), the API answers `500 INTERNAL_ERROR` and releases the idempotency key, so a retry sends the message a second time. After a 500, check the SENT folder before retrying.

Fires a `draft.sent` webhook. Attachments cannot be sent through the API.

## Authentication

Send your API key in the `Authorization` header: `Authorization: <your API key>`.

## Request body

Content type: `application/json`, required.

| Field | Type | Required | Description |
|---|---|---|---|
| `mailboxId` | string | yes | Sending mailbox (`GET /mailboxes`). Min length 1. |
| `from` | object | no | Optional send-as alias; must be a verified alias of the mailbox, otherwise 400 INVALID_SENDER_ALIAS. |
| `from.email` | string (email) | yes |  |
| `from.name` | string | no |  |
| `to` | array of object | yes |  |
| `to[].email` | string (email) | yes |  |
| `to[].name` | string | no |  |
| `cc` | array of object | no |  |
| `cc[].email` | string (email) | yes |  |
| `cc[].name` | string | no |  |
| `bcc` | array of object | no |  |
| `bcc[].email` | string (email) | yes |  |
| `bcc[].name` | string | no |  |
| `subject` | string | no | No line breaks. Default: `""`. |
| `body` | object | yes | HTML, text or both. Both may be omitted (empty body). |
| `body.html` | string | no |  |
| `body.text` | string | no |  |
| `inReplyTo` | string | no | `messageId` of the email being answered. |
| `references` | array of string | no | References of the answered email followed by its `messageId`. |
| `signature` | string | no | `default` (or omitted): default signature of the sending identity. `none`: no signature, and a signature already in the body is removed. Otherwise a signature id from `GET /signatures` (unknown id: 404 SIGNATURE_NOT_FOUND). A body already signed is not signed twice. Default: `"default"`. Min length 1. |

```json
{
  "mailboxId": "cmbxacme0000000000000001",
  "to": [
    {
      "email": "jane.roe@example.com",
      "name": "Jane Roe"
    }
  ],
  "subject": "Re: Partnership with Acme",
  "inReplyTo": "<CAJacme.0001@mail.example.com>",
  "references": [
    "<20261008091500.acme01@acme.com>",
    "<CAJacme.0001@mail.example.com>"
  ],
  "body": {
    "text": "Hello Jane,\n\nTuesday 10am works for me.",
    "html": "<p>Hello Jane,</p><p>Tuesday 10am works for me.</p>"
  },
  "signature": "default"
}
```

## Example request

**cURL**

```bash
curl -X POST "https://api.emelia.io/inbox/api/v1/drafts/send" \
  -H "Authorization: $EMELIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "mailboxId": "cmbxacme0000000000000001",
  "to": [
    {
      "email": "jane.roe@example.com",
      "name": "Jane Roe"
    }
  ],
  "subject": "Re: Partnership with Acme",
  "inReplyTo": "<CAJacme.0001@mail.example.com>",
  "references": [
    "<20261008091500.acme01@acme.com>",
    "<CAJacme.0001@mail.example.com>"
  ],
  "body": {
    "text": "Hello Jane,\n\nTuesday 10am works for me.",
    "html": "<p>Hello Jane,</p><p>Tuesday 10am works for me.</p>"
  },
  "signature": "default"
}'
```

**JavaScript**

```js
const response = await fetch('https://api.emelia.io/inbox/api/v1/drafts/send', {
  method: 'POST',
  headers: { Authorization: process.env.EMELIA_API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    "mailboxId": "cmbxacme0000000000000001",
    "to": [
      {
        "email": "jane.roe@example.com",
        "name": "Jane Roe"
      }
    ],
    "subject": "Re: Partnership with Acme",
    "inReplyTo": "<CAJacme.0001@mail.example.com>",
    "references": [
      "<20261008091500.acme01@acme.com>",
      "<CAJacme.0001@mail.example.com>"
    ],
    "body": {
      "text": "Hello Jane,\n\nTuesday 10am works for me.",
      "html": "<p>Hello Jane,</p><p>Tuesday 10am works for me.</p>"
    },
    "signature": "default"
  }),
});
console.log(await response.json());
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://api.emelia.io/inbox/api/v1/drafts/send",
    headers={"Authorization": os.environ["EMELIA_API_KEY"]},
    json={
        "mailboxId": "cmbxacme0000000000000001",
        "to": [
            {
                "email": "jane.roe@example.com",
                "name": "Jane Roe"
            }
        ],
        "subject": "Re: Partnership with Acme",
        "inReplyTo": "<CAJacme.0001@mail.example.com>",
        "references": [
            "<20261008091500.acme01@acme.com>",
            "<CAJacme.0001@mail.example.com>"
        ],
        "body": {
            "text": "Hello Jane,\n\nTuesday 10am works for me.",
            "html": "<p>Hello Jane,</p><p>Tuesday 10am works for me.</p>"
        },
        "signature": "default"
    },
)
print(response.json())
```

## Responses

### 201: Sent.

| Field | Type | Required | Description |
|---|---|---|---|
| `messageId` | string | yes | RFC Message-ID of the sent message. |
| `threadId` | string or null | yes |  |

```json
{
  "messageId": "<20261008100233.acme02@acme.com>",
  "threadId": "18c2acme0001"
}
```

### 400: `VALIDATION_FAILED` (invalid payload), `MAILBOX_INACTIVE` (mailbox disconnected) or `INVALID_SENDER_ALIAS` (`from` is not a verified send-as alias of the mailbox).

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 401: Missing, malformed, unknown or disabled key. No `X-RateLimit-*` headers.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 403: `WORKSPACE_SUSPENDED`: the Emelia subscription has expired.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 404: `MAILBOX_NOT_FOUND` or `SIGNATURE_NOT_FOUND`.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 409: `CONFLICT`: Idempotency-Key reused with a different request, or the original call is still in progress.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 413: `PAYLOAD_TOO_LARGE`: JSON body above 256 KB.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 429: `RATE_LIMIT_EXCEEDED`: per minute limit of the workspace (wait `Retry-After`). `QUOTA_EXCEEDED`: monthly quota exhausted until the 1st of next month UTC (in Emelia the plan cannot be upgraded).

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```

### 500: `INTERNAL_ERROR`: the provider refused the message, or a step failed AFTER the message left (see known issue). The idempotency key is released in both cases.

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | enum: `INVALID_API_KEY`, `WORKSPACE_SUSPENDED`, `FORBIDDEN`, `PLAN_REQUIRED`, `VALIDATION_FAILED`, `REQUEST_ERROR`, `PAYLOAD_TOO_LARGE`, `NOT_FOUND`, `EMAIL_NOT_FOUND`, `DRAFT_NOT_FOUND`, `MAILBOX_NOT_FOUND`, `LABEL_NOT_FOUND`, `VIEW_NOT_FOUND`, `RULE_NOT_FOUND`, `FILTER_NOT_FOUND`, `SIGNATURE_NOT_FOUND`, `MAILBOX_INACTIVE`, `INVALID_SENDER_ALIAS`, `NO_TRASH_FOLDER`, `INVALID_TAG`, `CONFLICT`, `DUPLICATE_TAG_NAME`, `DUPLICATE_RULE`, `DUPLICATE_FILTER`, `SCHEDULED_SEND_IN_PROGRESS`, `DRAFT_ALREADY_EXISTS`, `QUERY_TIMEOUT`, `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED`, `INTERNAL_ERROR` | yes | Codes that the Inbox API can return, as found in the code. Branch on `code`, never on `error`. The HTTP status is derived from the code (unknown business codes are 400). |
| `error` | string | yes | Human readable. |
| `details` | object | no | Optional context: faulty fields, bounds, ids. |

```json
{
  "code": "INVALID_API_KEY",
  "error": "string",
  "details": {}
}
```
