Choose the notification channel whose recipient eligibility you can reconstruct after a failure. For a property-management startup comparing an email API with an SMS API for event alerts, that means accounting for bounce handling, suppression, and evidence review before comparing dispatch charges.

TL;DR: Keep routine email alerts subject to a destination-specific suppression check; authorize SMS through a separate eligibility decision. Before resuming notifications after an address correction, require 3 checks: the failure belongs to the original destination, the correction identifies a new destination version, and any independent restriction still applies. Record the evidence behind each decision. A successful API call cannot establish those facts.

This decision record proposes a Go implementation for maintenance notices and rent-account events. Its decision axis is compliance evidence, examined through reconciliation: an operator should be able to reproduce why a destination was suppressed, why a corrected address became eligible, and why an email failure did or did not produce a text message. These are design requirements, not claims about a deployed system. The FTC's CAN-SPAM guide supplies the US email-purpose boundary; SMTP specifications supply delivery-status distinctions. Neither establishes that a property notice satisfies a jurisdiction's service requirements.

Should email API failures trigger SMS alerts for property events?

An address correction is a new decision, not an instruction to erase the old bounce. Consider illustrative records for property P-204, tenancy T-73, and maintenance event M-108: email destination version 2 receives a confirmed invalid-mailbox report, while a resident subsequently supplies destination version 3. A later replay of the report for version 2 should remain visible without suppressing version 3. Conversely, changing the address must not silently erase a separately recorded opt-out. These identifiers and versions describe a proposed test fixture, not customer data or measured results.

Keep the history.

SMTP enhanced status codes distinguish success, persistent transient failure, and permanent failure through the first component of the code; RFC 3463 also separates address status from policy and other failure subjects. The permanent-failure class alone is insufficient evidence that a mailbox does not exist. A policy rejection needs its own classification. Suppressing every address associated with a permanent response can turn a delivery-policy problem into a growing population of incorrectly excluded residents.

For this design, the bounce consumer records the authenticated source, correlated message, original destination version, classification, and policy revision. The send decision records the version it evaluated. Reconciliation then asks a precise question: did the authorization use the destination and restrictions that were current at that point in the recorded ordering?

That question is narrower than delivery success. It is also answerable.

The FTC guide makes another distinction relevant to property notices: commercial messages and transactional or relationship messages have different treatment, and mixed content requires a primary-purpose assessment. Calling a maintenance template “transactional” in code does not complete that assessment. Keep the reviewed message purpose with the template revision so that a later promotional edit cannot inherit an unexplained eligibility decision. The cited guidance concerns email; SMS permission and legally sufficient property notice require separate applicable authority and review.

Invariants: reconstruct decisions without rewriting the ledger

The proposed system treats notification evidence like a journal alongside the business ledger. A rent-account adjustment has its own authoritative record; its notification can fail without reversing the adjustment, and notification acceptance must not mutate the account balance. Linking the two records makes reconciliation possible while preserving their different meanings. An evidence journal supports auditability, but its existence alone is not a claim of regulatory compliance. Three invariants govern the journal. First, a delivery report may affect only the account and destination version established by its correlated outbound attempt. Second, committing a report receipt must also commit its classification and resulting suppression transition, or commit none of them. Third, a correction must retain the historical restriction and explicitly identify which destination version the new eligibility decision covers. Opt-outs and invalid-address restrictions remain separately identifiable because an address correction is evidence about address validity, not evidence that every other restriction has ended.

The exactly-once mindset belongs at that local transition boundary. It does not extend automatically to remote delivery.

There are two failure boundaries to document. An unmatched or unauthenticated report cannot authorize a suppression update; retain it for investigation through a controlled evidence process. A send timeout, meanwhile, can leave the external result unknown: the remote system might have accepted the request before the local caller lost the response. Preserve that uncertainty in the attempt record and reconcile against documented delivery evidence before deciding on another attempt. A retry decision needs a reason of its own.

Ordering matters too. Suppression updates and send authorization must share a serialization rule in the storage implementation: if suppression commits first, a subsequent authorization must observe it. If authorization precedes the bounce, the audit trail should show that ordering. A worker that checks eligibility, waits in a queue, and sends much later without another check has created a gap between its evidence and its action; the design must define where authorization occurs and what changes invalidate it.

Compare policies by the evidence they leave behind

The following alternatives concern the same property workflow. They compare operating policies rather than commercial products, so the choice turns on the required alert outcome and the work needed to explain it.

Policy Evidence required Treatment of an invalid email address Operational trade-off
Email for routine eligible notices Message purpose, destination version, current restrictions Suppress the affected version and retain the report One channel to reconcile, with unresolved notices requiring an explicit follow-up process
Independently authorized SMS for urgent alerts Phone eligibility, alert urgency, applicable permission decision Preserve email suppression independently A second result stream and permission workflow to maintain
Conditional SMS escalation after email failure Both channel decisions plus the qualifying failure Keep the email failure and SMS attempt linked but distinct More branching and reconciliation, justified only by the escalation requirement

