The problem
A subscription status is only useful if every connected system agrees.
The CRM needed to know whether a contact was on the Free Tier, Ignite, Growth, Scale, or Canceled. A new signup had to create the right contact state, upgrades and downgrades had to replace the old tier cleanly, and cancellation had to remove paid access rather than simply adding another tag on top.
The tricky part was not sending data from Stripe to Zoho. The tricky part was maintaining a trustworthy state across multiple event types.
The event-driven flow
The scenario listens for subscription-created, subscription-updated, and subscription-deleted events.
A first router separates the active subscription paths from the cancellation path.
Price IDs map to the intended CRM tier so the workflow has one clear target state rather than relying on free-text matching.
The contact record is updated with the relevant subscription information before tag changes are applied.
Old paid-tier tags are removed so the contact cannot simultaneously look like multiple subscription levels.
The correct tier tag is added. On deletion, paid tiers are removed and the contact receives the Canceled state.
The hardest part was dynamic data, not the router diagram.
During the build, static updates could work while dynamic tag updates still failed. That meant separating “the API call works” from “the values being passed into the API call are correct.” I tested the pieces independently, corrected the update body and mapping, adjusted filters and aggregators, and then reintroduced the dynamic tier variables.
That debugging pattern is important in integration work. When several systems are connected, changing everything at once turns one error into a fog bank. I prefer to make one assumption testable at a time.
Final logic
- New Stripe signup: create or identify the CRM contact and establish the initial state.
- Subscription created or updated: derive the current plan, update the record, remove old tier tags, and add the correct tier.
- Subscription deleted: mark the canceled state, remove paid tiers, and apply the Canceled tag.
- Unrelated events or unchanged states: stop rather than performing unnecessary writes.
What this project demonstrates
Integrations are not really about moving JSON from one box to another. They are about maintaining business meaning across systems. In this case, the useful outcome was not “Stripe is connected to Zoho.” It was that the CRM could reliably represent the customer’s current subscription tier.