Skip to main content
Use Signs up, never pays for a member who creates an account or reaches your paywall without purchasing. Your backend sends hyzl an event when the member leaves without paying. hyzl waits, then checks Stripe or RevenueCat for a purchase. If the member remains unpaid, hyzl sends a heads-up text and calls with your offer.
Connect the billing provider that processes these purchases before you set up this workflow. See Connect Stripe or Connect RevenueCat.
hyzl needs a stable identifier to ask your provider whether the member paid. Standard provider paywalls already support one.

Stripe

Use a hosted Stripe Checkout Session in subscription mode. Set client_reference_id or metadata.userId to your internal member identifier. Stripe grants access after checkout.session.completed, and hyzl uses the same identifier to match the payment. Stripe Pricing Tables, Payment Links, and the Payment Element also work. Set the same identifier on every payment surface. Do not create a Stripe Customer only because someone views the paywall. Stripe stopped doing that by default in 2022. Otherwise, visitors who never pay create unnecessary Customer records.

RevenueCat

Use RevenueCat Paywalls in your app. Send the RevenueCat App User ID as userId. The older field name was appUserId. The identifier exists at the paywall for logged-in and anonymous members. A custom paywall built with getOfferings() also exposes it.

Identify the member

Every event must include a durable identifier. Send userId or a Stripe Customer identifier in stripeCustomerId. For RevenueCat, send the exact identifier your app passes to Purchases.logIn(). RevenueCat can check purchases only by that identifier. Use your authentication system identifier or another internal identifier that you never reuse. Do not use an email address or phone number. Email addresses change, and carriers recycle phone numbers. hyzl can receive several identifiers over multiple events. It keeps identifiers already received and joins later events to the same member.
Send a durable identifier with every event. hyzl rejects email-only or phone-only events with 400 no_user_id because it cannot safely check payment.

Anonymous or optional signup

Use persistent anonymous authentication when a paywall appears before signup or lets the member skip signup.
  • RevenueCat provides an $RCAnonymousID from configure(). Purchases.logIn() links it after signup.
  • Supabase and Firebase provide anonymous sign-in. The anonymous identifier can become the account identifier after signup.
Send the persistent identifier as userId. If anonymous authentication is unavailable, generate a server-side identifier for each device. Never send an event without a durable identifier.

Send the request

Send this request from your backend. Never expose the ingest token in browser or mobile-app code.

Choose the event location

The event starts the recovery timer. hyzl waits 5 minutes to 24 hours and then checks whether the member paid. hyzl never calls a signup older than 48 hours. Send the event at the purchase decision, preferably when the member leaves the paywall without buying.

Personalize the first text

The First text, personalized card appears below First offer text in both signup recovery modes. It starts unset. With a saved personalized message and a goal, hyzl sends that message instead of the first offer text. Otherwise, the standard first text sends unchanged. In Text only, this replaces the initial text. In Call + text, this replaces the first text after unanswered calls.

Send the goal

Add the optional goal string to the signup event:
If the answer is “Other” or blank, omit goal or send an empty string. Do not send the word “Other”. hyzl quotes the value verbatim. An absent, empty, or whitespace-only goal uses the standard first text for a new enrollment. A later blank event keeps an earlier goal. hyzl trims the goal, removes control characters, and caps it at 120 characters. A non-string goal returns 400.

Write the personalized message

  1. Click Customize on First text, personalized.
  2. Insert {goal} where the member’s answer belongs.
  3. Save the workflow.
For example:
The {goal} placeholder works only in the personalized first text. A message without it shows an amber warning and still saves. An unknown placeholder blocks the save.

Request fields

Recommended stage values are:
  • saw_value when the member saw a real result
  • saw_price when the member saw the price but did not buy
  • pre_offer when the member left before the price
hyzl accepts other stage names and passes them to the call as context. Without stage, the AI uses the generic never-paid script.

Minimum event

The minimum contactable event depends on your billing provider:
  • RevenueCat: userId, phoneNumber, and consent.attested: true
  • Stripe: stripeCustomerId or userId, plus phoneNumber and consent.attested: true
