Email from workers
Send email from your worker code with the Ploy Email binding.
Email from workers
Workers can send email through Ploy Email with a binding. Ploy sends from your organization's verified domains and applies the same approval, suppression and limits as every other email your organization sends.
Sending requires email access for your organization
and a verified sending domain: your <name>.ploy.email domain or a custom
domain.
Configuration
Add an email section to your ploy.yaml:
kind: worker
build: pnpm build
out: dist
email:
binding: EMAIL
from: "Acme <hello@acme.ploy.email>"
allowed_from:
- "*@acme.ploy.email"
- "support@mail.acme.com"| Key | Description |
|---|---|
binding | Name of the binding in your worker's env, uppercase with underscores. Required when from or allowed_from is set. |
from | Default sender, hello@acme.com or Acme <hello@acme.com>. Used when send() is called without from. |
allowed_from | Senders the worker may use: exact addresses or a whole domain as *@domain. Without it, any address on a verified domain of the organization works. |
The settings are taken from your production deployment's ploy.yaml. Preview
deployments use the same settings as production.
Run ploy types to add EMAIL: EmailBinding to your worker's
Env type.
Sending email
Call send() on the binding:
export default {
async fetch(request: Request, env: Env) {
const { id } = await env.EMAIL.send({
to: "jane@example.com",
subject: "Your order has shipped",
text: "Your order #1234 is on its way.",
html: "<p>Your order <strong>#1234</strong> is on its way.</p>",
});
return Response.json({ id });
},
};send() resolves with the ID of the message once Ploy has accepted it for
delivery.
| Option | Type | Description |
|---|---|---|
to | string | string[] | Recipients. to, cc and bcc together hold at most 50 addresses. |
cc | string | string[] | Optional copy recipients. |
bcc | string | string[] | Optional blind copy recipients. |
from | string | Sender. Defaults to email.from. Must be on a verified domain and allowed by allowed_from. |
replyTo | string | Optional Reply-To address. |
subject | string | Subject line, required. |
text | string | Plain-text body. Pass text, html or both. |
html | string | HTML body. |
headers | object | Extra headers: In-Reply-To, References, List-Id, List-Unsubscribe, List-Unsubscribe-Post and custom X-* headers. |
An email goes out to all of its recipients or to none: if any recipient is refused, for example because it is on the suppression list, nothing is sent. Every recipient counts towards your project's daily limit.
Errors
When Ploy refuses an email, send() throws an error with a code:
try {
await env.EMAIL.send({ to, subject, text });
} catch (error) {
if (error instanceof Error && "code" in error) {
console.warn("Email refused:", error.code, error.message);
}
throw error;
}| Code | Meaning |
|---|---|
invalid_request | The message is malformed, for example without a subject or body. |
invalid_sender | No sender was given and email.from is not set, or the address is invalid. |
sender_not_allowed | The sender is not covered by email.allowed_from. |
invalid_recipient | A recipient address is invalid. |
too_many_recipients | More than 50 recipients. |
access_not_approved | Email sending is not enabled for your organization. |
domain_not_verified | The sender's domain is not a verified domain of your organization. |
recipient_suppressed | A recipient bounced or marked earlier mail as spam. |
quota_exceeded | The project reached its daily limit. |
recipient_rate_limited | Too many emails to the same address within an hour. |
send_failed | The email could not be sent. Retrying later may work. |
Local development
Under ploy dev, nothing is sent. send() checks the message
against your ploy.yaml like in production, and every email lands in the
local inbox: open the dev dashboard and go to Email to read it.
How is this guide?
Last updated on