The single module every customer send goes through — estimates, contracts, portal links, reminders, stage automations and the inbox composer. It resolves which providers this account uses in priority order, tries them until one accepts, records what happened with the true billable quantity, and queues a retryable failure instead of losing it.
Also called: how messages go out · email provider · sending engine · why didn't that send
- 1caller → sendCustomerEmail / sendCustomerSms → provider registry → adapter chain, first 'sent' wins
- 2Every attempt writes a send_log row naming the provider, the outcome and the units
- 3A retryable failure (the request never reached the provider) is persisted to the outbox rather than lost
- 4A hard failure with no later fallback surfaces as a real error instead of collapsing into generic 'nothing configured' guidance
- 5Public signatures are unchanged from before the refactor, so none of the ~14 callers needed editing
Changing which provider an account sends through changed contracts, invites and notifications but not the conversation composer, which quietly carried on using the old one — so "which provider does this account send with" had two different answers depending on which part of the product you asked. That is the kind of split that surfaces as a customer not receiving something, weeks later. Every customer-facing email and text now leaves through one path that resolves the account's provider chain, logs the attempt and retries what is worth retrying.
- Two different answers to 'which provider sends this'
- Real provider errors masked as configuration guidance
- Sends lost to a transient network failure
See it on your own jobs
Twenty minutes, your numbers, no slide deck. We’ll build one of your real buildings in front of you and send you the estimate link at the end — yours to keep either way.
or keep browsing features →