The durable identifier lets hyzl confirm that the member remains unpaid. Email and phone are lookup fields only.
Use the exact RevenueCat identifier passed to Purchases.logIn(). A wrong value can make a paid member appear unpaid and cause an incorrect call.
Set Stripe client_reference_id or metadata.userId on the Checkout Session. Without either, hyzl must use stripeCustomerId or the weaker email lookup.
Stripe purchase checks look for subscriptions, including cancelled and expired subscriptions. A one-time Stripe payment does not count as paid.

Get the URL and credential

  1. Open Settings and click New workflow.
  2. Choose Signs up, never pays.
  3. Choose the provider that processes purchases.
  4. Set the offer, wait time, daily call cap, and calling window.
  5. Copy the webhook URL, ingest token, and provided code examples.
  6. Confirm the consent acknowledgment.
  7. Configure the script, voice, and schedule.
  8. Click Finalize & Launch.
Give the webhook URL and ingest token to your developer. The setup also provides a complete developer bundle.

Stable webhook URL

The URL stays the same when you edit, pause, or relaunch the workflow. You can keep it in server configuration. Deleting the workflow retires the URL. A request to a retired URL returns 404 unknown_slug. The response includes the current URL in url, and Settings warns you about recent retired-URL requests.

Request examples

Keep the credential secret. A native Swift app cannot protect a bundled credential. Have your server send the webhook after the app reports the event.

Edge runtimes

Let the POST finish after your endpoint returns. This avoids delaying the member or losing an unfinished request. Always include a stable eventId.

Send the event in steps

You can send available data first and add the phone number later. Use the same userId on every request. hyzl joins requests by userId first, then shared email, then shared phone number. A later request never removes an earlier field.
Node
Add an unchecked checkbox beside the phone number field. Adapt this example with qualified legal advice for your business and location.
This checkbox belongs in your signup or paywall. hyzl provides the example but does not render it. Each clause has a purpose: Follow these rules:
  • Put the checkbox where you collect the phone number.
  • Keep the phone field optional in a web signup.
  • Leave the checkbox unchecked by default.
  • Send the real state as consent.attested, including false.
  • Optionally send consent.text and consent.at for your Telephone Consumer Protection Act evidence trail.
  • Keep the exact accepted wording and time in your own records.
hyzl stores a signup without consent but never contacts that member. If phone number login is required, keep the consent checkbox optional instead.

Wait time and repeat events

After hyzl receives a phone number and consent, it waits before checking payment. The default is 5 minutes. You can choose from 5 minutes to 24 hours. A stored wait of 60 minutes appears as 1 hour when you edit the workflow. The timer starts at startTimerAt, then occurredAt, then receipt time. A later startTimerAt on the same identity resets the timer. Send startTimerAt from your backend. This keeps the ingest token away from the browser. hyzl checks payment again before it queues the call. Purchases usually reach Stripe and RevenueCat within seconds. When calls reach the daily cap, remaining calls wait until the next day. The paid check happens before this hold. Provider purchase events cancel a queued call when they arrive before dialing. After contact, resolution, or parking, a later event can create a new cycle. The new cycle starts only after 30 days. Within 30 days, hyzl treats the event as the same signup.

One event at the paywall

Start at signup and reset at abandonment

Send the first event at signup:
Send the second event when the member abandons the paywall:

Add a Stripe identifier later

Keep userId on the later event and add the Stripe Customer identifier:

Configure call timing

The daily cap and calling window apply only to this workflow. You can change both later from the workflow editor. Daily call cap: The default is 100 calls. hyzl holds extra signups for the next day instead of dropping them. Calling window: The default and widest window is 9:00 AM through 8:00 PM in the member’s local time. You can narrow this window but cannot extend it. Calls run every day of the week. A wait that ends before the window opens stays pending until the opening time.

Configure the offer

By default, hyzl builds the win-back link. For Stripe, hyzl creates a Checkout Session with the coupon already applied. It sends the member a short link after the call. Turn on merchant-provided recovery links in the workflow settings. Then send recoveryUrl for each member. The URL can point to web billing, Stripe, or your app paywall. It must use HTTPS. hyzl sends it unchanged and never reads or adds parameters. A later event can replace an earlier URL. Without a URL, the default behavior parks the member. Turn on fallback to let hyzl build a link instead. hyzl still checks payment before every send. Only link creation changes.

