Context
A field-service app sends customers updates: enrolled, booking confirmed, technician on the way, job completed, leak detected. Operations wanted to change wording, timing and quiet hours themselves, and every change was a code release.
The problem
- Message copy, retry rules and opt-out handling lived in application code, so marketing and operations depended on engineering for every edit.
- Webhooks from the field-service platform are redelivered, and a faulty sensor can fire dozens of times a minute. Without protection, a customer could get the same text forty times.
- A failed text must never block the thing that matters: an enrollment saved or a work order opened.
Architecture
- 01Events, not messagesThe app says what happened (job.on_my_way, leak.valve_closed). It never says what to text. Each event name is a contract: new ones may be added, existing ones are never repurposed.
- 02One emitterEvery outbound event goes through one module, which normalizes the phone number, attaches the payload and hands it to the CRM. There is exactly one place to look when a message did not go out.
- 03De-duplicate at the edgeThe last 500 event keys are remembered, so a redelivered webhook or a chattering sensor produces one message, not forty.
- 04Never block the business actionThe emitter cannot throw. If the CRM is slow or down, the enrollment still saves and the work order still opens; the event is logged for retry.
Security model
- The CRM receives only the fields each event needs, not the full customer record.
- CRM credentials are held server side; the browser never calls the CRM directly.
- Opt-out and quiet-hour rules live in one place, the CRM, so compliance does not depend on every code path remembering them.
Tasks now automated
- Sending the right customer update for each of 13 lifecycle events
- Normalizing every phone number before it reaches the CRM
- Suppressing duplicate events from webhook redelivery and faulty sensors
- Enforcing quiet hours and opt-outs on every message
Time returned
- Days to minutes
- Turnaround on a copy change
- 0
- Engineering hours per message edit
- 40 to 1
- Texts from a chattering sensor
Was a code change, review and release; now an edit in the CRM.
Copy and timing moved out of the codebase entirely.
De-duplication collapses repeated events into one message.
Time figures are estimates per unit of work, based on the manual steps the build removed. Every other number on this page is measured.
Outcome
Operations edits message copy, timing and quiet hours in the CRM without an engineering ticket.
Duplicate texts from redelivered webhooks are blocked by design rather than by luck.
Draw the line where the people who change things work. Engineers own events; the business owns words.