エージェントは Cloudflare Email Service でメールを送受信できます。このガイドでは、Workers バインディングで送信メールを送り、受信メールを Agent へルーティングし、後続の返信を安全に扱う方法を示します。
エージェントでメールを使う前に、次が必要です。
- Cloudflare Email Service にオンボードしたドメイン。
- 送信メール用の
wrangler.jsonc内のsend_emailバインディング。 - 受信メールを Worker へ送る Email Service のルーティングルール。
- 任意: 安全な返信ルーティングが必要な場合の
EMAIL_SECRETシークレット。
- Cloudflare Dashboard ↗ にログインします。
- Compute & AI > Email Service を開きます。
- Onboard Domain を選び、ドメインを選択します。
- 送信を許可するため、DNS レコード(SPF と DKIM)を追加します。
DNS の変更は、Cloudflare DNS を使うドメインでは通常 5〜15 分で完了します。世界中への伝播には最大 24 時間かかることがあります。
Worker にメールバインディングを追加します。
{
"$schema": "./node_modules/wrangler/config-schema.json",
"send_email": [
{
"name": "EMAIL",
"remote": true
}
]
}[[send_email]]
name = "EMAIL"
remote = trueremote = true を指定すると、wrangler dev でのローカル開発中に実際の Email Service API を呼び出せます。
import { Agent, callable, routeAgentEmail } from "agents";
import { createAddressBasedEmailResolver } from "agents/email";
import PostalMime from "postal-mime";
export class EmailAgent extends Agent {
@callable()
async sendWelcomeEmail(to) {
await this.sendEmail({
binding: this.env.EMAIL,
to,
from: "[email protected]",
replyTo: "[email protected]",
subject: "Welcome to our service",
text: "Thanks for signing up. Reply to this email if you need help.",
});
}
async onEmail(email) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
console.log("Received email from:", email.from);
console.log("Subject:", parsed.subject);
await this.replyToEmail(email, {
fromName: "Support Agent",
body: "Thanks for your email! We received it.",
});
}
}
export default {
async email(message, env) {
await routeAgentEmail(message, env, {
resolver: createAddressBasedEmailResolver("EmailAgent"),
});
},
};import { Agent, callable, routeAgentEmail } from "agents";
import { createAddressBasedEmailResolver, type AgentEmail } from "agents/email";
import PostalMime from "postal-mime";
export class EmailAgent extends Agent {
@callable()
async sendWelcomeEmail(to: string) {
await this.sendEmail({
binding: this.env.EMAIL,
to,
from: "[email protected]",
replyTo: "[email protected]",
subject: "Welcome to our service",
text: "Thanks for signing up. Reply to this email if you need help.",
});
}
async onEmail(email: AgentEmail) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
console.log("Received email from:", email.from);
console.log("Subject:", parsed.subject);
await this.replyToEmail(email, {
fromName: "Support Agent",
body: "Thanks for your email! We received it.",
});
}
}
export default {
async email(message, env) {
await routeAgentEmail(message, env, {
resolver: createAddressBasedEmailResolver("EmailAgent"),
});
},
} satisfies ExportedHandler<Env>;sendEmail() は、明示的に渡した send_email バインディング経由で送信メールを送ります。すべてのメッセージにエージェントルーティングヘッダー(X-Agent-Name、X-Agent-ID)を自動で注入します。任意で HMAC-SHA256 により署名し、返信を同じエージェントインスタンスへ戻せます。
class MyAgent extends Agent {
@callable()
async sendReceipt(to, orderId) {
const result = await this.sendEmail({
binding: this.env.EMAIL,
to,
from: { email: "[email protected]", name: "Billing Bot" },
replyTo: "[email protected]",
subject: `Receipt for order ${orderId}`,
text: `Your receipt for order ${orderId} is ready.`,
secret: this.env.EMAIL_SECRET,
});
return result.messageId;
}
}class MyAgent extends Agent {
@callable()
async sendReceipt(to: string, orderId: string) {
const result = await this.sendEmail({
binding: this.env.EMAIL,
to,
from: { email: "[email protected]", name: "Billing Bot" },
replyTo: "[email protected]",
subject: `Receipt for order ${orderId}`,
text: `Your receipt for order ${orderId} is ready.`,
secret: this.env.EMAIL_SECRET,
});
return result.messageId;
}
}secret を渡すと、エージェントはルーティングヘッダーに署名します。createSecureReplyEmailResolver が検証した返信は、同じエージェントインスタンスへ戻ります。
受信者が同じエージェントと会話を続けられるようにしたいときは、replyTo を Worker へ戻るメールボックスに設定します。
resolver は、受信メールをどの Agent インスタンスが受け取るかを決めます。用途に合う resolver を選びます。
Email Service の基本的な送受信だけなら、createAddressBasedEmailResolver() で十分です。次の安全な返信 resolver は任意で、Agents SDK の返信署名に固有です。Email Service 自体の必須要件ではありません。
受信メールに推奨します。宛先アドレスに基づいてメールをルーティングします。
import { createAddressBasedEmailResolver } from "agents/email";
const resolver = createAddressBasedEmailResolver("EmailAgent");import { createAddressBasedEmailResolver } from "agents/email";
const resolver = createAddressBasedEmailResolver("EmailAgent");ルーティングのロジック:
| 宛先アドレス | Agent 名 | Agent ID |
|---|---|---|
[email protected] |
EmailAgent(デフォルト) |
support |
[email protected] |
EmailAgent(デフォルト) |
sales |
[email protected] |
NotificationAgent |
user123 |
サブアドレス形式(agent+id@domain)を使うと、1 つのメールドメインから異なるエージェント名前空間とインスタンスへルーティングできます。
署名検証付きの返信フロー向けです。受信メールが送信メールへの本物の返信であることを検証し、攻撃者が任意のエージェントインスタンスへメールをルーティングするのを防ぎます。
import { createSecureReplyEmailResolver } from "agents/email";
const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET);import { createSecureReplyEmailResolver } from "agents/email";
const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET);エージェントが replyToEmail() または sendEmail() と secret でメールを送ると、タイムスタンプ付きでルーティングヘッダーに署名します。返信が戻ると、この resolver は署名を検証し、期限切れでないことを確認してからルーティングします。
オプション:
const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
// Maximum age of signature in seconds (default: 30 days)
maxAge: 7 * 24 * 60 * 60, // 7 days
// Callback for logging/debugging signature failures
onInvalidSignature: (email, reason) => {
console.warn(`Invalid signature from ${email.from}: ${reason}`);
// reason can be: "missing_headers", "expired", "invalid", "malformed_timestamp"
},
});const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
// Maximum age of signature in seconds (default: 30 days)
maxAge: 7 * 24 * 60 * 60, // 7 days
// Callback for logging/debugging signature failures
onInvalidSignature: (email, reason) => {
console.warn(`Invalid signature from ${email.from}: ${reason}`);
// reason can be: "missing_headers", "expired", "invalid", "malformed_timestamp"
},
});使うとき: エージェントがメール会話を開始し、返信を同じエージェントインスタンスへ安全に戻したい場合。
単一インスタンスへのルーティング向けです。宛先アドレスに関係なく、すべてのメールを特定のエージェントインスタンスへ送ります。
import { createCatchAllEmailResolver } from "agents/email";
const resolver = createCatchAllEmailResolver("EmailAgent", "default");import { createCatchAllEmailResolver } from "agents/email";
const resolver = createCatchAllEmailResolver("EmailAgent", "default");使うとき: すべてのメールを 1 つのエージェントインスタンスが扱う場合(共有受信箱など)。
異なるシナリオを扱うため、resolver を組み合わせられます。
export default {
async email(message, env) {
const secureReplyResolver = createSecureReplyEmailResolver(
env.EMAIL_SECRET,
);
const addressResolver = createAddressBasedEmailResolver("EmailAgent");
await routeAgentEmail(message, env, {
resolver: async (email, env) => {
// First, check if this is a signed reply
const replyRouting = await secureReplyResolver(email, env);
if (replyRouting) return replyRouting;
// Otherwise, route based on recipient address
return addressResolver(email, env);
},
// Handle emails that do not match any routing rule
onNoRoute: (email) => {
console.warn(`No route found for email from ${email.from}`);
email.setReject("Unknown recipient");
},
});
},
};export default {
async email(message, env) {
const secureReplyResolver = createSecureReplyEmailResolver(
env.EMAIL_SECRET,
);
const addressResolver = createAddressBasedEmailResolver("EmailAgent");
await routeAgentEmail(message, env, {
resolver: async (email, env) => {
// First, check if this is a signed reply
const replyRouting = await secureReplyResolver(email, env);
if (replyRouting) return replyRouting;
// Otherwise, route based on recipient address
return addressResolver(email, env);
},
// Handle emails that do not match any routing rule
onNoRoute: (email) => {
console.warn(`No route found for email from ${email.from}`);
email.setReject("Unknown recipient");
},
});
},
} satisfies ExportedHandler<Env>;エージェントの onEmail メソッドが呼ばれると、AgentEmail オブジェクトを受け取ります。
type AgentEmail = {
from: string; // Sender's email address
to: string; // Recipient's email address
headers: Headers; // Email headers (subject, message-id, etc.)
rawSize: number; // Size of the raw email in bytes
getRaw(): Promise<Uint8Array>; // Get the full raw email content
reply(options): Promise<void>; // Send a reply
forward(rcptTo, headers?): Promise<void>; // Forward the email
setReject(reason): void; // Reject the email with a reason
};生メールの解析には postal-mime ↗ のようなライブラリを使います。
import PostalMime from "postal-mime";
class MyAgent extends Agent {
async onEmail(email) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
console.log("Subject:", parsed.subject);
console.log("Text body:", parsed.text);
console.log("HTML body:", parsed.html);
console.log("Attachments:", parsed.attachments);
}
}import PostalMime from "postal-mime";
class MyAgent extends Agent {
async onEmail(email: AgentEmail) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
console.log("Subject:", parsed.subject);
console.log("Text body:", parsed.text);
console.log("HTML body:", parsed.html);
console.log("Attachments:", parsed.attachments);
}
}メールループを避けるため、isAutoReplyEmail() で自動返信メールを検出します。
import { isAutoReplyEmail } from "agents/email";
import PostalMime from "postal-mime";
class MyAgent extends Agent {
async onEmail(email) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
// Detect auto-reply emails to avoid sending duplicate responses
if (isAutoReplyEmail(parsed.headers)) {
console.log("Skipping auto-reply email");
return;
}
// Process the email...
}
}import { isAutoReplyEmail } from "agents/email";
import PostalMime from "postal-mime";
class MyAgent extends Agent {
async onEmail(email: AgentEmail) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
// Detect auto-reply emails to avoid sending duplicate responses
if (isAutoReplyEmail(parsed.headers)) {
console.log("Skipping auto-reply email");
return;
}
// Process the email...
}
}これは、メールが自動返信であることを示す標準の RFC 3834 ヘッダー(Auto-Submitted、X-Auto-Response-Suppress、Precedence)を確認します。
受信メールの返信チャネル経由で返信するには、this.replyToEmail() を使います。
class MyAgent extends Agent {
async onEmail(email) {
await this.replyToEmail(email, {
fromName: "Support Bot", // Display name for the sender
subject: "Re: Your inquiry", // Optional, defaults to "Re: "
body: "Thanks for contacting us!", // Email body
contentType: "text/plain", // Optional, defaults to "text/plain"
headers: {
// Optional custom headers
"X-Custom-Header": "value",
},
secret: this.env.EMAIL_SECRET, // Optional, signs headers for secure reply routing
});
}
}class MyAgent extends Agent {
async onEmail(email: AgentEmail) {
await this.replyToEmail(email, {
fromName: "Support Bot", // Display name for the sender
subject: "Re: Your inquiry", // Optional, defaults to "Re: "
body: "Thanks for contacting us!", // Email body
contentType: "text/plain", // Optional, defaults to "text/plain"
headers: {
// Optional custom headers
"X-Custom-Header": "value",
},
secret: this.env.EMAIL_SECRET, // Optional, signs headers for secure reply routing
});
}
}replyToEmail() は生きた AgentEmail オブジェクトが必要なため、onEmail() の中でのみ使えます。あとから返信したい場合(スケジュールタスク、callable メソッド、human-in-the-loop の承認後など)は、送信者情報を状態に保存し、sendEmail() を使います。
class MyAgent extends Agent {
async onEmail(email) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
this.setState({
...this.state,
pendingReply: {
to: email.from,
messageId: parsed.messageId,
subject: parsed.subject,
},
});
}
@callable()
async sendDelayedReply(body) {
const { pendingReply } = this.state;
if (!pendingReply) return;
await this.sendEmail({
binding: this.env.EMAIL,
to: pendingReply.to,
from: "[email protected]",
subject: `Re: ${pendingReply.subject}`,
text: body,
inReplyTo: pendingReply.messageId,
secret: this.env.EMAIL_SECRET,
});
}
}class MyAgent extends Agent {
async onEmail(email: AgentEmail) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
this.setState({
...this.state,
pendingReply: {
to: email.from,
messageId: parsed.messageId,
subject: parsed.subject,
},
});
}
@callable()
async sendDelayedReply(body: string) {
const { pendingReply } = this.state;
if (!pendingReply) return;
await this.sendEmail({
binding: this.env.EMAIL,
to: pendingReply.to,
from: "[email protected]",
subject: `Re: ${pendingReply.subject}`,
text: body,
inReplyTo: pendingReply.messageId,
secret: this.env.EMAIL_SECRET,
});
}
}inReplyTo フィールドは In-Reply-To ヘッダーを設定し、メールクライアントがスレッドを正しく組み立てます。secret はエージェントルーティングヘッダーに署名するため、後続の返信は createSecureReplyEmailResolver 経由でこのエージェントインスタンスへ戻ります。
class MyAgent extends Agent {
async onEmail(email) {
await email.forward("[email protected]");
}
}class MyAgent extends Agent {
async onEmail(email: AgentEmail) {
await email.forward("[email protected]");
}
}class MyAgent extends Agent {
async onEmail(email) {
if (isSpam(email)) {
email.setReject("Message rejected as spam");
return;
}
// Process the email...
}
}class MyAgent extends Agent {
async onEmail(email: AgentEmail) {
if (isSpam(email)) {
email.setReject("Message rejected as spam");
return;
}
// Process the email...
}
}sendEmail() または replyToEmail() でメールを送るときは、次のよくあるエラーを扱います。
class MyAgent extends Agent {
async onEmail(email) {
try {
await this.replyToEmail(email, {
fromName: "Support Bot",
body: "Thanks for your email!",
});
} catch (error) {
switch (error.code) {
case "E_SENDER_NOT_VERIFIED":
console.error("Sender domain not verified. Verify in dashboard.");
break;
case "E_RATE_LIMIT_EXCEEDED":
console.error("Rate limit exceeded. Back off and retry.");
break;
case "E_DAILY_LIMIT_EXCEEDED":
console.error("Daily sending quota reached.");
break;
case "E_CONTENT_TOO_LARGE":
console.error("Email content exceeds size limit.");
break;
default:
console.error("Email sending failed:", error.message);
}
}
}
}class MyAgent extends Agent {
async onEmail(email: AgentEmail) {
try {
await this.replyToEmail(email, {
fromName: "Support Bot",
body: "Thanks for your email!",
});
} catch (error) {
switch (error.code) {
case "E_SENDER_NOT_VERIFIED":
console.error("Sender domain not verified. Verify in dashboard.");
break;
case "E_RATE_LIMIT_EXCEEDED":
console.error("Rate limit exceeded. Back off and retry.");
break;
case "E_DAILY_LIMIT_EXCEEDED":
console.error("Daily sending quota reached.");
break;
case "E_CONTENT_TOO_LARGE":
console.error("Email content exceeds size limit.");
break;
default:
console.error("Email sending failed:", error.message);
}
}
}
}| エラーコード | 説明 | 対処 |
|---|---|---|
E_SENDER_NOT_VERIFIED |
送信元ドメイン / アドレスが未検証 | Cloudflare ダッシュボードで検証する |
E_RATE_LIMIT_EXCEEDED |
送信レート制限に達した | 指数バックオフを実装する |
E_DAILY_LIMIT_EXCEEDED |
日間クォータを超えた | クォータリセットを待つか、プランを上げる |
E_CONTENT_TOO_LARGE |
メールがサイズ上限を超えた | 添付または本文を減らす |
E_RECIPIENT_NOT_ALLOWED |
受信者が許可リストにない | 許可された宛先アドレスを確認する |
E_RECIPIENT_SUPPRESSED |
受信者が抑制リストにある | 抑制リストから外す |
E_VALIDATION_ERROR |
メール形式が不正 | メールアドレスを確認する |
E_TOO_MANY_RECIPIENTS |
受信者が 50 件を超えている | 複数回の送信に分割する |
エージェントがメールを送り、返信を期待するときは、安全な返信ルーティングを使います。攻撃者がヘッダーを偽造して任意のエージェントインスタンスへメールをルーティングするのを防ぎます。
- 送信:
replyToEmail()またはsendEmail()をsecret付きで呼ぶと、エージェントはルーティングヘッダー(X-Agent-Name、X-Agent-ID)を HMAC-SHA256 で署名します。 - 受信:
createSecureReplyEmailResolverは、ルーティング前に署名を検証します。 - 強制: メールが安全な resolver 経由でルーティングされた場合、
replyToEmail()はシークレットを要求します(オプトアウトするには明示的にnull)。
-
署名キーを Wrangler シークレットとして保存します。
varsに入れたり、ソース管理にコミットしたりしないでください。npx wrangler secret put EMAIL_SECRETyarn wrangler secret put EMAIL_SECRETpnpm wrangler secret put EMAIL_SECRET -
組み合わせ resolver パターンを使います。
export default { async email(message, env) { const secureReplyResolver = createSecureReplyEmailResolver( env.EMAIL_SECRET, ); const addressResolver = createAddressBasedEmailResolver("EmailAgent"); await routeAgentEmail(message, env, { resolver: async (email, env) => { const replyRouting = await secureReplyResolver(email, env); if (replyRouting) return replyRouting; return addressResolver(email, env); }, }); }, };export default { async email(message, env) { const secureReplyResolver = createSecureReplyEmailResolver( env.EMAIL_SECRET, ); const addressResolver = createAddressBasedEmailResolver("EmailAgent"); await routeAgentEmail(message, env, { resolver: async (email, env) => { const replyRouting = await secureReplyResolver(email, env); if (replyRouting) return replyRouting; return addressResolver(email, env); }, }); }, } satisfies ExportedHandler<Env>; -
送信メールに署名します。
class MyAgent extends Agent { async onEmail(email) { await this.replyToEmail(email, { fromName: "My Agent", body: "Thanks for your email!", secret: this.env.EMAIL_SECRET, // Signs the routing headers }); } }class MyAgent extends Agent { async onEmail(email: AgentEmail) { await this.replyToEmail(email, { fromName: "My Agent", body: "Thanks for your email!", secret: this.env.EMAIL_SECRET, // Signs the routing headers }); } }
メールが createSecureReplyEmailResolver 経由でルーティングされた場合、replyToEmail() メソッドは署名を強制します。
secret の値 |
挙動 |
|---|---|
"my-secret" |
ヘッダーに署名する(安全) |
undefined(省略) |
エラーを投げる — シークレットか明示的なオプトアウトが必要 |
null |
許可されるが非推奨 — 署名を明示的にオプトアウトする |
送信メールを送り、安全な返信を扱う完全な Email Service エージェントの例です。
import { Agent, callable, routeAgentEmail } from "agents";
import {
createAddressBasedEmailResolver,
createSecureReplyEmailResolver,
} from "agents/email";
import PostalMime from "postal-mime";
export class EmailAgent extends Agent {
@callable()
async sendWelcome(to) {
return this.sendEmail({
binding: this.env.EMAIL,
to,
from: "[email protected]",
subject: "Welcome!",
text: "Thanks for signing up.",
secret: this.env.EMAIL_SECRET,
});
}
async onEmail(email) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
console.log(`Email from ${email.from}: ${parsed.subject}`);
const emails = this.state.emails || [];
emails.push({
from: email.from,
subject: parsed.subject,
receivedAt: new Date().toISOString(),
});
this.setState({ ...this.state, emails });
await this.replyToEmail(email, {
fromName: "Support Bot",
body: `Thanks for your email! We received: "${parsed.subject}"`,
secret: this.env.EMAIL_SECRET,
});
}
}
export default {
async email(message, env) {
const secureReplyResolver = createSecureReplyEmailResolver(
env.EMAIL_SECRET,
{
maxAge: 7 * 24 * 60 * 60, // 7 days
onInvalidSignature: (email, reason) => {
console.warn(`Invalid signature from ${email.from}: ${reason}`);
},
},
);
const addressResolver = createAddressBasedEmailResolver("EmailAgent");
await routeAgentEmail(message, env, {
resolver: async (email, env) => {
const replyRouting = await secureReplyResolver(email, env);
if (replyRouting) return replyRouting;
return addressResolver(email, env);
},
onNoRoute: (email) => {
console.warn(`No route found for email from ${email.from}`);
email.setReject("Unknown recipient");
},
});
},
};import { Agent, callable, routeAgentEmail } from "agents";
import {
createAddressBasedEmailResolver,
createSecureReplyEmailResolver,
type AgentEmail,
} from "agents/email";
import PostalMime from "postal-mime";
interface Env {
EmailAgent: DurableObjectNamespace<EmailAgent>;
EMAIL: SendEmail;
EMAIL_SECRET: string;
}
export class EmailAgent extends Agent<Env> {
@callable()
async sendWelcome(to: string) {
return this.sendEmail({
binding: this.env.EMAIL,
to,
from: "[email protected]",
subject: "Welcome!",
text: "Thanks for signing up.",
secret: this.env.EMAIL_SECRET,
});
}
async onEmail(email: AgentEmail) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
console.log(`Email from ${email.from}: ${parsed.subject}`);
const emails = this.state.emails || [];
emails.push({
from: email.from,
subject: parsed.subject,
receivedAt: new Date().toISOString(),
});
this.setState({ ...this.state, emails });
await this.replyToEmail(email, {
fromName: "Support Bot",
body: `Thanks for your email! We received: "${parsed.subject}"`,
secret: this.env.EMAIL_SECRET,
});
}
}
export default {
async email(message, env: Env) {
const secureReplyResolver = createSecureReplyEmailResolver(
env.EMAIL_SECRET,
{
maxAge: 7 * 24 * 60 * 60, // 7 days
onInvalidSignature: (email, reason) => {
console.warn(`Invalid signature from ${email.from}: ${reason}`);
},
},
);
const addressResolver = createAddressBasedEmailResolver("EmailAgent");
await routeAgentEmail(message, env, {
resolver: async (email, env) => {
const replyRouting = await secureReplyResolver(email, env);
if (replyRouting) return replyRouting;
return addressResolver(email, env);
},
onNoRoute: (email) => {
console.warn(`No route found for email from ${email.from}`);
email.setReject("Unknown recipient");
},
});
},
} satisfies ExportedHandler<Env>;interface EmailAddress {
email: string;
name?: string;
}async sendEmail(options: {
binding: EmailSendBinding;
to: string | EmailAddress | (string | EmailAddress)[];
from: string | EmailAddress;
subject: string;
text?: string;
html?: string;
replyTo?: string | EmailAddress;
cc?: string | EmailAddress | (string | EmailAddress)[];
bcc?: string | EmailAddress | (string | EmailAddress)[];
inReplyTo?: string;
headers?: Record<string, string>;
secret?: string;
}): Promise<EmailSendResult>;Email Service バインディング経由で送信メールを送ります。X-Agent-Name と X-Agent-ID ヘッダーを自動で注入します。secret を渡すと、安全な返信ルーティングのためヘッダーを HMAC-SHA256 で署名します。
| オプション | 説明 |
|---|---|
binding |
send_email バインディング(例: this.env.EMAIL)。必須。 |
to |
宛先アドレス、アドレス配列、または EmailAddress オブジェクト |
from |
送信元アドレスまたは EmailAddress オブジェクト |
subject |
メールの件名 |
text |
プレーンテキスト本文(text / html のいずれかは必須) |
html |
HTML 本文(text / html のいずれかは必須) |
replyTo |
Reply-to アドレスまたは EmailAddress オブジェクト |
cc |
CC 宛先アドレス、アドレス配列、または EmailAddress オブジェクト |
bcc |
BCC 宛先アドレス、アドレス配列、または EmailAddress オブジェクト |
inReplyTo |
スレッド用の Message-ID(In-Reply-To ヘッダーを設定) |
headers |
追加のカスタムヘッダー(衝突時はエージェントヘッダーが優先) |
secret |
エージェントルーティングヘッダーの HMAC 署名用シークレット |
function routeAgentEmail<Env>(
email: ForwardableEmailMessage,
env: Env,
options: {
resolver: EmailResolver;
onNoRoute?: (email: ForwardableEmailMessage) => void | Promise<void>;
},
): Promise<void>;resolver の判定に基づき、受信メールを適切な Agent へルーティングします。
| オプション | 説明 |
|---|---|
resolver |
メールをどのエージェントへルーティングするかを決める関数 |
onNoRoute |
ルーティング情報が見つからないときに呼ばれる任意のコールバック。メールの拒否や独自処理に使います。未指定の場合は警告を記録し、メールを破棄します。 |
function createSecureReplyEmailResolver(
secret: string,
options?: {
maxAge?: number;
onInvalidSignature?: (
email: ForwardableEmailMessage,
reason: SignatureFailureReason,
) => void;
},
): EmailResolver;
type SignatureFailureReason =
| "missing_headers"
| "expired"
| "invalid"
| "malformed_timestamp";署名検証付きでメール返信をルーティングする resolver を作成します。
| オプション | 説明 |
|---|---|
secret |
HMAC 検証用のシークレットキー(署名に使ったキーと一致させる) |
maxAge |
署名の最大有効秒数(デフォルト: 30 日 / 2592000 秒) |
onInvalidSignature |
署名検証が失敗したときのログ用の任意コールバック |
function signAgentHeaders(
secret: string,
agentName: string,
agentId: string,
): Promise<Record<string, string>>;エージェントルーティングヘッダーを手動で署名します。X-Agent-Name、X-Agent-ID、X-Agent-Sig、X-Agent-Sig-Ts ヘッダーを含むオブジェクトを返します。
外部サービス経由でメールを送りつつ、安全な返信ルーティングを維持したいときに便利です。署名にはタイムスタンプが含まれ、デフォルトでは 30 日間有効です。