Send your own discount

Turn on merchant-provided discount percentages in the workflow settings. Then send percentOff from 1 through 90. hyzl quotes this percentage on the call and in the offer text. A hyzl-built Stripe link applies the same percentage at checkout. RevenueCat codes keep their App Store Connect discount. On that path, hyzl quotes the campaign percentage that matches the code. A later event can replace an earlier percentage. Without a percentage, the default behavior parks the member. Turn on fallback to use the workflow percentage instead. If you also provide recoveryUrl, make sure your link provides the percentage that hyzl quotes.

Handle unanswered calls

After all attempts go unanswered, hyzl checks payment one final time. If the member remains unpaid, hyzl sends the offer once.
  • Stripe sends a discounted checkout link that expires 24 hours later.
  • RevenueCat sends an App Store redemption code from your inventory.
  • A 0% workflow sends no no-answer offer because it has no discount.
If the member does not redeem the offer, hyzl sends one reminder about 23 hours later. The default voicemail also says that a link will follow. hyzl never replaces voicemail wording that you customized.

Response contract

Treat both 200 and 202 as success. Log and investigate every other response. On a network error or 5xx, retry with the same eventId. A 202 response means stored, not called. A 200 response is normal before Finalize & Launch or during a workflow pause. Events stored while it is off do not run after you turn it on. Every response also includes these diagnostic fields:
  • outcome is created for a new member or joined for a later matching event.
  • alreadyContacted reports whether a joined member already moved beyond waiting.
  • flowEnabled reports whether the workflow is live.
  • contactable reports whether hyzl has a usable phone number.
  • consentAttested reports whether positive consent arrived.
  • note lists conditions that currently block a call.

Cancel pending recovery

Send a cancel when the member opts out, pays elsewhere, or should no longer receive outreach. Replace paywall_recovery_webhook in your webhook URL with paywall_recovery_cancel_webhook. Keep the same slug and bearer credential. Send the durable identifier used during enrollment. Email or phone alone cannot identify a cancellation request. A cancel stops only pending outreach. It stops a member in the wait and cancels a queued, undialed call. It cannot undo a sent text, placed call, or recorded recovery. It never records a purchase.

Pause behavior

hyzl stores events sent before launch or during a pause for reporting. It schedules no contact for those events. Turning the workflow on does not process events received while it was off. This prevents a backlog from causing stale calls. Send a fresh test event after the workflow is live.

Test the integration

Set test: true only with a phone number you control. The test runs within one minute. It bypasses the wait, paid check, and workflow switch. Never set test: true on a real signup. It can call a member without checking whether that member already paid. The portal test button sends this event to your own number. After deployment, Analytics should show the event within about one minute. Inspect the response diagnostic fields if it does not become contactable. Open the workflow card in Settings for integration warnings. The card identifies invalid phone numbers, missing identifiers, retired URLs, and other blocking fields. It also shows affected counts and the latest occurrence.

Find signups in analytics

Open Analytics and choose Signs up, never pays. The funnel tracks Signed up, Unpaid, Contacted, and Paid. The member table shows these outcomes:
  • Waiting means the timer is active or the phone number has not arrived.
  • Due to be contacted means the wait finished and contact has not started.
  • Not contacted includes the specific reason hyzl stopped.
  • Reached means contact succeeded without a later purchase yet.
  • Paid means the member paid within 14 days of signing up. The row shows whether they redeemed your offer or came back through your own checkout.
  • Paid on their own means payment arrived before hyzl made contact. hyzl never counts or bills this outcome.
Click a member row for calls, messages, recordings, transcripts, and payment events. Analytics displays timestamps in America/Los_Angeles, not the browser’s local time.

Use the developer bundle

The setup screen has a copyable developer bundle. It includes:
  • the implementation task and event placement
  • webhook URL and credential
  • consent wording and rules
  • all request fields and minimum requests
  • ready cURL, Node, and Swift examples
  • responses, retry rules, cancellation, and verification
Paste the bundle into a coding assistant or send it to your developer. It contains the merchant-specific URL and credential.