Use cases

Restaurant waitlist by iMessage: text guests when the table is ready

ZeroSettle lets a walk-in restaurant run its waitlist over iMessage. Guests scan a QR code at the host stand that opens Messages with “JOIN party of _, name _” prefilled, the host answers from the ZeroSettle console on an iPad, and “your table is ready” goes out from the restaurant's own number. Because every guest texts first, a Saturday rush of 200 parties never touches ZeroSettle's new-contact limit. The Pro plan, with one number, costs $49 a month.

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. Hosts can also answer from the web console, with no code.
  • The workflow: the guest texts JOIN from a QR code, the host replies with a place in line, then texts when the table is ready.
  • Main limit: the new-contact cap doesn't apply to guests who text first. A line sends one message at a time, normally 2–5 seconds each.
  • Price: Pro is $49/month for one number; Growth is from $599/mo for several numbers working as one. No per-message fees.
  • What it does not do: there is no SMS fallback for Android guests, and ZeroSettle does not keep the list or quote waits.

Who uses iMessage for restaurant waitlists?

An iMessage waitlist fits walk-in restaurants, brunch spots, breweries and food halls that quote waits at a host stand and don't use a reservation app's waitlist. Pagers walk out in pockets, paper lists get illegible, and a “table ready” text from an unfamiliar number is easy to miss at the bar next door. With ZeroSettle the guest starts the thread, so the ready text comes from a number they just texted.

Restaurants that take bookings have different jobs: filling last-minute cancellations from a waitlist and iMessage appointment reminders. More are in the index of iMessage use cases.

How iMessage for restaurant waitlists works, step by step

The guest starts the thread, the host works the list from the ZeroSettle console, and the ready text goes out from the restaurant's number. Code is optional.

  1. The guest scans the QR code at the host stand. It holds an sms: link to your number with the JOIN text prefilled. The iPhone Camera app reads QR codes and shows a link to tap, which opens Messages with the text ready; the guest fills the blanks and taps Send. There is no app to download (see how to get customers to text you first).
  2. The JOIN text appears in the console within seconds, and reaches a webhook as message.received in about 1–2 seconds. The guest texted first, so it is never a new contact.
  3. The host replies from the iPad: “You're #4, about 25 minutes.” A menu link in that reply is tappable, because the guest wrote first.
  4. The host texts “Your table is ready”. The guest's “on our way” or 👍 tapback shows up within seconds (on a webhook, as message.received or reaction.received).
  5. Optionally, a developer parses party size into your own list, sends the ready text with POST /api/imessage/send, and drives a host-stand screen from the live stream (SSE).

Example conversation: a party of four on a Saturday

One party at a fictional restaurant, Lark Kitchen, seen from the restaurant's side of the thread.

  1. Sat 7:12 PM
  2. Guest: JOIN party of 4, name Rivera
  3. Restaurant: Lark Kitchen here: you're on the list, Rivera party of 4. You're #4, about 25 min. We'll text you here when your table's ready 🍸
  4. Sat 7:37 PM
  5. Restaurant: Your table for 4 is ready! Head to the host stand within 10 minutes.👍
  6. Guest: walking back now, 3 min
Illustrative example with a fictional restaurant. Blue bubbles are sent from the restaurant's number by the host; gray bubbles and the 👍 tapback are the guest's.

Setup: a QR code and the console on an iPad

A restaurant needs a QR code that opens Messages with the JOIN text, and the ZeroSettle console open in a browser on the host-stand iPad. Code is only for keeping the list in your own system.

The QR code

Turn this link, with your number in it, into a QR code with any generator. It follows the sms: format in the customer support section of the ZeroSettle docs:

link
sms:+15555550100&body=JOIN%20party%20of%20_%2C%20name%20_

Test it with an iPhone before printing, and add “No iPhone? Give your name to the host.” underneath. On Android the link opens the SMS app instead, and Apple's own page explains that texts to non-Apple phones go as SMS, MMS or RCS rather than iMessage, so ZeroSettle can't text those guests back.

Optional: parse JOIN texts into your own list

This receiver verifies the signature as in the docs' webhook steps, skips retries, adds each JOIN to a list and confirms it; anything else is left for the host. Sends go out one at a time, the way the line handles them. See how iMessage webhooks work.

python
# waitlist.py: run with  uvicorn waitlist:app --port 8100
import hashlib, hmac, json, os, re, threading, time
import httpx
from fastapi import BackgroundTasks, FastAPI, Request, Response

