---
question: How do I send mail from a module?
kind: task
via_web: available
via_cli: na
via_python: available
via_api: available
via_config: na
ref_web: System › Data › Email
ref_python: email.send
ref_api: PUT /api/admin/system/resources/email/entry
prereq: An email adapter in `config.yaml`. See [Choose a provider](/user-guide/build/platform/choose-an-email-provider?via=config).
next:
  - /capabilities/build/platform/choose-an-email-provider
  - /capabilities/build/platform/email-overview
---

# Send an email

A send hands one message to the configured adapter: an SMTP server, a provider API, or `.eml` files on disk. A successful send publishes `EmailSent`. A failure publishes `EmailError` and raises.

## Web UI

Super admins only. Use it to test the adapter, or to send a one-off message as the platform.

### Open the email view

In Nexus, open **System** in the sidebar, then the **Data** tab, then **Email**.

![System app, Data tab, on Email](/abi/Screenshot_System_Email.png)

*System › Data › Email. Compose sends through the configured adapter; this project has sent none.*

### Compose the message

Choose **Compose**. Fill **To** (one or more addresses, comma-separated), **Cc** if needed, **Subject** and the message. Tick **Also send an HTML version** to add an HTML body, with a **Preview**.

### Send it

Choose **Send**. The message comes from the `naas_abi` module's `nexus_config.email_from_address`. With the `filesystem` adapter it is listed in the view, newest first: open it to read it or download the `.eml`. With any other adapter the list stays empty, because nothing is kept.

## CLI

Not applicable. There is no `abi` command that sends mail. Use another tab on this page. The command list is the [CLI reference](/user-guide/reference/cli).

## Python

### Declare the service

```python title="src/my_module/__init__.py"
from naas_abi_core.module.Module import BaseModule, ModuleDependencies
from naas_abi_core.services.email.EmailService import EmailService


class ABIModule(BaseModule):
    dependencies: ModuleDependencies = ModuleDependencies(
        modules=[],
        services=[EmailService],
    )
```

### Send

```python
message_id = self.engine.services.email.send(
    to_email="reader@example.com",
    subject="Weekly report",
    text_body="The report is attached.",
    from_email="reports@example.com",
)
```

Every argument is a keyword. `subject`, `text_body` and `from_email` are required, and at least one recipient in `to_email` or `to_emails`. `html_body`, `from_name`, `reply_to` and `cc_emails` are optional.

### Attach a file

```python
from naas_abi_core.services.email.EmailPorts import EmailAttachment

report = EmailAttachment(
    filename="report.pdf",
    content=pdf_bytes,
    mime_type="application/pdf",
)
self.engine.services.email.send(
    to_emails=["ops@example.com", "cto@example.com"],
    subject="Weekly report",
    text_body="The report is attached.",
    from_email="reports@example.com",
    attachments=[report],
)
```

Set `content_id` and `is_inline=True` on an attachment to show it inside the HTML body as `<img src="cid:...">`.

### Read what was kept

`send` returns the id of the kept copy, or `None` when the adapter keeps none. Only `filesystem` keeps copies:

```python
for summary in self.engine.services.email.list_sent(limit=20):
    print(summary.message_id, summary.subject, summary.to)
```

`get_sent(message_id)` returns the summary and the raw `.eml` bytes. `delete_sent(message_id)` removes the copy; it does not recall the message. On other adapters the three calls raise `SentEmailsNotKept`.

## REST API

The System routes of the Nexus API. Auth: a Nexus session of a super admin (`POST /api/auth/login` returns the access token). Every send is audited.

### Send a message

The body is a JSON message. `id` is only a label for the audit log:

```bash
curl -X PUT "http://localhost:9879/api/admin/system/resources/email/entry?id=weekly-report" \
  -H "Authorization: Bearer $NEXUS_TOKEN" \
  --data-binary '{"to": ["reader@example.com"], "subject": "Weekly report", "text": "Hello."}'
```

With `abi dev up`, use the `api` port that `abi dev ports` prints. The message needs `to`, a `subject` and `text` or `html`. `cc`, `reply_to`, `from` and `from_name` are optional; `from` defaults to `nexus_config.email_from_address`. The reply is the kept copy, or an entry whose `status` says the message was sent but not kept.

### List and read kept mail

```http
GET /api/admin/system/resources/email/entries
GET /api/admin/system/resources/email/entry?id=<message_id>
GET /api/admin/system/resources/email/download?id=<message_id>
```

`download` returns the raw `.eml`. A sent message cannot be replaced. `DELETE` on `entry` removes a kept copy, with `confirm` set to the id.

## Config

Not applicable. A send is a call. The adapter it goes through is [Choose a provider](/user-guide/build/platform/choose-an-email-provider?via=config).
