Win back lapsed subscribers with a hosted iMessage AI agent
ZeroSettle lets a subscription app, meal kit or membership start iMessage conversations with its highest-value lapsed customers who opted in to marketing texts, and have the hosted AI agent (Beta) handle their objections and send a come-back checkout link. A nightly job picks the day's customers, and your server calls POST /api/imessage/agent/start for each during their daytime, up to the daily allowance: Pro's 5 new contacts a day is about 150 a month, and Growth allows 50 a day per number. By default the agent sends one follow-up the next morning (9 to 11 AM in its time zone) if they haven't replied.
Key takeaways
- What it is: ZeroSettle is an iMessage API: a real phone number on real iMessage, running on Apple hardware we operate for you, that your code texts customers from and hears back on through a bearer token and signed webhooks. Its hosted AI agent (Beta) runs each win-back conversation, so you need no webhook receiver, only a job that starts the conversations.
- Who it fits: subscription businesses with lapsed customers whose marketing-text consent is on record, and something new to tell them.
- Price: Pro is $49/month for one number with 1,000 AI credits a month and unlimited messages with people who message you first; Growth, for several numbers working as one, is from $599/mo with 5,000. Each text the agent sends uses one credit.
- Main limit: 5 new contacts a day per number on Pro; 50 a day per number on Growth (3 per 15 minutes). On every plan, at most 2 messages to a customer can go unanswered.
- What it does not do: there is no SMS fallback for customers who moved to Android, and the agent cannot apply a discount or restart a subscription. It sends the link you set.
Who uses iMessage for win-back?
iMessage win-back fits a subscription business with lapsed customers who agreed to marketing texts and a real reason to ask them back. Unlike a win-back email, a conversation lets the hosted AI agent ask why the customer left, answer that objection from your business context, and send the checkout link once they are interested.
- Meal kits: the reason someone paused (price, portions, packaging) often has a concrete answer, such as a new box size, menu or liner.
- Subscription apps: Apple's App Store Review Guidelines (3.1.3) say developers "can send communications outside of the app to their user base about purchasing methods other than in-app purchase", so the come-back text can point to a web checkout.
- Memberships and subscription boxes that changed prices or perks since the customer cancelled.
It is for people who left weeks or months ago, not a failed card, a customer in your cancel flow or a user who just left the paywall.
How iMessage for win-back works, step by step
A nightly job chooses who to ask back, one API call starts each conversation, and ZeroSettle's hosted AI agent runs it from there.
- After the 3 AM Eastern reset, the job picks the highest-value lapsed customers with stored marketing-text consent, skipping anyone already asked back, reactivated or opted out anywhere.
- For each one, up to the day's allowance (5 on Pro, 50 on Growth), it calls
POST /api/imessage/agent/startwith their number and first name, during the customer's daytime. - The agent sends your
first_message: 1–3 texts, exactly as written, that say who you are, name what changed and end on a question. No link: a cold first text shows it as plain text (why first-message links aren't tappable). - The agent answers each reply in up to three texts and never invents facts your
business_contextdoesn't give. Withgoalset topurchase, it sends your come-back checkoutlink, tappable now that the customer has replied. - If the customer goes quiet, the default follow-up trigger sends one follow-up a day after the last message, at a moment between 9 and 11 AM in the agent's time zone; the setup below moves it to the afternoon.
- The agent's texts follow the same rules as your own sends: a STOP blocks every number on the account, and on every plan at most two messages to a customer can go unanswered.
Ask what made them take a break, not why they stopped. ZeroSettle treats any text with the word STOP in it, or a clear request like "take me off your list", as an opt-out, so an answer like "had to stop, too pricey" opts them out on the spot, and only their own START opts them back in.
Example conversation: win-back
The opener is the same for every lapsed customer; the agent uses the name from its first answer on.
- Thu 11:00 AM
- FreshFork: Hi, it's FreshFork. We've missed you!
- FreshFork: We just added 15-minute dinners and a 2-person vegetarian box. What made you take a break?
- Customer: honestly all the packaging
- FreshFork: Fair, Leo. We switched to fully recyclable liners in July.
- FreshFork: Want your first box back at 50% off? freshfork.example.com/back
- Customer: ok trying it
Setup: a nightly agent/start job
Setup is a business context, the agent settings and a two-part job. No webhook receiver is needed: on a line group Managed by AI Agent, the agent answers inbound texts instead of your webhook. Field names follow the AI agent section of the ZeroSettle docs.
1. Write the business context
The agent's facts come only from this text (up to 40,000 characters): what changed, the offer and its terms, and what it must never promise.
You are FreshFork's assistant, texting former subscribers who agreed to
marketing texts. Ask what made them take a break (never "why did you
stop"), answer that, offer the come-back box. If they say no, thank
them and stop.
Changed since they left: recyclable liners (July); 15-minute dinners
and a 2-person vegetarian box (August).
Offer: 50% off the first box back, only through the link. Later boxes
renew weekly at the regular price; skip or cancel any week in the account.
Never promise more discounts, refunds or credits for past boxes, an old
price or a delivery date. Account questions: help@freshfork.example.com
2. Turn on the agent
Write the opener (the first two texts of the example above) in the console's Messaging tab, then send the rest with PUT /api/imessage/agent.
jq -n --rawfile ctx business_context.txt '{
managed_by: "zerosettle", business_context: $ctx,
goal: "purchase", link: "https://freshfork.example.com/back",
timezone: "America/New_York",
triggers: [{id: "followup", repeat: "once", after_days: 1, at: "afternoon",
instruction: "If they have not replied, send one short, no-pressure follow-up with no link that reads fine even if they already came back, e.g. that the 15-minute dinners are on the menu all month."}]
}' | curl -s -X PUT https://portal.zerosettle.io/api/imessage/agent \
-H "Authorization: Bearer $ZEROSETTLE_TOKEN" -H "Content-Type: application/json" -d @-
- A link-free follow-up instruction that reads fine either way: a customer who never replied would see a link as plain text, and one who bought without texting back still gets the follow-up, since the agent doesn't see your checkout.
at: "afternoon": the follow-up lands at a random moment between 1 and 3 PM intimezone(Eastern here), which is 10 AM to noon Pacific.triggersis always the whole list, so this replaces the default follow-up.- One
linkfor everyone: have the checkout confirm the buyer is a lapsed customer, or a forwarded link gives anyone 50% off.
3. Plan at night, start in the daytime
The planner queues the day's customers for late morning in each one's time zone; a worker calls agent/start as they come due.
# winback.py: plan() nightly after 3 AM Eastern; start_due() every 5 minutes
import os
import requests
BASE = "https://portal.zerosettle.io"
TOKEN = os.environ["ZEROSETTLE_TOKEN"]
PER_DAY = 5 # Pro; 50 on Growth
def plan() -> None:
for c in lapsed_customers(marketing_text_consent=True, opted_out=False,
asked_back=False, reactivated=False,
limit=PER_DAY): # your DB, highest value first
queue(c, send_at=local_time(c, hour=11))
def start_due() -> None:
for c in due_from_queue():
r = requests.post(f"{BASE}/api/imessage/agent/start",
headers={"Authorization": f"Bearer {TOKEN}"},
json={"to": c.mobile, "name": c.first_name}, timeout=180)
if r.ok:
c.mark_asked_back() # one win-back per customer
continue
err = r.json().get("error") or {}
if err.get("code") == "new_contact_limit":
if err["window"] == "day": # today's allowance is used
clear_queue() # tomorrow's plan() picks them again
else: # "15m": Growth allows 3 per 15 minutes
c.requeue(seconds=err["retry_after_s"])
break # the rest stay due for the next run
elif err.get("code") == "recipient_opted_out":
c.mark_opted_out() # texted STOP: never retry
else:
alert(err.get("message") or r.text) # e.g. 402 ai_credits_exhausted
break
Limits and consent for win-back
Each lapsed customer counts once against ZeroSettle's new-contact limit, when the opener reaches them; the rest of the conversation never counts against it. AI credits are the second budget. The limits help keep the number out of Apple's spam filters.
| Limit | Pro | Growth |
|---|---|---|
| Lapsed customers started per day, per number (3 AM to 3 AM Eastern) | 5 | 50 |
| Started per rolling 15 minutes, per number | — | 3 |
| A month at the daily cap, per number | About 150 | About 1,500 |
| Unanswered messages to one customer (texts within 5 minutes count as one) | 2, then 403 awaiting_reply, the agent included | 2, then 403 awaiting_reply, the agent included |
| AI credits included, per account | 1,000 a month | 5,000 a month |
A failed send never counts as a new contact, and its AI credit comes back.
The limit math for a lapsed list
- Reach: 3,000 opted-in lapsed customers take about 600 days at Pro's 5 a day, so a list that size belongs on Growth: 60 days at 50 a day on one number. At 3 per 15 minutes, a Growth number needs at least four hours to start its 50, which the worker's retries spread from 11 AM into the afternoon.
- Credits: a two-text opener uses 2, the follow-up 1, each answer up to 3. Pro's five starts a day are 10 to 15 credits before any answers, or 300 to 450 a month, which leaves room in its 1,000 for answers. Growth's 50 a day on one number is 100 to 150 credits a day, or 3,000 to 4,500 a month before answers: close to its 5,000, so budget a pack or start fewer. With fewer credits left than the opener needs,
agent/startsends nothing and returns402 ai_credits_exhausted; at zero the agent also goes quiet in open threads.
Consent, opt-outs and the come-back checkout
Text only customers with stored consent to marketing texts, between 8 am and 9 pm in their time zone, the window the FCC also sets for telephone solicitations. If consent is old or unclear, recheck it by email or in your app: "If you are unsure whether you have consent, you don't." The iMessage TCPA consent guide has more. This section is general information, not legal advice; have counsel review your consent records and checkout.
- A delivery number is not marketing consent. ZeroSettle's docs require a number given "for this purpose", and written consent for marketing texts under the TCPA.
- Other channels' opt-outs are yours to exclude. ZeroSettle knows only the STOPs texted to your numbers, and the FCC lets consumers revoke consent "by using any reasonable method".
- The checkout is a new sign-up. For an offer that renews automatically, the Restore Online Shoppers' Confidence Act requires the checkout to disclose all material terms, get express informed consent before charging, and offer a simple way to stop. This is not legal advice.
When ZeroSettle is not a fit for win-back
ZeroSettle is the wrong tool for win-back in these cases:
- Consent is old or unclear. Recheck it first; ZeroSettle suspends lines used for unsolicited messaging.
- You want to reach thousands of lapsed users a week from one number. One Pro number starts 35 a week, and one Growth number 350; the docs call the limits "a floor, not a licence".
- Users on Android. There is no SMS fallback, so keep email for them. You can check whether a number is on iMessage before texting it, and the check sends nothing.
- You need the agent to apply account credits itself. It can't touch your billing or accounts: it works from your business context and sends the link you set, and one link and context serve the whole line group.
- Your one number already answers support from your server. On a group Managed by AI Agent, inbound texts skip your webhook and your sends get
403 managed_by_zerosettle. The docs add: "Do not send automated marketing into a support thread."
How much does iMessage for win-back cost?
Win-back on ZeroSettle costs the plan price plus any AI credits beyond the monthly allowance.
| Option | Price | AI credits |
|---|---|---|
| Pro | $49/month, one number to start, 5 new contacts a day, unlimited messages with people who message you first, no setup fee | 1,000 a month |
| Growth | From $599/mo, several numbers working as one | 5,000 a month |
| AI credit pack | 1,000 for $50 or 5,000 for $200, paid once | Never expire |
Monthly credits don't carry over. Both plans have a 30-day money-back guarantee on the subscription. Get a Pro number, book a Growth demo, see ZeroSettle pricing, or browse the iMessage use-case hub.
Frequently asked questions
Does the agent know the customer's history, or that they bought?
No. It works from your business context, its settings, the conversation, the current time and the first name you passed. It learns why they left by asking and doesn't see your checkout, so the nightly job must skip anyone who already reactivated.
Can the agent send more than one follow-up?
Not to someone who hasn't answered. The opener and one follow-up already make two unanswered messages, so on every plan a third is refused until they reply. You can add up to 5 triggers, but each follows that rule. The docs call repeated messages to people who never answer "the fastest way to get a number flagged by Apple".
How do I copy STOPs into my CRM?
Read GET /api/imessage/opt-outs in the nightly job; it lists every handle that texted STOP, with opted_out_at. On an agent-managed line your webhook isn't called, so this list, or the live stream's optout events, is how your systems hear about a STOP.
Sources
- ZeroSettle iMessage API docs: "AI agent", "Links in a first message", "Customer support" and "Errors, limits, and compliance". Checked October 8, 2026.
- ZeroSettle iMessage API Terms of Service, section 13 (opt-in and prohibited uses). Last modified September 28, 2026.
- 47 CFR 64.1200, Delivery restrictions, the FCC's TCPA rules: written consent (a)(2), revocation (a)(10), calling hours (c)(1) and texts to wireless numbers (e) (Legal Information Institute). Checked October 8, 2026.
- 15 U.S.C. 8403, Negative option marketing on the Internet, the Restore Online Shoppers' Confidence Act (Legal Information Institute). Checked October 8, 2026.
- App Store Review Guidelines, guideline 3.1.3 (Other Purchase Methods), Apple Developer. Checked October 8, 2026.