Skip to main content
A server is private software that handles app requests. A paywall event is a request from that server after a person leaves your paywall. It gives Hyzl the facts that it needs for recovery. Start with Send the paywall event for a short setup guide. You can also use an AI code assistant.

Address

Protect the ingest token

The ingest token is the secret that permits your server to send paywall events. On the Install page, this appears as Server key.
  1. Select Copy setup guide on the Install page.
  2. Store the guide’s key from your clipboard.
  3. Store it as HYZL_INGEST_TOKEN in your server environment.
  4. Use your platform’s secret command to read the clipboard.
On macOS, use $(pbpaste). On Windows, use Get-Clipboard -Raw. On Linux, use xclip -selection clipboard -o. Do not put this key in a chat, code file, test, log, or screenshot. Anyone with the key can send events for your app and use your Hyzl number. Copying the guide again returns the same key. It does not rotate it. If the key becomes visible, rotate it explicitly. A rotated key replaces the old key at once.

Choose the right moment

Send the event after a signed-in person sees your price and leaves without payment. This moment starts recovery. If you collect the phone number later, send another event with the same userId. The event with the phone number and consent starts the wait.

Fields

Hyzl ignores fields that do not appear in this table.

Match each person to a payment

For RevenueCat, userId must equal the value that your app gives to Purchases.logIn(). The wrong value can make a person appear unpaid after a purchase. For Stripe, send stripeCustomerId when you know it. Also set client_reference_id or metadata.userId on the Checkout Session or Payment Link. Use the same userId in the paywall event. Hyzl checks each available Stripe identifier before contact. If a Stripe check fails, Hyzl waits instead of treating the person as unpaid. If people pay on your website through Stripe and in your iPhone app through RevenueCat, connect both. Send one userId for each person, the same value that your iPhone app passes to Purchases.logIn(). Set that same value as client_reference_id or metadata.userId on your Stripe Checkout. Hyzl then asks Stripe and RevenueCat before every step, and a payment that either one finds ends recovery. Someone who signed up in your iPhone app may have no Stripe customer. Hyzl contacts that person only when RevenueCat answers that they have not paid and Stripe finds no payment. Without a stripeCustomerId, Hyzl finds a web payment in only three ways. The first is a Checkout that carries the userId and started no earlier than a day before this event. The second is a subscription whose metadata.userId is the userId, or whose Stripe customer has that metadata.userId. Stripe can take about a minute to make a new one findable. The third is a subscription whose Stripe customer has this event’s email, written the same way or in all lowercase letters. So a one-time web payment is found only through the first way. Hyzl misses any other web payment by that person and can contact them. That includes an older Checkout that carried the userId only on the Checkout. For subscriptions, send stripeCustomerId whenever you know it, and set subscription_data.metadata.userId on the Checkout so that the subscription carries the userId. Hyzl also asks both before it texts your link to someone who writes back or calls. That applies only to people Hyzl has not already found paid. Someone Hyzl already found paid gets the link if they ask for it, without a new check, as before. If either check fails, Hyzl waits and does not contact the person. A RevenueCat failure is retried, even when RevenueCat refuses the connection. After eight failed checks in a row, about 25 hours after the first, recovery ends for that person. When Stripe refuses the connection (a 401 or 403 error), recovery ends sooner, about five minutes after the first failed check. Reconnect as soon as a card shows Connection broken. See Read the connection status. Without a userId, RevenueCat cannot find the person, so Hyzl never contacts them. This holds even when the event has a stripeCustomerId. Once you connect RevenueCat, send the Purchases.logIn() value as userId for every person.

Mark the payments Hyzl recovered

Hyzl can see that a person paid after it reached them. It cannot see why they paid. If you also run your own win-back emails, texts or checkout, those payments look identical. So Hyzl counts a payment as recovered revenue only when the Stripe object your checkout creates from recoveryUrl is marked as coming from Hyzl. Set either of these on the Checkout Session, Subscription or Invoice: Stripe copies subscription_data.metadata onto the Subscription rather than onto the invoices it raises. Hyzl follows that link for you, so marking the subscription is enough. If you send offers, mark the Stripe object behind each plan’s link: the lead and every fallback. client_reference_id is not attribution. It is how Hyzl finds the payment at all; keep sending it. Payments without a mark are not lost. People who pay after Hyzl reaches them are reported on Home and in Analytics as a separate count, and never as recovered revenue. RevenueCat purchases are always in that count: an App Store or Play Store transaction has no metadata for you to mark. Use offerLinks if your checkout has a different secure link for each allowed discount. Each key is a whole discount percent from 0 through 90. The 0 key means the regular price.
Hyzl uses only the links that you send for that person. Each link must follow the same rules as recoveryUrl.

Give Hyzl named offers

