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,
:invoice_pdf_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).:billing_schema- PostgreSQL schema where Accrue-owned billing tables are stored. Defaults tobilling; set topublicexplicitly to keep Accrue tables in the default schema. This is compile-time configuration for Ecto schemas and migration generation. The default value is"billing".:processor(atom/0) - Processor adapter implementingAccrue.Processorbehaviour. The default value isAccrue.Processor.Fake.:rails(keyword/0) - Additive entitlement rail registrations. Omit to retain legacy single-processor behaviour. The default value is[].:default_rail- The registered controllable rail that agrees with the legacy :processor alias. The default value isnil.: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.:invoice_pdf_adapter(atom/0) - Invoice PDF adapter implementingAccrue.InvoiceRendererbehaviour. Defaults to the native Rendro-backed invoice renderer. The default value isAccrue.InvoiceRenderer.Rendro.:pdf_adapter(atom/0) - Legacy HTML-to-PDF adapter implementingAccrue.PDFbehaviour. Used by the Chromic invoice renderer path and by custom HTML callers. The default value isAccrue.PDF.ChromicPDF.:auth_adapter(atom/0) - Auth adapter implementingAccrue.Authbehaviour. The default value isAccrue.Auth.Default.:plan_resolver- Optional host-owned resolver module implementingAccrue.PlanResolver. Required when a processor needs more than a bareprice_idto perform plan changes through the shared facade (for example Braintree swap-plan flows). The default value isnil.: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[].:portal_mount_path(String.t/0) - Mount path for the first-partyAccrue.Portalroutes when a processor uses local portal semantics. Returned checkout / billing-portal URLs append to this normalized path after:portal_base_urlsupplies the absolute host. The default value is"/billing".:portal_base_url- Absolute base URL (for examplehttps://app.example.com) used to generate returned local portal checkout and billing-portal URLs. Leave unset only when those local portal URLs are not in use;portal_url/1raises instead of falling back to a relative path. The default value isnil.:braintree_client_token_generator(atom/0) - Module used to generate Braintree client tokens for Hosted Fields. Must exportgenerate/1. The default value isBraintree.ClientToken.: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.:campaignis the multi-step dunning cadence (see its own doc). The default value is[mode: :stripe_smart_retries, grace_days: 14, terminal_action: :unpaid, telemetry_prefix: [:accrue, :ops], campaign: [enabled: true, steps: [[after_days: 0, key: :reminder, template: Accrue.Emails.InvoicePaymentFailed], [after_days: 5, key: :action_required, template: Accrue.Emails.DunningActionRequired], [after_days: 12, key: :final_notice, template: Accrue.Emails.DunningFinalNotice]]], engine: Accrue.Dunning.Engine.Oban].:mode- The default value is:stripe_smart_retries.:grace_days(pos_integer/0) - The default value is14.:terminal_action- The default value is:unpaid.:telemetry_prefix(list ofatom/0) - The default value is[:accrue, :ops].:campaign- Multi-step dunning cadence (D-04). A keyword list of[enabled: boolean, steps: [...]]where each step is[after_days: non_neg_integer, key: atom, template: atom].after_daysis ABSOLUTE from campaign start, strictly increasing and unique across the list;keyis required and unique (it becomes the Oban-unique step identity). Ships a default journey ON by default (offsets[0, 5, 12]); setcampaign: false(normalized to[enabled: false, steps: []]) to opt out.steps: []whileenabled: trueis a loud error. The last step'safter_daysMUST be<= grace_days(validated loud at boot) so the final notice precedes the sweeper's terminal action. The default value is[enabled: true, steps: [[after_days: 0, key: :reminder, template: Accrue.Emails.InvoicePaymentFailed], [after_days: 5, key: :action_required, template: Accrue.Emails.DunningActionRequired], [after_days: 12, key: :final_notice, template: Accrue.Emails.DunningFinalNotice]]].:engine(atom/0) - Module implementingAccrue.Dunning.Engine. Default:Accrue.Dunning.Engine.Oban(built-in Oban campaign). Set toAccrue.Integrations.Chimewayto delegate orchestration to Chimeway. The default value isAccrue.Dunning.Engine.Oban.
: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.:theme- Customer portal color-mode policy.:system(default) offers light + dark with a picker (follows the OS, user-switchable via theaccrue_themecookie).:lightor:darklocks the portal to that mode and hides the picker. The default value is:system.
:admin_branding(keyword/0) - Optional Accrue Admin chrome branding. When unset, admin chrome keeps the legacy behavior and derives its app name, logo, and accent from:branding. Set this when customer-facing billing should use one brand while the mounted operator UI keeps an Accrue/admin identity. The default value is[].:app_name(String.t/0) - The default value is"Billing".:logo_url- The default value isnil.:accent_color- The default value is"#5D79F6".
: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]].:entitlements(keyword/0) - Plan->feature/quota entitlement catalog (host-owned, runtime). Boot-validated and the sameprice_idmay not map to two plans (raisesAccrue.ConfigErrorat boot). See guides/entitlements.md. The default value is[].:plans(keyword/0) - Logical plan name (atom) -> entitlement entry (features/limits/price_ids) The default value is[].:features(list ofatom/0) - The default value is[].:limits(keyword/0) - The default value is[].:price_ids(list ofString.t/0) - The default value is[].:products(keyword/0) - The default value is[].
:resolver(atom/0) - Resolver module (Accrue.Entitlements.Resolver behaviour). Default LocalMap. The default value isAccrue.Entitlements.Resolver.LocalMap.:unmapped_action- Behaviour when an active price_id is unmapped. :deny fails closed; never silent-allow. The default value is:deny.:billable- Global billable resolver: a 1-arity fn(conn | socket -> billable | nil). When unset, the default probescurrent_scope.user -> current_user -> nil. Must never raise — fail closed (resolve to nil) on any miss. The default value isnil.:on_deny- Global deny handler. Default is a content-negotiated opaque 403. Accepts:forbidden | {:redirect, path} | {status, body} | fun/2 | {m,f,a}. A per-guardon_deny:opt overrides this; this overrides the built-in 403. A malformed value fails loud at boot (never silently fails open). The default value is:forbidden.:deny_path(String.t/0) - LiveView fallback redirect target for:forbidden/ non-redirectable denies. The default value is"/".:past_due_grace- Entitlement access for :past_due subscriptions. :none (default) fails closed immediately. :dunning honors the dunning grace window (reuses Accrue.Config.dunning()[:grace_days]). A positive integer N honors an entitlement-specific N-day window. Grace grants are affirmative, resolved, configured decisions — never a fail-open. The default value is:none.:stripe_native_sync- Optional Stripe-native entitlement-summary sync. :disabled (default) is fully inert — no webhook reducer runs, no cache table is read or written. :advisory records Stripe entitlement summaries to an advisory cache for audit / telemetry / the admin read-seam; it does NOT change entitled?/has_active_plan? decisions in v1.x (local mapping stays canonical). The enum (not a boolean) reserves room for future additive modes without a breaking config change. The default value is:disabled.:multi_rail(keyword/0) - Compatibility authority lane for canonical entitlement projection. Omit it (or use :disabled) to retain LocalMap authority; :shadow and :enabled require an explicit approved-account/MFA cohort. The default value is[].
Summary
Functions
Returns branding for the mounted Accrue Admin operator chrome.
Returns a single admin branding key. Raises if the key is unknown.
Runtime billing schema prefix used by migration and raw SQL helpers.
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.
Compile-time billing schema prefix used by Accrue Ecto schemas.
Returns the Connect config keyword list.
Returns the number of days to retain :dead webhook events.
Returns the application default locale string.
Returns the configured controllable entitlement rail, when rails are configured.
Returns the application default IANA timezone string.
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.
Returns the dunning campaign cadence config (D-07).
Returns whether the multi-step dunning campaign is enabled (D-07).
Returns the configured dunning campaign steps (D-07).
Returns the configured dunning engine module (D-03).
Returns logical plans indexed by {rail, environment, product_id}.
Returns the runtime :entitlements config keyword list (the plan->feature/
quota catalog), or the schema default [] when unset.
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 validated compatibility authority configuration.
Returns the past-due entitlement grace policy: :none (default,
fail-closed), :dunning (reuse the dunning grace window), or a positive
integer of days.
Returns configured entitlement rails, or [] when a host uses the legacy processor path.
Returns the NimbleOptions schema keyword list. Used by boot-time validation to iterate keys.
Returns the configured Stripe API version string.
Returns the optional Stripe-native entitlement-sync mode: :disabled
(default, fully inert) or :advisory (record summaries to the advisory
cache for audit / telemetry / the admin read-seam — does NOT change
entitled?/has_active_plan? decisions; local mapping stays canonical).
Ergonomic predicate for the Stripe-native sync gate: true when sync is
enabled in any non-:disabled mode, false otherwise. The webhook
reducer dispatch checks this FIRST so the off lane early-returns before
any Repo call.
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 the :dunning.campaign cadence
(DUN-01, D-04/D-05).
NimbleOptions :custom validator for :branding.accent_color /
:branding.secondary_color. Accepts #rgb, #rrggbb, and
#rrggbbaa hex color strings; rejects anything else.
NimbleOptions :custom validator for the global :entitlements
on_deny handler (Phase 124, ENT-06).
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 admin_branding() :: keyword()
Returns branding for the mounted Accrue Admin operator chrome.
Backwards compatible default: when :admin_branding is unset, derive the
admin label, logo, and accent from the customer billing :branding config.
Hosts that need a split identity can set :admin_branding explicitly.
Returns a single admin branding key. Raises if the key is unknown.
@spec billing_schema() :: String.t()
Runtime billing schema prefix used by migration and raw SQL helpers.
@spec branding() :: keyword()
Returns the branding config keyword list.
Reads the nested :branding keyword list from app env, merging any
unset keys with their schema defaults so callers can Keyword.fetch!
any valid key without re-running the full NimbleOptions validator on
every call site.
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 compile_time_billing_schema() :: String.t()
Compile-time billing schema prefix used by Accrue Ecto schemas.
@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_rail() :: atom() | nil
Returns the configured controllable entitlement rail, when rails are configured.
@spec default_timezone() :: String.t()
Returns the application default IANA timezone string.
@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.
@spec dunning_campaign() :: keyword()
Returns the dunning campaign cadence config (D-07).
Raw read with its own nested default — dunning/0 does not normalize the
nested :campaign key (identical constraint to past_due_grace/0), so
the default journey is supplied here when a host overrides :dunning
without re-stating :campaign. Ships ON by default.
@spec dunning_campaign_enabled?() :: boolean()
Returns whether the multi-step dunning campaign is enabled (D-07).
false when the campaign is opted out (campaign: false /
enabled: false). Fail-closed: an absent :enabled key reads as false.
@spec dunning_campaign_steps() :: [keyword()]
Returns the configured dunning campaign steps (D-07).
Returns [] when the campaign is disabled (so downstream consumers never
fire steps for an opted-out host), else the configured step list.
@spec dunning_engine() :: module()
Returns the configured dunning engine module (D-03).
Defaults to Accrue.Dunning.Engine.Oban (built-in Oban campaign).
Set dunning: [engine: Accrue.Integrations.Chimeway] to opt into
Chimeway orchestration.
The atom is returned as-is without loading the module — resolution happens at dispatch time in the calling code.
Returns logical plans indexed by {rail, environment, product_id}.
@spec entitlements() :: keyword()
Returns the runtime :entitlements config keyword list (the plan->feature/
quota catalog), or the schema default [] when unset.
This is a thin runtime accessor (get!/1, NOT compile_env!) because
:entitlements is host-owned catalog data that legitimately differs per
environment (e.g. test-mode vs live-mode price_ids), like :branding and
:dunning. Unlike branding/0 (which merges schema defaults), this is a
raw runtime read — it does NOT apply the nested per-plan defaults
(features: [], limits: [], price_ids: []). validate_at_boot!/0
validates the config but discards the normalized result and never writes it
back to app env. The resolver tolerates missing nested keys via
Keyword.get/3 defaults, so it reads this raw value without re-running the
full validator.
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 multi_rail() :: keyword()
Returns the validated compatibility authority configuration.
@spec past_due_grace() :: :none | :dunning | pos_integer()
Returns the past-due entitlement grace policy: :none (default,
fail-closed), :dunning (reuse the dunning grace window), or a positive
integer of days.
The :none default is applied here because entitlements/0 does a raw
read without normalizing nested :entitlements defaults. The
:dunning -> grace_days resolution is done by the resolver when it
widens the fold, not by this accessor.
@spec plan_resolver() :: module() | nil
@spec portal_base_url() :: String.t() | nil
@spec portal_mount_path() :: String.t()
@spec rails() :: keyword()
Returns configured entitlement rails, or [] when a host uses the legacy processor path.
@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 stripe_native_sync() :: :disabled | :advisory
Returns the optional Stripe-native entitlement-sync mode: :disabled
(default, fully inert) or :advisory (record summaries to the advisory
cache for audit / telemetry / the admin read-seam — does NOT change
entitled?/has_active_plan? decisions; local mapping stays canonical).
The :disabled default is supplied here because entitlements/0 does a
raw read without normalizing nested :entitlements defaults (identical
constraint to past_due_grace/0).
@spec stripe_native_sync?() :: boolean()
Ergonomic predicate for the Stripe-native sync gate: true when sync is
enabled in any non-:disabled mode, false otherwise. The webhook
reducer dispatch checks this FIRST so the off lane early-returns before
any Repo call.
@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 the :dunning.campaign cadence
(DUN-01, D-04/D-05).
Accepts:
false— normalized to[enabled: false, steps: []](opt-out, D-05).- a keyword list
[enabled: boolean, steps: [...]]where each step is a keyword list[after_days: non_neg_integer, key: atom, template: atom]validated against@step_schema.
Intra-list invariants (the cross-field last_step.after_days <= grace_days
invariant is checked separately at boot by
validate_dunning_campaign_grace!/1, since a {:custom} validator cannot
read the sibling :grace_days key):
after_daysstrictly increasing AND unique across the list.keyunique across the list.- when
enabled: true,stepsMUST be non-empty —steps: []while enabled is a LOUD{:error, _}(never a silent disable, D-05).
Returns {:ok, normalized_keyword} on success, {:error, message} on
failure (so boot validation fails loud rather than silently mis-firing).
NimbleOptions :custom validator for :branding.accent_color /
:branding.secondary_color. Accepts #rgb, #rrggbb, and
#rrggbbaa hex color strings; rejects anything else.
NimbleOptions :custom validator for the global :entitlements
on_deny handler (Phase 124, ENT-06).
Accepts any of the supported deny-handler shapes and returns
{:ok, value}; a malformed value returns {:error, message} so boot
validation (validate_at_boot!/0) fails loud rather than letting a
broken deny path silently fail open (T-124-01).
Supported shapes:
:forbidden— built-in opaque 403 / LiveView redirect todeny_path.{:redirect, path}whenpathis a binary.{status, body}whenstatusis an integer andbodyis a binary.- a 2-arity
fun(container, ctx -> result). - an MFA
{m, f, a}whenm/fare atoms andais a list.
@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.