Frameworks and agents · Published 2026-11-09 · Updated 2026-11-09 · AgentiSend
Send email with Nodemailer
Send email with Nodemailer through an API. The transport posts the message to POST /emails and returns the message id, or throws the refusal's fix.
To send email with Nodemailer through an API, pass createTransport() from nodemailer-agentisend-transport to nodemailer.createTransport. Nodemailer still builds the message. The transport posts that message to POST /emails and returns the id from the response as messageId. A refusal is thrown with the API's fix on the error, so the caller can read what to change.
Install
npm install nodemailer nodemailer-agentisend-transportEnvironment
| Variable | Required | What it is |
|---|---|---|
AGENTISEND_API_KEY | yes | A key with sending_access. createTransport({ apiKey }) overrides it. |
MAIL_FROM | yes | The From address, on a domain you have verified. |
AGENTISEND_BASE_URL | no | Defaults to https://api.agentisend.com. createTransport({ baseUrl }) overrides it. |
The swap
import nodemailer from 'nodemailer';
import { createTransport } from 'nodemailer-agentisend-transport';
const transporter = nodemailer.createTransport(createTransport());
const info = await transporter.sendMail({ from, to, subject, text });
console.log(info.messageId);from, to, subject and text are the fields Nodemailer already takes. createTransport() reads the key and the base URL from the environment.
What is posted
replyTo is sent as reply_to. An attachment's bytes go out as base64 content, with filename, content_type and content_id when the message had them. A list.unsubscribe value is sent as a List-Unsubscribe header, and every other header set on the message is sent in headers. The id in the response is info.messageId. Read that message with GET /emails/{id}. Create the key the transport sends with POST /api-keys.
curl -sS -X POST https://api.agentisend.com/emails \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"from":"billing@yourdomain.com","to":"delivered@simulator.agentisend.com","subject":"Your receipt","text":"Thanks — the details are attached."}'201
{
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"simulated": true,
"warnings": []
}curl -sS -X GET https://api.agentisend.com/emails/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42 \
-H "Authorization: Bearer $AGENTISEND_API_KEY"200
{
"api_key_id": "example",
"attachments": [],
"bcc": [],
"cc": [],
"created_at": "2026-09-04T09:14:00.000Z",
"domain": "example",
"from": "example",
"headers": {},
"html": "example",
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"last_event": "queued",
"message_id": "example",
"object": "email",
"provider_message_id": "example",
"reply_to": [],
"scheduled_at": "2026-09-04T09:14:00.000Z",
"status": "queued",
"subject": "example",
"tags": {},
"template_id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"template_version": -9007199254740991,
"text": "example",
"to": [],
"updated_at": "2026-09-04T09:14:00.000Z",
"warnings": []
}curl -sS -X POST https://api.agentisend.com/api-keys \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name":"example","permission":"full_access"}'201
{
"budget_per_period": 5000,
"created_at": "2026-09-04T09:14:00.000Z",
"domain_scope": "example",
"expires_at": "2026-09-04T09:14:00.000Z",
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"last_used_at": "2026-09-04T09:14:00.000Z",
"loop_guard": "off",
"name": "example",
"object": "api_key",
"period": "monthly",
"permission": "full_access",
"previous_key_expires_at": "2026-09-04T09:14:00.000Z",
"rate_ceiling_per_minute": 600,
"request_count_30d": -9007199254740991,
"rotated_at": "2026-09-04T09:14:00.000Z",
"scopes": [],
"system": false,
"token": "as_abababababababababababababababababababababababababababababababab",
"token_prefix": "as_abababab"
}What comes back when it goes wrong
Every refused request carries code, message, fix and docs_url. The thrown error's fix is that field, unchanged, and its code is the catalogue code. The error catalogue lists every code.