@flowmails / flowmails-sdk
的官方JavaScript / TypeScript SDK Flowmails SDK后端 (https://sdk.flowmails.net)。
状态:公开预览版(v0.2.x) -发表于Flowmails monorepo。线材格式和表面在v0.x中仍然可以改变;固定次要版本(
~0.2)正在生产中。
安装
pnpm add @flowmails/flowmails-sdk
快速启动
import { Flowmails } from "@flowmails/flowmails-sdk";
const fm = new Flowmails({
apiKey: process.env.FLOWMAILS_API_KEY!,
// baseURL: "https://sdk.flowmails.net", // default
});
const result = await fm.send({
from: "[email protected]",
to: "[email protected]",
subject: "Order #1234 confirmed",
text: "Thanks for your order — we'll ship it tomorrow.",
// html: "<p>Thanks for your order…</p>",
});
console.log(result); // { id: "msg_8421", status: "queued" }
A发的接力被拒绝了
send() 答案 201 为了传递消息 拒绝 ——行
存在,邮件不存在。结果带着理由,所以失败
从不沉默;
const result = await fm.send({ … });
if (result.status === "failed") {
console.error(result.error); // "send_failed" — always present
console.error(result.sendFeedback); // the relay's own reason, when it gave one
}
result.error 从来都不是 null 发送失败时: “没有成功”
理由” 电线不可能处于这种状态。
我的域名可以发送吗?
const me = await fm.me();
if (me.sending && !me.sending.canSend) {
console.warn(me.sending.state); // "not_onboarded" | "unconfigured" | "unknown" | …
}
me.sending 这个平台自己的裁决,是从同一份文件中读出的吗
仪表板上写着: null 当它不能被知道时(无定义域),
或者无法读取平台)。一起读 state:
canSend: false 与 state: "unknown" 意味着 “我们还没有看过。”,
不 “此域无法发送”.
关于Flowmails
Flowmails 是一个cloudflare原生的电子邮件平台:自定义域的邮件(收件箱、发件箱和路由规则)存在于你自己的D1中,上面有REST API和webhook。仪表板、文档和博客都在 https://flowmails.net -在使用此SDK之前,在那里注册一个API密钥并绑定一个域。
API密钥
API密钥是在flowmail仪表板中生成的 Settings → API Keys. 每个密钥都固定到您的一个注册域名- SDK后端拒绝 send(...) 调用,其中 from 地址不会落在键绑定域上。
错误
SDK抛出类型化错误:
| 类 | HTTP状态 | 的事情 |
|---|---|---|
AuthenticationError | 401 | 否 |
ValidationError | 400 / 404 / 409 / 422 | 否 |
RateLimitError | 429 | 是(客户退出) |
UpstreamError | 5xx / 408 | 是 |
FlowmailsError | 其他 | 否 |
使用 instanceof 区别:
import { RateLimitError, FlowmailsError } from "@flowmails/flowmails-sdk";
try {
await fm.send({ ... });
} catch (err) {
if (err instanceof RateLimitError) {
// backoff and retry
} else if (err instanceof FlowmailsError) {
// surface `err.code` to your logs
} else {
throw err;
}
}
幂等发送
发送邮件 idempotencyKey 然后重试变成免费的:相同的键
在相同的帐户和相同的请求下返回 原始
结果,不发送第二个消息。
await fm.send({ from, to, subject, text, idempotencyKey: "order-42" });
该键的作用域是帐户,而不是全局。重用一个键
不同的 有效负载是一个客户端错误,被拒绝使用
idempotency_key_conflict (一个 ValidationError)而不是回答
第一个结果。
发配额
发送上限为每个帐户和每个域、每小时和每天。一个
拒绝是一种 RateLimitError 带着你需要回避的事实:
catch (err) {
if (err instanceof RateLimitError && err.code === "rate_limit_exceeded") {
console.log(err.remaining, err.limit, err.resetAt); // ISO-8601
}
}
同样的数字在 X-Flowmails-RateLimit-Remaining 和
X-Flowmails-RateLimit-Reset 响应标头。SDK可以 不
为您重试配额拒绝-窗口是一个小时,所以重试
在内部,它不可能成功。
读的那一半
发送是SDK的一半;另一个是 阅读 ——还有它
与不同的源(平台工作器,而不是SDK)进行通信
后端),所以一个自托管安装集 platformURL 了。
// Check the credential without sending anything.
const me = await fm.me(); // { userId, domain, scopes[], sending }
// Tail the inbound log with a cursor (an event id, never a timestamp).
const page = await fm.events.list({ after: lastEventId });
// Search the mailbox history — addressing, subject and a preview.
const hits = await fm.messages.search(domainId, { q: "invoice" });
// Read the conversation a message belongs to.
const thread = await fm.threads.get(threadId);
// Wait for a message without holding a request open.
const wait = await fm.waits.create({ address: "[email protected]" });
const status = await fm.waits.get(wait.waitId); // pending → resolved | expired
// Create a mailbox — one local part, idempotent.
await fm.addresses.create(domainId, "billing");
身体是 获取,永远不会推送:事件携带路由
事实和主题行,以及 fm.messages.get(messageId) 是
问。SDK永远不会给你一个你没有要求的消息。
限制(v0.2)
- 自定义域(HTTP/HTTPS)附件托管不能由SDK调用者配置;SDK后端总是通过R2发送。
- webhook交付状态事件(
message.delivered/message.bounced)还不存在——反弹检测也不存在。接收工作者没有DSN摄取,因此发送的状态为sent(接力棒接受了)或者failed(继电器拒绝了)仅此而已。词汇表保持不变delivered/bounced/complained/delayed,但什么也写不出来。
许可证
MIT©2026 flowmail。看到 LICENSE.