Documentation for agents: an index at /llms.txt, every docs page in one file at /llms-full.txt, and Markdown for any docs page by adding .md to its URL.

Effect KitEmails

Mailgun

Send email through Mailgun with the @effect-kit/emails/mailgun Layer.

The @effect-kit/emails/mailgun subpath sends messages through Mailgun and provides the Email service.

Installation

npm install @effect-kit/emails effect mailgun.js@14.1.0 form-data@4.0.5

Both packages are pinned peers and optional until you import this subpath.

Setup

Mailgun.layerConfig() reads these variables:

VariableRequiredValue
MAILGUN_API_KEYyesAPI key
MAILGUN_DOMAINyesSending domain, such as mg.example.com
MAILGUN_REGIONyesus or eu

Example (Sending a Message With Mailgun)

import { , ,  } from "effect";
import {  } from "@effect-kit/emails";
import * as  from "@effect-kit/emails/mailgun";

const  = .().(
  .(.(.())),
);

const  = .(function* () {
  const  = yield* ;
  return yield* .({
    : "hello@example.com",
    : "ada@example.com",
    : "Welcome",
    : "Your account is ready.",
  });
});

.(.(.()));

Use a from address your Mailgun account is allowed to send from.

Options

Mailgun.layer takes these options. layerConfig builds the same object from a Config.

Prop

Type

Capabilities

Mailgun reports this in email.capabilities:

TextHTMLBothSender nameCcBccReply-toHeadersAttachmentsInline imagesIdempotency
yesyesyesyesyesyesmultipleyesyesnono

These fail with UnsupportedFeatureError and acceptance: "notSubmitted" before any request:

  • An attachment with a contentId (feature inlineAttachments).
  • An idempotencyKey (feature idempotency).

Idempotency

Not supported. A key fails with UnsupportedFeatureError before sending.

Provider notes

  • region: "eu" uses https://api.eu.mailgun.net and region: "us" uses https://api.mailgun.net. endpoint replaces both.
  • Mailgun has no inline image support here. Every attachment goes out as a regular attachment, so a contentId fails before sending with feature inlineAttachments.
  • Install form-data next to mailgun.js. Both are pinned peers.

Using your own client

layerClient takes any object with messages.create(domain, message), which a mailgun.js client has. Unlike most providers its options are required, because they carry the domain.

Example (Borrowing a Mailgun Client)

import FormData from "form-data";
import Mailgun from "mailgun.js";
import * as MailgunEmail from "@effect-kit/emails/mailgun";

const client = new Mailgun(FormData).client({ username: "api", key: "fake-key" });

const EmailLive = MailgunEmail.layerClient(client, { domain: "mg.example.com" });

You own a borrowed client. The Layer never closes it, and the client keeps its own retry, timeout and logging policy. The client is created with its own region and URL, so layerClient does not take a region.

Errors

ProviderConfigurationError carries provider: "mailgun" and a field naming the bad option. Layer construction fails with it, and layerClient fails with it for the client options.

fieldRaised when
timeoutThe timeout is not finite and positive.
tokenThe API key is not printable ASCII without spaces.
regionThe region is neither us nor eu.
endpointThe URL is not an http or https origin, or has credentials, a path, a query or a fragment.
domainThe domain is not a valid hostname.

Send failures use the HTTP status with the shared mapping: 401 is AuthenticationError, 403 PermissionDeniedError, 429 RateLimitedError, 400 and 422 InvalidMessageError, 5xx ProviderUnavailableError, and anything else ProviderError. acceptance is rejected for a 4xx status other than 409 and unknown otherwise. A network failure with no status is TransportError, and ETIMEDOUT or ECONNABORTED is TimeoutError, both with acceptance: "unknown". See Errors.