Missed calls: auto-SMS with the real ship date instead of a canned reply
A cloud-telephony webhook → customer and order lookup across two Shopify stores → an SMS carrying the actual fulfillment status. Plus a "Missed calls" page in the CRM: who called, what's in their orders, and a ready-to-send reply.
The customer calls during their business day — it's the middle of the night for support
A US outdoor-gear brand, two Shopify stores, and a support team several time zones away. The customer dials in the middle of their working day — which is evening or night for the team. Roughly 70% of inbound calls went unanswered.
Missed calls were not collected anywhere. The morning started not with a list but with a wall of notifications in the telephony app: a median of 5 overnight, up to 25 on peak days. Working through that feed by hand is a job of its own, and nobody was doing it systematically.
The single most common reason for calling was "where is my order". In other words, people called precisely because they couldn't see a status: an expensive product, a long production lead time, and silence in both the emails and the account page.
The built-in telephony auto-reply made it worse: night callers received a daytime "we'll call you back shortly", and on one of the lines the business-hours toggle was switched off entirely — so the canned message went out to everyone, always.
Webhook → Shopify order → an SMS with the real date
A call.completed
webhook receiver on the CRM side: signature verification, filtering out irrelevant event types,
protection against processing the same call twice (otherwise the customer gets two callbacks),
and a journal entry. From there the caller's number resolves to a customer and their orders
across both stores — and the reply gets assembled.
call.completed from cloud telephony: HMAC signature, event-type filter, dedup by call id
Caller number → customer and their orders across two Shopify stores
From "the real ship date" down to a neutral fallback
Quiet hours, per-number cooldown, rate limiting, draft mode
"Missed calls" page: who called, their orders, ready-made reply, "handled" checkbox
The key finding: the order has no ship date
The central question of the whole pipeline is where to get a date you're not ashamed to send a customer. It turned out that a calendar ship date does not exist anywhere on the Shopify order. Four other candidate sources were checked and rejected — none of them carries the date the customer was actually promised.
The only carrier of that promise is the name of the shipping tier chosen at checkout: that's where the buyer saw a timeframe and agreed to it. The date is derived from that string. This is the kind of project where two weeks of work hinge on one design decision — and you have to find it before writing code.
The reply ladder: from useful to fallback
The reply is assembled in steps, from maximally specific down to neutral, and no step is allowed to lie:
- customer and order found, promised date still in the future → state the date
- promised date already passed → do NOT repeat it; say plainly that we're checking with the warehouse
- order exists but no date can be derived → reference the order without inventing a promise
- no customer matched the number → neutral reply, the call goes into the human queue
The "never repeat an overdue date" rule was validated against the historical export (over 11k orders): not a single old, stuck order gets handed a future date.
Safety rails on sending
An auto-SMS goes out under the business's name with no human in the loop. That makes the limiters more important than the generation itself:
- quiet hours: sending only inside an 11:00–21:00 New York window — safe across all continental US time zones; Alaska and Hawaii are handled separately
- per-number cooldown — a repeat call doesn't turn into a stream of messages
- rate limiting for the pipeline as a whole
- draft mode: everything is computed and logged but nothing leaves the system — plus a separate explicit flag for live sending
The "Missed calls" page in the CRM
The auto-reply covers first contact, but a human still has to work the calls. Instead of a
notification feed there is one page: who called and when, the number as a
tel:
link, the matched customer, their orders with state ("unfulfilled · 82 days"), a
ready-to-send reply, and a "handled" checkbox.
Degradation instead of failure is built in: a malformed queue line does not take the page down, and a read failure renders "queue could not be read" rather than "no calls". The difference matters: in the second case the operator calmly closes the tab and walks away.
Nothing extra — this thing has to survive the night unattended
Webhook receiver and HMAC signature check without a web framework
Call webhooks, SMS API, voicemail transcripts
Customer lookup by phone number and their orders across two stores
Reply text written for the specific order, not slotted into a template
Receiver service, auto-restart, journal
Over 100 tests on the pipeline: reply ladder, quiet hours, dedup, event parsing
Data first, code second
missed calls per month — the problem was 1.6× larger than the initial estimate
of missed calls already carry a voicemail transcript — a ready answer to "what did this person want"
of messages became single-segment after a one-character fix
The work started with a telephony export, not with code. The analysis showed the problem was 1.6× larger than the initial estimate, that 29% of missed calls already had a voicemail transcript — a channel nobody was using at all — and that 36% of customer SMS never received a human reply. Three facts that appeared in no report.
Three breakages fixed along the way — without which the pipeline was dead
- customer lookup crashed on a signature mismatch — every call was treated as "customer not found"
- event parsing read the wrong nesting level and silently discarded every missed call
- the caller's number was being read from a field the webhook does not contain
None of the three were visible from the outside: the service ran, logs were written, no SMS went out.
A separate find was a one-character detail with a direct effect on the bill: an em dash in the templates pushed messages outside the basic SMS alphabet and into a two-byte encoding, so 100% of messages took at least two segments. Replacing the em dash with a hyphen makes 62% of them single-segment.
The business owner switched live sending on and the first messages were delivered. Every missed call now lands in the CRM queue and gets a reply with a real status — instead of a daytime "we'll call you back shortly" at three in the morning.
Where else the same methodology applies
This isn't "an answering machine". It's the standard task of "an event in a communication channel → pull the customer's context from the system of record → answer with a fact, not a template". The same architecture applies anywhere inquiries arrive outside business hours:
- → Online stores with long lead times — "where is my order" gets closed by a status from the system of record, not by a callback
- → Service and repair shops — missed call → SMS with the work-order stage and the real readiness date
- → Clinics and salons — a call outside opening hours → appointment confirmation or the next open slot from the schedule
- → Any business whose customers sit in another time zone — a night shift replaced by a pipeline that knows the facts about the order
- → Any inbound channel — email, messenger, web form: only the event source changes, the reply ladder and the safety rails stay
- Webhook receiver with signature verification, event-type filtering and duplicate-processing protection
- A reply ladder with an explicit ban on quoting an overdue date — dry-run against the historical export before go-live
- The sending safety-rail set: quiet hours in the customer's time zone, per-number cooldown, draft mode, an explicit live-mode flag
- A queue page with ready-made reply text and degradation instead of failure: "couldn't read" is never rendered as "empty"
If missed calls are piling up — measure first, automate second
I start with an export from your telephony: how many calls are actually missed, what people call about, and what's already sitting in channels nobody reads. Then comes the auto-reply pipeline built on facts from your system of record. In this case: about two weeks from data analysis to the first delivered messages.
The 5,000 ₽ audit — with a concrete report and quote
I'll tell you what to deploy in your business first, what the payback looks like, and whether you need AI for the task at all (sometimes you don't).
Or just send your question — I reply within 2 hours