Runtime configuration schema for Accrue, backed by NimbleOptions.
This module is the single source of truth for supported :accrue
application keys. Host code reads validated values via get!/1 or
Application.get_env/3; extend behaviour through adapters, not by editing
this schema from application code.
Compile-time vs runtime
Adapter atoms (:processor, :mailer, :mailer_adapter, :pdf_adapter,
:auth_adapter) are stable per-deploy and fine at compile time via
Application.compile_env!/2.
Secrets (:stripe_secret_key) and host-owned fields (:default_currency,
:from_email, brand colors) MUST be read at runtime. See CLAUDE.md
§Config Boundaries.
Options
:repo(atom/0) - Required. HostEcto.Repomodule that Accrue writes to (event ledger, webhook events, billing tables).:processor(atom/0) - Processor adapter implementingAccrue.Processorbehaviour. The default value isAccrue.Processor.Fake.:mailer(atom/0) - Mailer pipeline module implementingAccrue.Mailerbehaviour. The default value isAccrue.Mailer.Default.:mailer_adapter(atom/0) - Swoosh-backed mailer delivery module. The default value isAccrue.Mailer.Swoosh.:pdf_adapter(atom/0) - PDF adapter implementingAccrue.PDFbehaviour. The default value isAccrue.PDF.ChromicPDF.:auth_adapter(atom/0) - Auth adapter implementingAccrue.Authbehaviour. The default value isAccrue.Auth.Default.:storage_adapter(atom/0) - Storage adapter implementingAccrue.Storagebehaviour. v1.0 shipsAccrue.Storage.Nullonly; hosts supply a custom adapter (e.g., S3) to enable persisted asset storage.Accrue.Storage.Filesystemships in v1.1. The default value isAccrue.Storage.Null.:stripe_secret_key(String.t/0) - Runtime Stripe secret key. MUST be read at runtime only; never viaApplication.compile_env!/2. Validated at boot whenprocessor == Accrue.Processor.Stripe.:stripe_api_version(String.t/0) - Stripe API version pinned by the:lattice_stripewrapper. The default value is"2026-03-25.dahlia".:emails(keyword/0) - Per-email-type switches. Keys are email type atoms; values arebooleanor{Mod, :fun, args}MFA callbacks. The default value is[].:email_overrides(keyword/0) - Per-email-type template module overrides (third rung of the override ladder; seeguides/email.md). Keys are email type atoms; values are module names. The default value is[].:attach_invoice_pdf(boolean/0) - Auto-attach invoice PDF to the receipt email. The default value istrue.:enforce_immutability(boolean/0) - When true,Accrue.Applicationboot raises if the current PG role has UPDATE/DELETE onaccrue_events. The default value isfalse.:business_name(String.t/0) - Business name shown in email headers, PDFs, and admin UI. The default value is"Accrue".:business_address(String.t/0) - Business postal address shown in invoice footers. The default value is"".:logo_url(String.t/0) - Absolute URL to the brand logo used in email + PDF headers. The default value is"".:support_email(String.t/0) - Reply-to support email address for transactional mail. The default value is"support@example.com".:from_email(String.t/0) - Default From: address for transactional mail. The default value is"noreply@example.com".:from_name(String.t/0) - Default From: name for transactional mail. The default value is"Accrue".:default_currency(atom/0) - Default currency when one is not explicitly supplied. The default value is:usd.:webhook_signing_secrets(term/0) - Map of processor atom to signing secret(s). Each value is a string or list of strings for rotation. Example:%{stripe: ["whsec_old", "whsec_new"]}. The default value is%{}.:succeeded_retention_days- Number of days to retain:succeededwebhook events before the Pruner deletes them. Set to:infinityto disable pruning. Default: 14. The default value is14.:dead_retention_days- Number of days to retain:deadwebhook events before the Pruner deletes them. Set to:infinityto disable pruning. Default: 90. The default value is90.:webhook_handlers(list ofatom/0) - List of modules implementingAccrue.Webhook.Handlerbehaviour. Called sequentially after the default handler on each webhook event. Example:[MyApp.BillingHandler, MyApp.AnalyticsHandler]. The default value is[].:expiring_card_thresholds- Strictly-descending list of day thresholds at which the expiring-card reminder email fires ahead of a stored card's expiration. Default:[30, 7, 1]— 30, 7, and 1 days out. The default value is[30, 7, 1].:idempotency_mode- HowAccrue.Actor.current_operation_id!/0behaves when the process dict has no operation_id.:strictraisesAccrue.ConfigError;:warn(the default) generates a random UUID and logs a warning. Set to:strictin production to ensure every outbound processor call carries a deterministic idempotency key. The default value is:warn.:succeeded_refund_retention_days(pos_integer/0) - Number of days to retain:succeededrefund records before pruning Default: 90. The default value is90.:dunning(keyword/0) - Dunning grace-period overlay config.:modeis:stripe_smart_retriesor:disabled;:terminal_actionis:unpaidor:canceled;:grace_daysadds N days past Stripe's last retry before Accrue asks the processor facade to move the subscription to the terminal action. The default value is[mode: :stripe_smart_retries, grace_days: 14, terminal_action: :unpaid, telemetry_prefix: [:accrue, :ops]].:webhook_endpoints(keyword/0) - Map of endpoint name to[secret:, mode:]for multi-endpoint webhooks. Example:[primary: [secret: "whsec_..."], connect: [secret: "whsec_...", mode: :connect]]. The default value is[].:dlq_replay_batch_size(pos_integer/0) - Number of rows per chunk inAccrue.Webhooks.DLQ.requeue_where/2bulk replay. The default value is100.:dlq_replay_stagger_ms(non_neg_integer/0) - Milliseconds to sleep between chunks during DLQ bulk replay (protects downstream). Default: 1_000. The default value is1000.:dlq_replay_max_rows(pos_integer/0) - Hard cap on bulk replay. Returns{:error, :replay_too_large}unlessforce: trueis passed. Default: 10_000. The default value is10000.:branding(keyword/0) - Branding config. Single source of truth for email + PDF brand.:from_emailand:support_emailare required for any real deploy. See guides/branding.md. The default value is[].:business_name(String.t/0) - The default value is"Accrue".:from_name(String.t/0) - The default value is"Accrue".:from_email(String.t/0) - Required.:support_email(String.t/0) - Required.:reply_to_email- The default value isnil.:logo_url- The default value isnil.:logo_dark_url- The default value isnil.:accent_color- The default value is"#1F6FEB".:secondary_color- The default value is"#6B7280".:font_stack(String.t/0) - The default value is"-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif".:company_address- The default value isnil.:support_url- The default value isnil.:social_links(keyword/0) - The default value is[].:list_unsubscribe_url- The default value isnil.
:default_locale(String.t/0) - Application-wide default locale for email + PDF rendering. Third rung of the locale precedence ladder (after assigns[:locale] and customer.preferred_locale). Bad locales fall back to "en". The default value is"en".:default_timezone(String.t/0) - Application-wide default IANA timezone for datetime rendering. Third rung of the timezone precedence ladder (after assigns[:timezone] and customer.preferred_timezone). Bad zones fall back to "Etc/UTC". The default value is"Etc/UTC".:cldr_backend(atom/0) - Cldr backend module used byAccrue.Workers.Mailer.enrich/2to validate locale strings. Defaults toAccrue.Cldr. The default value isAccrue.Cldr.:connect(keyword/0) - Stripe Connect configuration.:default_stripe_accountis the fallback connected account id used when no per-call override or pdict scope is active (three-level precedence chain).:platform_feeconfigures the default flat-rate fee consumed byAccrue.Connect.platform_fee/2::percentis aDecimalpercentage (e.g.Decimal.new("2.9")for 2.9%),:fixedis anAccrue.Moneyfee in minor units added after the percentage, and:min/:maxoptionally clamp the result. The default value is[default_stripe_account: nil, platform_fee: [percent: Decimal.new("2.9"), fixed: nil, min: nil, max: nil]].
Summary
Functions
Returns the branding config keyword list.
Returns a single branding key. Raises if the key is unknown.
Returns the configured Cldr backend module used by
Accrue.Workers.Mailer.enrich/2 to validate locale strings.
Returns the Connect config keyword list.
Returns the number of days to retain :dead webhook events.
Returns the application default locale string.
Returns the application default IANA timezone string.
Returns the list of deprecated flat branding keys.
Consumed by Accrue.Application.warn_deprecated_branding/0 and by the
internal flat-key shim in branding/0.
Returns the DLQ bulk-replay chunk size.
Returns the hard cap on DLQ bulk-replay rows.
Returns the DLQ bulk-replay inter-chunk stagger in milliseconds.
Returns the dunning grace-period overlay config.
Reads a config key from Application.get_env/3, falling back to the
schema default. Raises Accrue.ConfigError if the key is not in the
schema at all (prevents silent typos in downstream code).
Returns the NimbleOptions schema keyword list. Used by boot-time validation to iterate keys.
Returns the configured Stripe API version string.
Returns the number of days to retain :succeeded webhook events.
Validates a keyword list against the Accrue config schema and returns the
normalized form. Raises NimbleOptions.ValidationError on failure.
Reads the current :accrue application env at boot time, filters it to
the schema-known keys, and validates via NimbleOptions.validate!/2.
NimbleOptions :custom validator for :expiring_card_thresholds.
NimbleOptions :custom validator for :branding.accent_color /
:branding.secondary_color. Accepts #rgb, #rrggbb, and
#rrggbbaa hex color strings; rejects anything else.
Returns the multi-endpoint webhook config.
Returns the list of user-registered webhook handler modules.
Returns the signing secret(s) for the given processor.
Functions
@spec branding() :: keyword()
Returns the branding config keyword list.
Falls back to building a keyword list from deprecated top-level flat
branding keys (:business_name, :logo_url, :from_email,
:from_name, :support_email, :business_address) when the nested
:branding key is unset or empty. Nested :branding always takes
precedence. See Accrue.Application.warn_deprecated_branding/0 for the
boot-time Logger.warning when flat keys are still in use.
Returns a single branding key. Raises if the key is unknown.
@spec cldr_backend() :: module()
Returns the configured Cldr backend module used by
Accrue.Workers.Mailer.enrich/2 to validate locale strings.
@spec connect() :: keyword()
Returns the Connect config keyword list.
Shape: `[default_stripe_account: String.t() | nil,
platform_fee: [percent: Decimal.t(), fixed: Accrue.Money.t() | nil,
min: Accrue.Money.t() | nil, max: Accrue.Money.t() | nil]]`.
@spec dead_retention_days() :: pos_integer() | :infinity
Returns the number of days to retain :dead webhook events.
@spec default_locale() :: String.t()
Returns the application default locale string.
@spec default_timezone() :: String.t()
Returns the application default IANA timezone string.
@spec deprecated_flat_branding_keys() :: [atom()]
Returns the list of deprecated flat branding keys.
Consumed by Accrue.Application.warn_deprecated_branding/0 and by the
internal flat-key shim in branding/0.
@spec dlq_replay_batch_size() :: pos_integer()
Returns the DLQ bulk-replay chunk size.
@spec dlq_replay_max_rows() :: pos_integer()
Returns the hard cap on DLQ bulk-replay rows.
@spec dlq_replay_stagger_ms() :: non_neg_integer()
Returns the DLQ bulk-replay inter-chunk stagger in milliseconds.
@spec dunning() :: keyword()
Returns the dunning grace-period overlay config.
Reads a config key from Application.get_env/3, falling back to the
schema default. Raises Accrue.ConfigError if the key is not in the
schema at all (prevents silent typos in downstream code).
@spec schema() :: keyword()
Returns the NimbleOptions schema keyword list. Used by boot-time validation to iterate keys.
@spec stripe_api_version() :: String.t()
Returns the configured Stripe API version string.
@spec succeeded_retention_days() :: pos_integer() | :infinity
Returns the number of days to retain :succeeded webhook events.
Validates a keyword list against the Accrue config schema and returns the
normalized form. Raises NimbleOptions.ValidationError on failure.
@spec validate_at_boot!() :: :ok
Reads the current :accrue application env at boot time, filters it to
the schema-known keys, and validates via NimbleOptions.validate!/2.
Called by Accrue.Application.start/2 before the supervision tree
starts. Raises NimbleOptions.ValidationError on misconfig — fail loud
rather than limp into production with silently-broken config.
Only schema-known keys are validated. Extra keys in the :accrue env
(e.g., per-module adapter configs like Accrue.Mailer.Swoosh) are
ignored here — they belong to their own libraries and would otherwise
produce spurious unknown option errors.
@spec validate_descending(term()) :: {:ok, [pos_integer()]} | {:error, String.t()}
NimbleOptions :custom validator for :expiring_card_thresholds.
Accepts a non-empty list of positive integers that is strictly
descending (each element strictly less than the previous). Returns
{:ok, list} on success, {:error, message} on failure.
NimbleOptions :custom validator for :branding.accent_color /
:branding.secondary_color. Accepts #rgb, #rrggbb, and
#rrggbbaa hex color strings; rejects anything else.
@spec webhook_endpoints() :: keyword()
Returns the multi-endpoint webhook config.
@spec webhook_handlers() :: [module()]
Returns the list of user-registered webhook handler modules.
Returns the signing secret(s) for the given processor.
Looks up webhook_signing_secrets in the :accrue application env
and extracts the value for the given processor atom. Returns a list
of strings (for multi-secret rotation support). Raises
Accrue.ConfigError if no secrets are configured for the processor.