Use offers when a person must see your own plans, at your own prices. For example, a price test shows each person the prices of one test group. Send that person the plans of their group, and Hyzl offers only those. It never offers a price that is not in their plans, and never a percent off. Each plan has four parts: Send exactly one lead, and up to three fallbacks. Hyzl leads with the lead plan in its texts, replies, calls and voicemails. It offers a fallback only when the person wants to try it first, needs it for one trip, asks for a monthly plan, or will not commit to a year. Each text names the plan whose link it sends. After a call, Hyzl texts the link of the plan that the call was about, or the lead.
For this person, offers replaces offerLinks, percentOff and the offer in your app settings. Hyzl also sends these links instead of recoveryUrl, whatever your link mode is. A later event with offers replaces the plans. An event without offers keeps them. When the plans change, Hyzl quotes the new price as the current one, and never says that the earlier price was wrong. Hyzl uses offers in Auto mode texts, in replies and in calls. Flow texts keep using recoveryUrl. Mark the Stripe object behind each link, as Mark the payments Hyzl recovered describes.

Send the search the person ran

Send search when the person ran a real search in your app before the paywall. The AI rep can then tell them what your app found, with your numbers exactly. Leave search out when no search finished. Send economy results only. For a premium cabin, send only how many dates had seats and the lowest points. The rep can then say that those seats exist without giving them away. A search that found nothing is still a result. Send it with an empty economy list. The rep then tells the person plainly that your app found no economy seats for those dates, and asks whether other dates or a smaller party would work.
program and via use letters, spaces and the characters & ' - / only: no digits, no dots and never a link. These words can appear in the first text, so a number or an address could reach the person. Apart from the cabins, if any part breaks these rules, Hyzl leaves out the whole search and still accepts the event. The response tells you what happened. A 202 response includes "search": "stored" or "search": "dropped" when your event carried a search. dropped means that the search broke a rule above, or that it is out of date: it ran before a search Hyzl already holds or, on an event without detail, before your previous event for that person arrived. Send detail with every search, so Hyzl knows which trip the search belongs to. A later event with search replaces the stored search. An event for a new trip, without a search, clears the stored search. Hyzl reads the trip from detail, up to its first comma, so "Tampa → London, business" and "Tampa → London, economy" are the same trip. That way, the rep never quotes the old trip’s numbers. You can send the search on a later event for the same person. Without occurredAt or startTimerAt, that event does not restart the timer or change when Hyzl next contacts them. Hyzl may ask you to run the search. When a person arrives without a search and you have given us an endpoint for it, Hyzl’s rep can ask your server to run that person’s search once, with their own cards and at no cost to them. Your server answers at once, runs the search, and sends the result as a paywall event with search and eventId set to <userId>:search:<request_id>, where request_id is the id Hyzl sent. Send it even when the search found nothing, with an empty economy list, or the rep cannot tell the person anything. Hyzl keeps the person’s schedule and delivers the result in the next text. Ask us to set it up. Consent is a person’s recorded permission for contact by text or call. Send the real state of your current consent choice. If you do not ask for consent, add an unchecked box beside your optional phone field. Edit this example for your business and legal requirements. Example wording:
I agree that {Your company} and its service providers may contact me by phone call and text message at the number provided, including with automated or AI-assisted technology, about my account and related offers. An AI may answer calls, and calls may be recorded and transcribed. Consent is not a condition of purchase. Message frequency varies. Message and data rates may apply. Reply STOP to opt out. Reply HELP for help. {Terms link} · {Privacy link}
Replace the placeholders with links to your Terms and Privacy Policy. Send the real consent state as consent.attested, either true or false. Never set it to true for every person. Keep the box clear by default. Keep the phone field optional. Hyzl stores a person without consent, but Hyzl does not contact that person.

Examples

curl

Node

Swift

Send in two steps

Two events arriving together for the same userId or stripeCustomerId use one person. Each distinct event is saved. A later event can add missing details or revoke consent. A repeated eventId does not reprocess event details, even when requests arrive together with different details. An explicit optedOut: true still records STOP and withdraws contact before the duplicate response. That response names the person from the first accepted event.

Responses

Treat 200 and 202 as success. A 202 response also includes these fields:
  • person_id: the Hyzl record for the person.
  • status: the person’s current recovery state.
  • contactable: whether Hyzl has a phone number.
  • consent_attested: the consent state that Hyzl stored.
  • next_action_at: the next planned action time, or null.
  • search: stored or dropped, only when the event carried a search. See Send the search the person ran.
A 202 response means that Hyzl stored the event. It does not mean that Hyzl sent a text.

Send from your server

Do not send this event from a browser or mobile app. A public app can expose the ingest token to other people. The future sdk-event endpoint returns 501 sdk_not_available and is not deployed. Send all paywall events to paywall-event from your server.

What happens after the event

  1. Hyzl stores the event.
  2. Hyzl waits for the first step in your Flow.
  3. Hyzl checks the payment provider before each step.
  4. Hyzl stops recovery if the person paid or sent STOP.
  5. Hyzl sends the step if the person remains eligible.
The default Flow waits five minutes, then sends one text. Its later text steps wait one day and three days. Quiet hours use the time zone for your app. The default quiet hours are 9 p.m. to 9 a.m. in that app time zone. Hyzl also holds scheduled texts outside 9 a.m. to 8 p.m. in the person’s own time zone. Hyzl reads that time zone from the phone area code, and uses your app time zone when it cannot. Hyzl holds scheduled texts until both allow them. Call steps use their own calling window. Outbound calls are off by default and only call United States and Canada numbers. A reply pauses the Flow for 24 hours while Hyzl talks with the person. Hyzl resumes the Flow only after the conversation stops and the person remains unpaid.