For the startup asking which API is most cost-effective, these obligations belong in the evaluation alongside dispatch charges: callback maintenance, investigation of unmatched reports, retries, evidence retention, and support work when a resident disputes a notice. No unit-price comparison resolves an unexplained eligibility transition. Select the smallest channel policy that meets the actual alert requirement and whose evidence the team can operate.

The trade-off is deliberate: independent SMS authorization adds work, but it prevents an email diagnostic from being mistaken for permission to contact a phone number. Urgency can justify designing an escalation path. It cannot supply the missing recipient evidence.

Go critical path: commit a replay-safe suppression decision

The critical path starts after source authentication, message correlation, and classification have established a confirmed invalid email destination. The code defines a storage contract rather than a provider integration; its receipt key is scoped to the authenticated account and source, while its destination version comes from the correlated attempt. Those are distinct identities.

package notifications import ( "context" "errors" ) type ReportKey struct { Account, Source, ID string } type Destination struct { Account, Tenancy string Version int64 } type InvalidEmail struct { Key ReportKey Target Destination MessageID, EvidenceRef, PolicyRevision string } type Tx interface { // Claim enforces uniqueness within the enclosing transaction. Claim(context.Context, ReportKey) (bool, error) AppendInvalidEmail(context.Context, InvalidEmail) error // SuppressEmail is idempotent for the same destination version. SuppressEmail(context.Context, Destination) error } type Store interface { // Within commits all writes together or rolls them all back. Within(context.Context, func(Tx) error) error } func ApplyInvalidEmail(ctx context.Context, db Store, r InvalidEmail) error { if r.Key.Account == "" || r.Key.Source == "" || r.Key.ID == "" || r.Target.Account != r.Key.Account || r.Target.Tenancy == "" || r.Target.Version < 1 || r.MessageID == "" || r.EvidenceRef == "" || r.PolicyRevision == "" { return errors.New("incomplete or inconsistent email evidence") } return db.Within(ctx, func(tx Tx) error { fresh, err := tx.Claim(ctx, r.Key) if err != nil { return err } if !fresh { return nil } if err := tx.AppendInvalidEmail(ctx, r); err != nil { return err } return tx.SuppressEmail(ctx, r.Target) }) } 

The important implementation work sits behind Within and Claim: concurrent consumers must not both commit a fresh claim for the same scoped report. The claim, evidence append, and suppression update need database-enforced atomicity and uniqueness. The interface cannot provide those properties by itself. An existing receipt must represent a committed transition; a rolled-back claim must remain retryable, and a competing uncommitted claim must be resolved by the storage implementation rather than treated as completed work.

There is a concrete crash trap here. If the consumer commits its receipt in one transaction and suppression in another, a crash between them can leave the replay marked complete while the invalid address remains eligible. A transaction spanning all three writes removes that local partial result. Different reports for the same destination can still append separate evidence while the suppression operation remains idempotent.

This path deliberately grants no eligibility to a corrected address and sends no SMS. Both actions need separate decisions. A correction workflow can refer to the earlier suppression while establishing the replacement version; an SMS workflow can refer to the email outcome while checking its own destination and restrictions. Keeping these contracts small makes it easier to inspect which evidence authorizes each state transition.

Rejected option: resume every channel after an address correction

Clearing all restrictions when a resident edits an email address is rejected because the edit supplies evidence about one destination. It does not explain why a historical opt-out should disappear or why a phone number should become eligible. A valid narrower use case is releasing an invalid-address restriction for a corrected destination after the configured verification and eligibility review, with the release recorded as a new decision. Historical reports stay attached to their original version.

Before deployment, exercise the fixture through a small replay matrix: duplicate report, concurrent duplicate, rollback after claim, old-version report after correction, and email failure with an ineligible SMS destination. The expected outcomes should include both the current restriction and the journal entries. Test send authorization racing with suppression as a storage-level property; sequential handler tests cannot establish the ordering guarantee.

Deploy classification-policy changes with a recorded revision and review their effect before allowing them to change eligibility. For observability, track unmatched evidence, unresolved send outcomes, replayed receipts, and time between a report's receipt and its committed suppression. These are proposed operational signals, not measured service targets. Assign an owner to unresolved records so that collecting evidence does not become a substitute for resolving it.

Retention also needs a purpose and a limit. Restrict access to recipient details, use controlled evidence references, and determine retention requirements through the applicable compliance review rather than assuming an indefinite archive is required by the email guidance. Reconciliation needs enough evidence to explain a decision; copying full message bodies into every log expands the review burden without necessarily improving that explanation.

For property alerts, the decisive result is an explainable next action: suppress the invalid destination, evaluate a corrected one under its own version, or escalate through an independently eligible channel. That is the evidence a startup must be able to maintain whichever delivery API it chooses.

References