Mailtrap
Mailtrap provides both a production Email API and an Email Sandbox for capturing test messages in a virtual inbox. The HTTP API supports HTML and text content, attachments, custom headers, categories, and custom variables.
@alexify/mailer provides the Mailtrap transport through the @alexify/mailer/mailtrap subpath. It supports single sends, batch sends of up to 500 messages per request, attachments, retry logic, and AbortSignal cancellation.
Installation
To use the Mailtrap transport, install the @alexify/mailer/mailtrap subpath:
pnpm add @alexify/mailer
# or
npm install @alexify/mailerGetting started
Before using the Mailtrap transport, you'll need a Mailtrap API token. The token is sent to Mailtrap as the Api-Token request header.
const { createMessage } = require('@alexify/mailer');
const { MailtrapTransport } = require('@alexify/mailer/mailtrap');
const transport = new MailtrapTransport({
apiToken: 'your-mailtrap-api-token',
sandbox: true,
inboxId: 12345,
});
const message = createMessage({
from: 'support@example.com',
to: 'customer@example.com',
subject: 'Welcome to our service',
content: { text: 'Thank you for signing up!' },
});
const receipt = await transport.send(message);
if (receipt.successful) {
console.log('Message sent with ID:', receipt.messageId);
} else {
console.error('Send failed:', receipt.errorMessages.join(', '));
}The transport converts @alexify/mailer messages to Mailtrap's JSON format and sends them through the /api/send endpoint (or /api/send/{inboxId} in sandbox mode). HTML and text alternatives, CC, BCC, reply-to, custom headers, priority, attachments, and inline Content-ID attachments are handled automatically.
Email API and Email Sandbox
Mailtrap uses one API token for both environments. Set sandbox: true and provide an inboxId to capture messages in a test inbox instead of sending through the production Email API:
const { createMessage } = require('@alexify/mailer');
const { MailtrapTransport } = require('@alexify/mailer/mailtrap');
const sandboxTransport = new MailtrapTransport({
apiToken: 'your-mailtrap-api-token',
sandbox: true,
inboxId: 12345,
});
const productionTransport = new MailtrapTransport({
apiToken: 'your-mailtrap-api-token',
});
const message = createMessage({
from: 'onboarding@example.com',
to: 'newuser@example.com',
subject: 'Welcome',
content: { text: 'Welcome to our platform.' },
});sandbox: Whentrue, messages are sent to the Email Sandbox API atsandbox.api.mailtrap.ioand captured in the inbox identified byinboxId.inboxId: Sandbox inbox ID frommailtrap.io/sandboxes/{id}. Required whensandboxistrue.
Categories, tags, and metadata
Mailtrap uses a category field for message classification. @alexify/mailer maps the first tag in Message.tags to category, or falls back to defaultCategory (default: transactional). Additional tags become custom variables prefixed with tag_. Configure transport-level metadata when every message should carry the same tracking fields:
const { createMessage } = require('@alexify/mailer');
const { MailtrapTransport } = require('@alexify/mailer/mailtrap');
const transport = new MailtrapTransport({
apiToken: 'your-mailtrap-api-token',
defaultCategory: 'transactional',
metadata: {
environment: 'production',
service: 'accounts',
},
});
const message = createMessage({
from: 'billing@example.com',
to: 'customer@example.com',
subject: 'Your invoice',
content: { text: 'Your invoice is ready.' },
tags: ['billing', 'invoice'],
});A message's own metadata goes in custom_variables too, its keys over those of the configuration. Mailtrap cannot schedule a message, so scheduledAt is refused as unsupported, and it sets tracking per domain, so a message's tracking is not sent.
Batch sending
Use sendMany() to send multiple messages. The transport batches up to 500 messages per API call:
const { createMessage } = require('@alexify/mailer');
const { MailtrapTransport } = require('@alexify/mailer/mailtrap');
const transport = new MailtrapTransport({
apiToken: "your-mailtrap-api-token",
sandbox: true,
inboxId: 12345,
});
const messages = [
createMessage({
from: "sender@example.com",
to: "user1@example.com",
subject: "Hello 1",
content: { text: "Message 1" },
}),
createMessage({
from: "sender@example.com",
to: "user2@example.com",
subject: "Hello 2",
content: { text: "Message 2" },
}),
];
for await (const receipt of transport.sendMany(messages)) {
if (receipt.successful) {
console.log("Sent:", receipt.messageId);
} else {
console.error("Failed:", receipt.errorMessages.join(", "));
}
}Configuration options
apiToken: Your Mailtrap API token.sandbox: Use Email Sandbox instead of Email API. Default:false.inboxId: Sandbox inbox ID. Required whensandboxistrue.sendBaseUrl: Email API base URL. Default:https://send.api.mailtrap.io.sandboxBaseUrl: Sandbox API base URL. Default:https://sandbox.api.mailtrap.io.defaultCategory: Default category when a message has no tags. Default:transactional.metadata: Metadata merged into Mailtrapcustom_variablesfor every message.userAgent: User-Agent header sent with requests. Default:@alexify/mailer/mailtrap.timeout: Request timeout in milliseconds. Default:30000.retries: Number of retry attempts for transient failures. Default:3.headers: Additional HTTP headers to include with every request.
Verification
This transport has no verify(): Mailtrap offers no request that checks a sending key without sending a message. isVerifiableTransport() is false for it, and ./pool, ./retry, ./otel and ./logger have nothing to check when they wrap it.