BASE = "https://portal.zerosettle.io"
TOKEN = os.environ["ZEROSETTLE_TOKEN"]
SECRET = os.environ["ZEROSETTLE_WEBHOOK_SECRET"]
JOIN = re.compile(r"\bjoin\b.*?party of\s*(\d+).*?name[:\s]+([^\d,.!?\n]+)", re.I | re.S)
app = FastAPI()
waitlist: list[dict] = []                # use your database in production
seen: set[tuple[int, str]] = set()
send_lock = threading.Lock()             # one send at a time, like the line

def verify(header: str, raw: bytes) -> bool:
    try:
        parts = dict(p.split("=", 1) for p in header.split(","))
        t, v1 = int(parts["t"]), parts["v1"]
    except Exception:
        return False
    if abs(time.time() - t) > 300:
        return False
    expected = hmac.new(SECRET.encode(), f"{t}.".encode() + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, v1)

def send(to: str, text: str) -> httpx.Response:
    with send_lock:
        for _ in range(3):
            r = httpx.post(f"{BASE}/api/imessage/send", headers={"Authorization": f"Bearer {TOKEN}"},
                           json={"to": to, "text": text}, timeout=90)
            if r.status_code != 429 or r.json()["error"]["code"] != "line_busy":
                return r
            time.sleep(2)                # the console was sending too: retry after 2 s
        return r

def add_party(m: dict) -> None:
    hit = JOIN.search(m.get("text") or "")
    if not hit:
        return                           # not a JOIN: the host answers in the console
    size, name = int(hit.group(1)), hit.group(2).strip()
    waitlist.append({"handle": m["handle"], "size": size, "name": name})
    send(m["handle"], f"Lark Kitchen here: you're on the list, {name} party of {size}. "
                      "We'll text you here when your table's ready.")

@app.post("/webhook")
async def webhook(request: Request, tasks: BackgroundTasks):
    raw = await request.body()
    if not verify(request.headers.get("X-ZeroSettle-Signature", ""), raw):
        return Response(status_code=401)
    payload = json.loads(raw)
    m = payload.get("message")
    if payload["event"] != "message.received" or (m["id"], payload["event"]) in seen:
        return Response(status_code=200) # our own sends echo back as message.sent
    seen.add((m["id"], payload["event"]))
    tasks.add_task(add_party, m)         # answer 2xx now; send after
    return Response(status_code=200)

The ready text is the same send() call, from a button on your host-stand screen that also takes the party off the list. The host adds the place in line and the wait from the console.

Optional: a host-stand screen from the live stream

ZeroSettle's live stream lets a screen at the stand show every JOIN, reply and tapback as it happens. Your server calls GET /api/imessage/stream-token and hands the browser the stream_url, which is relative, so prefix it with https://portal.zerosettle.io. The token lives 5 minutes, so mint a fresh one on each reconnect. See the docs' live stream section.

Limits and consent for restaurant waitlists

A restaurant waitlist stays clear of ZeroSettle's new-contact limit as long as guests text the line first. What matters at a host stand on a busy night is the send queue: a line sends one message at a time.

ZeroSettle limits applied to a restaurant waitlist (API docs, checked October 12, 2026)
LimitAt the host stand
New contacts (Pro 5 a day, Growth 50, per number)Doesn't apply to guests who texted the line first. A guest the host texts first, by typing in their number, counts as one.
SendsOne at a time, normally 2–5 s each; after about 12 s of queueing, 429 line_busy, retry after 2 s.
Inbound JOINs, replies, tapbacksUnlimited; about 1–2 s to your webhook.
Unanswered messages2 on either plan until the guest replies (a tapback counts, and texts sent within 5 minutes of the first count as one), then 403 awaiting_reply.

The limit math for 200 parties

Picture 200 parties between 6 and 10 PM, each getting about three texts (the confirmation, the ready text and one answer to a question): 600 sends. At 2–5 seconds each, that is 20 to 50 minutes of sending spread over four hours, which one host typing can't outpace.

  • When six tables turn at once, six ready texts take about 12 to 30 seconds to go out, one after another. Code should send them in sequence, as the receiver above does: a send left waiting about 12 seconds behind others returns 429 line_busy.
  • A guest who reads out a number instead of scanning is a new contact when the host texts them first: Pro allows 5 a day per number, and a menu link in that first text isn't tappable until they reply.
  • A “last call” is refused with 403 awaiting_reply when the guest answered neither the confirmation nor the ready text, because those were two separate unanswered messages. A 👍 on either one resets the count. This applies on Pro and Growth alike.
  • Android guests get nothing back, so they stay on the paper list.

