Capabilities
Inspect what a provider supports and understand the checks that run before any request is sent.
Each provider layer declares what it can send. email.send compares your message against those capabilities before it calls the provider, so an unsupported field fails with no network request.
Example (Failing Before the Request)
import { } from "effect";
import { } from "@effect-kit/emails";
import * as from "@effect-kit/emails/mailersend";
const = .(function* () {
const = yield* ;
return yield* .({
: "billing@example.com",
: "ada@example.com",
: "Invoice",
: "Your invoice is attached.",
: [
{
: "invoice.pdf",
: "application/pdf",
: new ([37, 80, 68, 70]),
},
],
});
}).(
.("EmailError", "UnsupportedFeatureError", () =>
.(`unsupported: ${.}`),
),
.(.()),
);
// Output: "unsupported: attachments"The failure is an EmailError with acceptance: "notSubmitted" and an UnsupportedFeatureError reason. Its feature names the first check that failed.
Reading capabilities
The service exposes email.provider and email.capabilities. Read them to adapt a message before sending.
Example (Passing a Key Only When Supported)
import { } from "effect";
import { } from "@effect-kit/emails";
const = .(function* () {
const = yield* ;
const { } = .;
return yield* .(
{
: "hello@example.com",
: "ada@example.com",
: "Welcome",
: "Your account is ready.",
},
. === "unsupported" ? {} : { : "welcome/account-123" },
);
});EmailCapabilities has eleven fields.
| Field | Type | Meaning |
|---|---|---|
text | boolean | Accepts a text body. |
html | boolean | Accepts an html body. |
htmlAndText | boolean | Accepts both bodies in one message. |
senderName | boolean | Accepts a display name on from. |
cc | boolean | Accepts cc. |
bcc | boolean | Accepts bcc. |
replyTo | "none" | "single" | "multiple" | How many reply-to addresses are allowed. |
headers | boolean | Accepts custom headers. |
attachments | boolean | Accepts attachments. |
inlineAttachments | boolean | Accepts attachments with a contentId. |
idempotency | { mode } | "unsupported", "replay" or "reject", with an optional retention. |
The per-provider values are in the capability matrix and in Capabilities reference.
Check order
The core runs the checks in this order and reports the first failure. Only one feature is named per attempt, so fixing one field can surface the next.
| Order | feature | Fails when |
|---|---|---|
| 1 | text | text is set and the provider has no text bodies. |
| 2 | html | html is set and the provider has no HTML bodies. |
| 3 | htmlAndText | Both are set and the provider cannot take both. |
| 4 | senderName | from has a name. |
| 5 | cc | cc is set. |
| 6 | bcc | bcc is set. |
| 7 | replyTo | replyTo is set and replyTo is "none". |
| 8 | multipleReplyTo | More than one reply-to address and replyTo is "single". |
| 9 | headers | Any custom header is set. |
| 10 | attachments | Any attachment is set. |
| 11 | inlineAttachments | An attachment has a contentId. |
| 12 | idempotency | An idempotencyKey is passed and the mode is "unsupported". |
Provider checks come after
Some providers also reject a field for reasons that are not capability flags, such as a malformed
idempotency key or an inline contentId that Lettermint needs to contain @. Those fail with
InvalidMessageError, still with acceptance: "notSubmitted".