Signup Recovery needs a RevenueCat or Stripe account connected, the same one used for cancellation recovery. See Connecting RevenueCat or Connecting Stripe.
The request
Your backend posts one event per signup:Where to place the call
Where you call the webhook decides when we start. The event is the trigger. We wait 5 to 60 minutes, then check whether the person has paid. We call only if they have not. We never call a signup older than 48 hours. So send the event at the buy decision, not before it.
Send the event at the paywall, the moment someone sees your price and does not buy.
Fields
The minimum event
The smallest event that produces a call depends on your billing provider:- RevenueCat:
phoneNumberandconsent.attested: true, plusappUserId - Stripe:
phoneNumberandconsent.attested: true, plusemailorstripeCustomerId
app_user_id. Stripe answers only for a Customer id or an email. hyzl stores an event that has a phone number but no identity, never verifies it, and never calls the person.
The Stripe check looks for subscriptions, including cancelled and expired ones. A one-time Stripe payment does not count as paid. A member whose only purchase is a one-time product can still get a call.
Get your webhook URL and ingest token
- Go to Revenue Recovery → Workflows and click New workflow
- Choose “A user signs up but does not pay” as the trigger
- Connect (or choose) whichever provider your purchases run through: RevenueCat or Stripe
- Set your win-back offer, the wait time, the daily call cap, and the calling window
- Copy your webhook URL, your
ingest token, and the copy-paste snippets in curl, Node, and Swift - Tick the consent acknowledgment. Nothing turns on until you do, here or from the Workflows list
- Finish the campaign (script, voice, schedule) and hit Finalize & Launch
ingest token to your developer. Or hand them the developer bundle described at the end of this page.
Send the event
Send it in steps
Most apps do not have a phone number at signup. Send what you have, then send the rest once you have it. Use the sameappUserId on every step. We assemble the record for you, joining on appUserId first, then email, then phone number, whichever the two requests share.
Node
firstName does not blank out a firstName step 1 already sent.
Consent
Add a checkbox next to where you already collect the phone number. Here is the wording we provide:- Put the checkbox wherever you already collect the phone number.
- Keep the phone field optional. The signup must finish without it, because consent cannot be a condition of purchase.
- Leave it unchecked by default. A pre-ticked box is not consent.
- Send us its real state as
consent.attested, true or false. Do not send true when it is unchecked. - We store signups that arrive without consent, and we never call them.
Wait time and re-enrollment
After we receive a phone number andconsent.attested: true, we wait before checking whether the person has paid. The default is 5 minutes, adjustable from 5-60 minutes when you set up the workflow.
The wait is not what protects a paying member from a call. RevenueCat and Stripe both know about a purchase within seconds, and we re-check before we queue the call. The wait mainly absorbs ordinary delivery delays on your side.
There is one gap worth knowing about. When a day’s calls hit your daily cap, the remaining calls are held to the next day. The paid check for those ran before they were held. Someone who buys overnight, in that window, can still be called the following morning. Purchases that reach us through RevenueCat or Stripe while a call is still queued do cancel it. So this only affects a purchase we hear about after the call has left the queue.
If the same person hits your paywall again, we do not restart the wait. Once someone has been contacted, resolved, or parked, a new event from them starts a fresh cycle. That cycle starts only after 30 days. Inside that window, hyzl treats it as the same signup.
How many calls, and when
Two settings on the same screen as the offer and the wait time. Both apply to this workflow only, and both can be changed later from Edit on the workflow. Daily call cap. The most calls this workflow places in one day. The default is 100. hyzl does not drop signups past the cap. It calls them the next day instead. Calling window. The hours we call, in each member’s own local time, every day of the week. The default is 9am to 8pm, and that is also the widest it goes. You can pull either end in, for example 10am to 6pm. You cannot push either end out, and there is no out-of-hours override. A narrower window does not mean fewer calls, it means later ones. A signup whose wait ends at 8am is held until the window opens rather than dialed early.Both settings are written to the workflow when you create it, so it runs under them from the first call. If you never open them, you get 100 calls a day between 9am and 8pm.
When nobody answers
A person who never answers still gets the offer. Once every dial has gone unanswered, hyzl checks with your provider one final time. If the person has still not paid, hyzl texts them the offer once.- On Stripe, the text carries a discounted checkout link. The link is minted at send time and expires 24 hours later. The expiry is Stripe’s own, so it is a real cutoff.
- On RevenueCat, the text carries an App Store redemption code from your offer-code pool.
Responses
Treat both
200 and 202 as success. Anything else is a real failure worth logging.
A 202 means we stored the event, not that we called anyone. We wait, check, and call only after both checks pass.
A 200 means we stored it but scheduled nothing, because the workflow is switched off. A draft workflow is off until you press Finalize & Launch. So your developer will see this on every request until then. A check written as if (status !== 202) throw reports a failure on a request that worked.
Every response also carries flowEnabled, contactable and consentAttested, plus a note naming each thing currently blocking a call. Those are the fields to assert on in a smoke test.
While a workflow is switched off
A workflow is off before you launch it, and off again whenever you pause it. hyzl stores events you send in the meantime, so they still count in your reporting. But nothing is scheduled for them, and nothing is called. Turning the workflow on does not call them either. We only call signups that arrive while it is on. A pause is not a queue. A three-day pause does not end in three days of calls landing at once. This is deliberate. It is the same rule we apply to any signup older than 48 hours. Past that point, a call stops being a nudge about a decision someone is still making. To test a live workflow, turn it on and send a fresh signup.Where your signups show up
Every signup you send gets a row in the Signup recovery card on Revenue Recovery → Activity. The row carries the date, the person, one sentence on what happened to them, and an outcome badge. That card has its own search, date filter, and outcome filter. None of them touch the cancellation table above it. Click a row to open the person’s profile, with the call recording and the transcript. See Activity Log.If it is not working
Check the Signup Recovery workflow card in Revenue Recovery → Workflows. It warns you when something is off with the integration and names the exact problem. For example:- A run of events with no
appUserId - A phone number it could not parse
Paste it into an AI coding assistant
The setup step in the portal also has a developer bundle. One copy button produces a self-contained brief covering:- The task
- Where the POST goes
- The consent checkbox
- The field table
- A ready request with your real URL and credential filled in
- How to confirm it is working