A guest's JOIN text is a reasonable basis for waitlist texts for that visit. Adding them to promotions or event announcements likely needs a separate yes; ZeroSettle's docs note that US marketing texts need written consent (see 47 CFR 64.1200). The docs' quiet hours (nothing promotional before 8 am or after 9 pm in the guest's time zone) are about promotions, not a ready text at 9:45 PM. This isn't legal advice; check your own program with counsel.

A text with the word STOP in it, or a clear request like “take me off your list”, opts the guest out of every number on your account, even “we'll stop at the bar first”. The console then disables that conversation and shows when they texted STOP, and the ready text is refused with 403 recipient_opted_out, so call the name at the stand. Only a text that is just START opts them back in.

When ZeroSettle is not a fit for restaurant waitlists

ZeroSettle is the wrong tool for a restaurant waitlist in these cases:

  • Guests already use your reservation platform's waitlist. A second list splits the queue.
  • Many guests use Android. With no SMS fallback, the paper list still does most of the work.
  • You want to text promotions to everyone who ever waited. They agreed to waitlist texts only, and ZeroSettle's terms require opt-in.
  • Many guests arrive without a phone on them. A text can't reach them; a pager or a called name can.
  • You want ZeroSettle to keep the list and quote waits. The console is an inbox, not a waitlist manager.

How much does iMessage for restaurant waitlists cost?

One restaurant's waitlist runs on Pro: $49 a month for one number, with no setup fee and no per-message fees. Growth, from $599/mo, is for several numbers working as one.

ZeroSettle plans (zerosettle.io, checked October 12, 2026)
PlanPriceIncludes
Pro$49/monthOne number to start, self-serve, 5 new contacts a day, unlimited messages with people who message you first, 30-day money-back guarantee
GrowthFrom $599/mo3+ numbers working as one, 50 new contacts a day per number, dedicated support, set up after a demo

Texts in both directions are unlimited. AI credits are spent only by the hosted AI agent, so a host answering from the console uses none. See ZeroSettle pricing, read the docs, or get a number.

Frequently asked questions

Can several hosts answer at once?

Yes. Invite teammates to the console; they work on the same line and the same conversations. Two sends at the same moment go out one after the other.

Will guests see the restaurant's name?

Yes, once you set a brand name and photo. ZeroSettle shares them automatically with the first reply each guest gets from your number, and the guest can save them to their contacts. The exact display depends on the guest's iPhone and contact settings.

Can we delete a guest's thread after the visit?

Yes. ZeroSettle keeps messages indefinitely for now, but deleting the conversation in the console (or with DELETE /api/imessage/conversations/{handle}) removes every stored message with that guest. It doesn't unsend anything they already received.

Can the hosted AI agent run the waitlist?

Not well. The hosted AI agent (Beta) answers from the business context you write, not the live list, and on a number it manages the console composer is refused, so the host couldn't send the ready text. It suits question lines, like an iMessage help line for event attendees.

Sources

  1. ZeroSettle iMessage API docs: “Send a message”, “Read messages”, “Customer support”, “Webhooks, step by step”, “Live stream (SSE)” and “Errors, limits, and compliance”. Checked October 8, 2026.
  2. ZeroSettle iMessage API Terms of Service, section 13, Compliance and Client Responsibility (opt-in). Last modified September 28, 2026.
  3. Scan a QR code with your iPhone or iPad, Apple Support. Checked October 8, 2026.
  4. What is the difference between iMessage, RCS, and SMS/MMS?, Apple Support. Checked October 8, 2026.
  5. 47 CFR 64.1200, Delivery restrictions, the FCC's TCPA rules, via Cornell Legal Information Institute. Checked October 8, 2026.

Written by the ZeroSettle team. 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.

Product facts on this page come from the ZeroSettle iMessage API docs, the zerosettle.io homepage and the iMessage API Terms as of October 12, 2026. Outside facts come from the pages listed under Sources, checked October 8, 2026. Lark Kitchen and the Rivera party are fictional, and nothing here is legal advice.

Text your customers from your own iMessage number

ZeroSettle gives your business its own phone number on real iMessage, running on Apple hardware we operate for you in a server facility. Your code sends with one API call and hears every reply on a signed webhook.

Pro is $49 a month with a 30-day money-back guarantee. Growth, for several numbers working as one, is from $599/mo.