The challenge
On paper, linking a phone system to an automation tool means pointing one webhook at one URL. The real requirements were less tidy. RingCentral’s SMS event doesn’t contain everything the Zapier workflows use, so each one has to be followed by REST calls to fill in the gaps. Message data also includes personal details, and those should reach as few systems as possible.
The service also had to be easy to look after: shipped as a container, configured through the environment instead of code edits, open to inspection when something goes wrong, and able to feed downstream compliance and do-not-call (DNC) checks. Middleware that behaves like a black box gets blamed for every misbehaving workflow, whether it caused the trouble or not.
The larger business risk lay in failures that make no noise. Automations run unattended, so a fault that raises no error can go unnoticed until someone asks why a workflow stopped. We planned around four of them:
- RingCentral webhook subscriptions expire unless renewed, and when one lapses nothing complains: messages simply stop coming in
- Retries at the transport level can resend an event that was already handled, which would start the same Zap again
- Zapier or the network can drop a delivery part-way, so a single attempt isn’t enough
- Each field forwarded to a third party widens the spread of personal data
Our approach
We chose Python and FastAPI and made the service async throughout. Nearly all of its time is spent waiting: on RingCentral’s webhook, on the follow-up REST calls and on Zapier. Async I/O lets one lightweight process hold many of those waits open together, and HTTPX supplies the async client for outbound calls to Zapier.
Subscription expiry got a background task of its own. A renewer keeps the RingCentral webhook subscriptions current, so the integration doesn’t run happily for a while and then go quiet. We see this as the most important safeguard in the service, because it covers the failure that is hardest to notice.
For duplicates we added an idempotency filter held in memory. When a transport retry presents a message that was already passed on, the filter recognises it and drops it before Zapier is called. Memory is the right place for that record while the service runs as a single container. Our advice if it ever scales out to several replicas: move the filter into a shared store, or each copy will only know about its own events.
Delivery to Zapier retries with backoff. A short outage on either side gets time to clear instead of costing the message, and the widening gap between attempts avoids piling pressure on a service that is already struggling.
Masking happens before any data leaves the service. Sensitive fields are hidden in what Zapier receives, so the automation layer sees only what its steps use. That is data minimisation designed in from the start, and it gives the compliance and DNC hooks further along a predictable, trimmed payload.
Day-to-day running is deliberately unremarkable. Pydantic settings read credentials and endpoints from the environment and validate them at start-up, so a missing value stops the service at once instead of surfacing on the first live text. Health endpoints let the container platform confirm it is up, and structured logs record each stage so anyone can trace what happened to a given message.
Every inbound message takes one fixed route:
- Accept the SMS event from RingCentral on the active webhook subscription
- Call RingCentral’s REST API for the metadata the event lacks
- Mask the sensitive fields
- Discard the event if the idempotency filter has seen it before
- Send the structured payload to Zapier, with retries and backoff if delivery fails
Architecture & stack
- Service
- Python, FastAPI, Pydantic settings, HTTPX
- Integrations
- RingCentral, Zapier, Webhooks
- Operations
- Container deployment, Health endpoints, Structured logging
The outcome
The client now has a compact, single-purpose bridge between RingCentral and Zapier. Texts reach Zapier already enriched and with sensitive fields masked, subscriptions look after themselves, failed deliveries are retried and resent events are filtered out.
It is also simple to hand over. It ships as a container, reports its own health and writes structured logs, and because it forwards only what the Zapier steps need, later compliance and DNC checks always receive data in the same shape.
Our advice for anyone wiring a phone system into workflow automation: plan for the failures that make no sound. Renew subscriptions automatically, make delivery idempotent, retry with backoff, and decide which fields may leave your systems before the first message goes through. Building those in at the start costs far less than working out, after the fact, why the automation went quiet.
- Webhook subscriptions renewed automatically, so messages keep arriving
- Resent events filtered out before they reach Zapier
- Personal fields masked before data reaches a third party
- Container deploys with health endpoints and searchable, structured logs
Last updated
