Reference: API
The package root exports the supported entrypoints:
Top-level exports
createFormMailercreateHttpTransportcreateSmtpTransportloadConfigFromEnvcreateFormMailerErrorisFormMailerError
It also exports the public TypeScript types used by those entrypoints.
createFormMailer(config)
Creates a mailer instance.
The returned mailer exposes:
validate(submission)send(submission)
validate(submission) returns a structured validation result.
send(submission) validates first, then returns a promise for a typed delivery outcome.
For the reasoning behind the validation pipeline, see Explanation: Validation.
createHttpTransport(config)
Creates a transport that delivers outgoing mail over an HTTP API.
Use it when you want to:
- provide a built-in REST transport explicitly to
createFormMailer() - post the assembled
OutgoingMailJSON shape to a provider endpoint - reuse the package's built-in bearer-token HTTP behavior instead of writing a custom adapter
The config object uses HttpTransportConfig, which supports:
urltokenheadersmapRequestparseResponse
mapRequest returns an HttpTransportRequest, which supports:
urlmethodheadersbody
Important behavior:
urlmust be a valid absolute URL- requests use
POSTby default - when
mapRequestis omitted, the request body is JSON serialized fromOutgoingMail - the built-in defaults include
content-type: application/json authorization: Bearer <token>is added whentokenis presentmapRequest(message)can override the request URL, method, headers, and body- mapped headers override the built-in defaults when they use the same header names
parseResponse(response)can return a provider-specificTransportSendResult- when
parseResponseis omitted, the transport looks for a stringmessageIdin a JSON response body
For transport-level expectations, see Reference: Adapters.
createSmtpTransport(config)
Creates a transport that delivers outgoing mail over SMTP.
Use it when you want to:
- provide a transport explicitly to
createFormMailer() - reuse the built-in SMTP behavior behind the shared transport interface
- swap between SMTP and custom adapters without changing the mailer contract
The config object uses SmtpConnectionConfig, which supports:
hostportsecurestarttlsusernamepasswordtokentls
Important behavior:
hostis required for a real SMTP connectionportdefaults to465whensecureis trueportdefaults to587otherwisestarttlsupgrades a non-implicit TLS connection afterEHLO- authentication runs whenever
passwordortokenis present - when both are provided,
tokenis used as the SMTP secret value - when
usernameis omitted but an SMTP secret is present, SMTP auth sends an empty username value
For transport-level expectations, see Reference: Adapters.
loadConfigFromEnv()
Loads a FormMailerConfig from environment variables.
The loader:
- reads
process.envby default - optionally loads a dotenv-style file from
FORM_MAILER_ENV_PATH - lets live environment variables override values from that file
It returns a promise because reading the optional env file is asynchronous.
For the supported environment variables and practical setup guidance, use How-To: Configuration.
createFormMailerError(code, message, details?)
Creates a typed FormMailerError.
Use it when you want your own code to return or throw errors that match the package error shape.
The resulting error includes:
messagecode- optional
details
isFormMailerError(value)
Checks whether an unknown value matches the package error shape.
Use it when you need to narrow an unknown error before reading:
error.codeerror.details
Submission shape
FormMailSubmission supports:
nameemailsubjectmessagerecipientKeyoriginhoneypotfields
Details worth calling out:
- there is no permanently reserved top-level honeypot property in the validation flow
- the honeypot is resolved by the configured
honeypotFieldName - that field name can point at a top-level submission property or a value inside
submission.fields - a property literally named
honeypotis only meaningful if you configurehoneypotFieldName: 'honeypot' submission.fieldscan contain nested JSON-like objects and arrays in addition to scalar values
Configuration shape
FormMailerConfig supports:
fromtorecipientMapsubjectreplyTooriginAllowlisthoneypotFieldNamerequiredFieldsmaxPayloadBytestransporthttpsmtp
Details worth calling out:
fromcan be a plain email string or a{ name, email }address objectsubjectcan be a string or a function that receives the submissionreplyTocan be a string or a function that receives the submissiontransportis optional whenhttporsmtpis providedhttpis optional whentransportis providedhttp.mapRequestandhttp.parseResponseare code-only hooks and are not loaded from environment variables- when no explicit
transportis provided,httpandsmtpare mutually exclusive honeypotFieldNamedefaults towebsitewhen omittedrequiredFieldsdefaults to an empty listmaxPayloadBytesdefaults to64 * 1024
Validation behavior
validate(submission) checks the submission in this order:
- submitter email
- configured required fields
- honeypot field
- origin allowlist
- payload size
The returned ValidationResult always includes:
okissues
Each ValidationIssue includes:
fieldcodemessage
Validation accumulates issues. It does not stop after the first failure.
Current issue codes are:
invalid_emailrequired_field_missinghoneypot_triggeredorigin_missingorigin_invalidorigin_not_allowedpayload_too_large
Notes worth calling out:
emailis checked with a lightweight address-shape regex after trimmingrequiredFieldschecks top-level submission properties first andsubmission.fieldssecond- only
undefined,null, and''count as empty forrequiredFieldsand honeypot checks honeypotFieldNamedefaults towebsite- the honeypot check only asks whether the field was populated at all
originAllowlistexpects full origins such ashttps://example.com- origin comparison is done against the normalized
new URL(submission.origin).originvalue maxPayloadBytesis measured against the JSON-serialized submission body- payload serialization is cycle-safe, so circular field objects are rendered with
"[Circular]"instead of throwing
Environment loading
loadConfigFromEnv() reads the active process environment directly.
If FORM_MAILER_ENV_PATH is set, it loads a dotenv-style file first and then lets process env values override the file defaults.
If the file contains FORM_MAILER_SMTP_PASSWORD, FORM_MAILER_SMTP_TOKEN, or FORM_MAILER_HTTP_TOKEN, the loader logs a warning because runtime environment variables are the preferred place for secrets.
The env loader reads these SMTP values:
FORM_MAILER_FROMis the primary sender valueFORM_MAILER_SENDER_EMAILcan supply the sender email whenFORM_MAILER_FROMis absentFORM_MAILER_SENDER_NAMEcan supply the sender display nameFORM_MAILER_SMTP_USERNAMEsupplies the SMTP usernameFORM_MAILER_SMTP_PASSWORDsupplies the SMTP passwordFORM_MAILER_SMTP_TOKENsupplies the SMTP token
The env loader reads these HTTP values:
FORM_MAILER_HTTP_URLselects the built-in HTTP transport endpointFORM_MAILER_HTTP_TOKENsupplies the bearer token for the built-in HTTP transportFORM_MAILER_HTTP_HEADERSsupplies optional JSON headers with string values
If both FORM_MAILER_HTTP_URL and FORM_MAILER_SMTP_HOST are set, loadConfigFromEnv() rejects with config_error instead of guessing which built-in transport to use.
If neither FORM_MAILER_HTTP_URL nor FORM_MAILER_SMTP_HOST is set, loadConfigFromEnv() also rejects with config_error.
Recipient mapping
recipientMap is an optional routing table keyed by FormMailSubmission.recipientKey.
- if the submission sets
recipientKeyand the key exists inrecipientMap, the mapped recipients are used - if the submission sets
recipientKeyand the key does not exist inrecipientMap, the send fails withconfig_error - otherwise the package falls back to
to tois still the default recipient list for submissions that do not set a route key
Transport adapter details live in Reference: Adapters.
Result shape
send() returns:
ok: truewith an optionalmessageIdand delivery envelopeok: falsewith a typed error object
The success envelope includes the resolved from address and final recipient list.
Error codes
config_errorvalidation_errortransport_errorsmtp_error