Sending mail from a function
substrio.mail.smtp(options) sends one message over SMTP and resolves with { ok, code, message }.
A server refusal resolves with ok: false, the SMTP code, and the server's message. A call with
no options object or a missing message throws a TypeError or MISSING_FIELD error.
export default async function (request) {
const result = await substrio.mail.smtp({
host: "smtp.example.com",
port: 587,
user: env.SMTP_USER,
pass: env.SMTP_PASS,
from: "[email protected]",
to: "[email protected]",
message: "Subject: Your order shipped\r\nFrom: [email protected]\r\nTo: [email protected]\r\n\r\nOn its way.\r\n",
});
return Response.json(result);
}
| Option | Type | Default | Meaning |
|---|---|---|---|
host |
string | (required) | the SMTP server |
port |
number | 587, or 465 when secure |
the SMTP port |
secure |
boolean | false |
TLS from the first byte (port 465) instead of STARTTLS |
user, pass |
string | none | SMTP login |
from |
string | (required) | the envelope sender |
to |
string or string[] | (required) | one or more recipients, each its own RCPT |
message |
string | (required) | the whole message, headers and body (RFC 5322) |
requireTLS |
boolean | true |
refuse a server with no STARTTLS when secure is false |
verifyTLS |
boolean | true |
verify the server's certificate |
encode |
boolean | true |
normalize line endings to CRLF |
timeoutMs |
number | none | the send's timeout |
host, from, and at least one to address are required. If any is missing, the call resolves with
ok: false and code: 0. The send uses the app's outbound restrictions and private-address allowlist,
as fetch() does. It also counts against the call's outbound fetch limit, so repeated sends can exhaust
that budget. message is limited to 16 MB; a larger message is refused with VALUE_TOO_LARGE. Store
the SMTP login as secrets and read it from env instead of writing it into function source.
The mail your app's sign-in flow sends
Substrio does not send mail on its own behalf. Name one of your functions as email_function in the
[auth] table of substrio.toml, and it is called whenever a sign-in flow needs a mail sent: a password
reset, an email verification, or an account invite.
[auth]
email_function = "send_auth_mail"
reset_url = "https://app.example/reset?token={token}"
verify_url = "https://app.example/verify?token={token}"
export default async function send_auth_mail(event) {
// event: { kind, email, token, link, expires_at, app, user_id }
await substrio.mail.smtp({
host: "smtp.example.com",
user: env.SMTP_USER,
pass: env.SMTP_PASS,
from: "[email protected]",
to: event.email,
message: `Subject: Confirm your action\r\nFrom: [email protected]\r\nTo: ${event.email}\r\n\r\n${event.link}\r\n`,
});
}
| Field | Meaning |
|---|---|
kind |
password_reset, email_verify or account_invite |
email |
where to send it |
token |
the single-use token, redeemed by POST /v1/_auth/password/reset or /v1/_auth/email/verify |
link |
reset_url or verify_url with {token} filled in (an invite's link comes from invite_url); empty when the setting is empty |
expires_at |
when the token stops working, in Unix seconds |
app |
the app's slug |
user_id |
the user the token was issued to (absent for account_invite) |
account_name, inviter_email, role |
present only for account_invite, in place of user_id |
For a password reset, verification, or invite, the function's result is not read and a failure does not
reach the caller. A reset request immediately returns 202 {"accepted": true} whether or not the address
has an account, so addresses cannot be probed. Read a failed send in the function's logs. Without an
email_function, a password reset or verification request returns 409.
A sign-in code request waits for the function's result and can return RECIPIENT_UNSUBSCRIBED when the
function refuses the email. See authentication
for the response and requirements.
curl -s -X POST https://my-app.substrio.com/v1/_auth/password/reset_request \
-H "Content-Type: application/json" -d '{"email": "[email protected]"}'
See authentication for the full sign-in, reset and invite flow, and the routes that redeem a token.