Email Config
Email Config protocol schemas
Email Service Configuration Protocol
Operator-facing configuration that selects the outbound email
transport for the EmailServicePlugin. Provider is a provider tag
- provider-specific settings; concrete
IEmailTransportimplementations live in@objectstack/plugin-email/transports/*.
Resolution order in serve.ts:
config.email.*from objectstack.config.tsOS_EMAIL_*environment variables (override per setting)- Default → provider='log' (LogTransport, no real send)
appName is the one key whose env layer is not OS_EMAIL_* — it is
OS_APP_NAME, because the same product name names the whole deployment,
not just its mail.
Every key here is one resolveEmailCapabilityArg reads (the single reader
of config.email); the schema is the operator-facing contract for that
function, so a key the runtime honours and this object omits is a type
error on a config that boots fine — the declared ≠ implemented gap of
#5104 (provider='smtp') and #5307 (queueDelivery / appName /
defaultTemplateContext), both times with the spec on the lagging side.
SMTP delivery is built in (ADR-0012): select it with provider='smtp'
and supply the connection through options (host / port / secure /
user / password) or the matching OS_EMAIL_SMTP_HOST / _PORT /
_SECURE / _USER / _PASSWORD environment variables.
Source: packages/spec/src/system/email-config.zod.ts
TypeScript Usage
import { EmailAddressConfigSchema, EmailProviderSchema, EmailServiceConfigSchema } from '@objectstack/spec/system';
import type { EmailAddressConfig, EmailProvider, EmailServiceConfig } from '@objectstack/spec/system';
// Validate data
const result = EmailAddressConfigSchema.parse(data);EmailAddressConfig
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | optional | Display name (e.g. "Acme CRM") |
| address | string | ✅ | RFC-5322 address |
EmailProvider
Allowed Values
logresendpostmarksmtp
EmailServiceConfig
Properties
| Property | Type | Required | Description |
|---|---|---|---|
| provider | Enum<'log' | 'resend' | 'postmark' | 'smtp'> | ✅ | Transport to deliver through (OS_EMAIL_PROVIDER env). Default log — boots, sends nothing |
| apiKey | string | optional | Provider API key (or OS_EMAIL_API_KEY env) |
| defaultFrom | { name?: string; address: string } | optional | |
| retries | integer | optional | Retry attempts on transport throw |
| persist | boolean | optional | Persist to sys_email (default true) |
| queueDelivery | boolean | optional | Deliver through the durable sys_job_queue instead of inline (or OS_EMAIL_QUEUE_ENABLED env). Default false. Reuses retries as the queue attempt budget; requires a queue service and sys_email persistence, else the boot fails |
| options | Record<string, any> | optional | Provider-specific extras. smtp: host (required) / port / secure / user / password, mirroring OS_EMAIL_SMTP_HOST / _PORT / _SECURE / _USER / _PASSWORD. postmark: messageStream |
| appName | string | optional | Product name templates interpolate as the appName variable — OS_APP_NAME env wins, then this, then defaultTemplateContext.appName, then the top-level config appName, then "ObjectStack". Also seeds the placeholder no-reply sender when no defaultFrom is configured |
| defaultTemplateContext | Record<string, any> | optional | Free-form render context merged into every sendTemplate() call, under the per-call data. Passed through unchanged except appName, which is resolved by its own chain — OS_APP_NAME and the appName key both override the value written here |