Messages and attachments
Define addresses, message bodies, attachments and schedules with validated TypeScript inputs.
A message requires from, to, subject and body. The body must contain nonempty text, html, or both. Optional fields include cc, bcc, replyTo, headers, tags, metadata, attachments and sendAt.
The common Schema validates message structure. The selected provider must also support every supplied field; see providers and validation.
Build a message
parseEmailMessage validates common input and creates an owned message: subsequent changes to caller-supplied objects do not change the decoded message. It does not acquire a sender or run provider-specific checks.
import { Effect } from "effect";
import { emailAttachmentFromBytes, parseEmailMessage } from "@corven/email-sdk";
export const prepareNotification = Effect.gen(function* prepareNotification() {
const attachment = yield* emailAttachmentFromBytes(
new TextEncoder().encode("Fictional receipt"),
{ contentType: "text/plain", filename: "receipt.txt" },
);
return yield* parseEmailMessage({
attachments: [attachment],
body: { html: "<p>Your receipt is attached.</p>", text: "Your receipt is attached." },
from: { address: "sender@example.test", name: "Example Shop" },
subject: "Receipt",
to: "Example Customer <recipient@example.test>",
});
});
Pass the result through email.validate or a provider preflight function to check provider support before submission.
Specify addresses
Mailbox inputs accept:
- A conservative ASCII address, such as
recipient@example.test. - A structured record with
addressand optionalname. - A single named mailbox string, such as
Example Customer <recipient@example.test>.
Recipient fields accept one mailbox or a statically nonempty array. Unicode display names are supported, but internationalized mailbox local parts and unrestricted RFC address-list syntax are not.
Add attachments
Use emailAttachmentFromBytes, emailAttachmentFromArrayBuffer or emailAttachmentFromBlob to construct a bounded attachment from bytes your application has already acquired. These helpers do not read file paths, fetch URLs or infer MIME types from filenames or Blob.type.
Choose an explicit MIME type where the provider supports it. Select ProviderSelected only when you intend to delegate MIME selection to a compatible provider. Inline/CID placement also requires provider support.
Your application owns file or URL acquisition, content inspection, consent and retention. Attachment byte limits do not guarantee acceptance under the provider’s encoded message-size limit. Interrupting a Blob read stops awaiting its result; it cannot guarantee physical cancellation of the native read.
Schedule submission
sendAt accepts a native Date, an Effect UTC instant, or a strict timestamp containing seconds and a known UTC offset. Decoding preserves one UTC instant and rejects malformed dates, ambiguous offsets or unsupported precision.
A parsed schedule is not a provider reservation. Providers may restrict the allowed time window or require whole-second precision. Dispatch can recheck time-sensitive constraints after preparation or queueing, so allow enough lead time and handle expired schedules explicitly.
Separate provider metadata from host context
Use message.metadata for provider-facing data when the provider supports it.
Use options.executionMetadata for bounded private application context needed during preparation, approval or complete operation observation. The SDK does not automatically forward this context to providers, models or minimal observations. It is not a template-variable source, credential store or replay token.