Use cases

Auto repair approvals over iMessage: photo of the part, then YES

ZeroSettle lets an auto shop text a customer a photo of the worn part with the price in one iMessage bubble, from the shop's own number, and log the customer's YES from a webhook that fires about 1–2 seconds after the reply lands. The thread keeps a timestamped record of what was shown and what was approved. A short “your car is on the lift” text at drop-off starts the conversation, and the customer's reply to it lets photos through, so the mid-job approval and the pickup link are not cold messages.

Key takeaways

  • What it does: sends the photo of the part and the price as one blue bubble from your shop's number, and delivers the customer's YES to your webhook about 1–2 seconds after it reaches the line.
  • Who it fits: independent shops, dealer service departments, tire and brake shops and mobile mechanics whose customers mostly use iPhone.
  • Price: Pro is $49/month for one number. Growth, for several numbers working as one, is from $599/mo.
  • Main limit: on Pro, 5 new contacts a day per number (50 on Growth). The approval and pickup texts that follow a drop-off text are not new contacts, but a photo goes only to a customer who has replied at least once.
  • What it does not do: no video, no SMS fallback for Android, and no ruling on whether a text YES meets your state's authorization rules.

Who uses iMessage for repair approvals?

iMessage approvals fit independent repair shops, dealer service departments, tire and brake shops and mobile mechanics, where a tech finds extra work mid-job and the car sits on the lift waiting for a yes. Customers at work often don't pick up, and “you need brakes” is easier to believe with the part in front of them. A photo of the worn pad with the price handles both.

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.

How repair approvals over iMessage work, step by step

A ZeroSettle repair approval runs from a drop-off text to a pay link, with the customer's YES reaching your webhook in between.

  1. Drop-off. Record text consent on the repair order or check-in kiosk and send a short “your car is on the lift” text that asks for a reply, or have the customer text first from a counter QR code. Photos go only to customers who have replied at least once (403 attachment_before_reply otherwise). For a customer new to the number, the drop-off text is the visit's one new contact.
  2. The finding. The advisor sends a photo of the worn pad from the ZeroSettle console, or your shop system calls POST /api/imessage/send with image_b64 and the price as the caption, which arrives in the same bubble. A JPEG up to 2048 px on the long edge goes out exactly as sent; a HEIC from a shop iPad, or a bigger photo, is re-encoded to JPEG without EXIF or GPS. The cap is 5 MB.
  3. The answer. The YES reaches your webhook as a signed message.received event about 1–2 seconds after it lands on the line, and your handler marks the line item approved.
  4. The record. GET /api/imessage/messages?handle=… returns both messages with their timestamps: the photo's caption and image link, then the reply.
  5. Pickup. The customer has replied, so the pay link in the “ready” text is tappable; see why links in a first iMessage aren't tappable.
  6. Next visit. A returning customer continues the same conversation, which uses no new-contact allowance.

Example conversation: a brake approval and a pay link

One visit, as the shop sees it in the ZeroSettle console:

  1. Tue 8:04 AM
  2. Business: Hi Mike, Eastside Auto here. Your Civic is on the lift now. Reply OK and we'll text you photos of anything we find.
  3. Customer: OK
  4. Tue 10:41 AM
  5. Business: [Photo: front brake pad worn to 2 mm]
    Front pads are down to 2mm and the rotors are scored. Pads and rotors are $340, so $412.17 total with the oil service. Reply YES to go ahead or NO to skip.
  6. Customer: YES👍
  7. Tue 3:52 PM
  8. Business: All done, total $412.17 including the oil service. Pay and skip the counter: pay.example.com/eastside/88213
  9. Customer: paid, see you at 5
Illustrative example with a fictional shop. Blue bubbles come from the shop's number through ZeroSettle; gray bubbles are the customer's. Mike's OK lets the photo through, the 👍 on his YES is the shop's tapback, and the pay link works because Mike has replied.

Asking for YES or NO gives the handler two words to match and keeps customers away from “stop”, which opts them out.

Setup: photo approvals from the console or your shop system

An advisor can run the whole loop from the ZeroSettle console with no code. A shop system needs a photo send and a signed-webhook receiver that records YES or NO when exactly one line item is open, following the ZeroSettle iMessage API docs.

python
# approvals.py: run with  uvicorn approvals:app --port 8100
import base64, hashlib, hmac, json, os, time
import requests
from fastapi import BackgroundTasks, FastAPI, Request, Response

BASE = os.environ.get("ZEROSETTLE_BASE_URL", "https://portal.zerosettle.io")
AUTH = {"Authorization": f"Bearer {os.environ['ZEROSETTLE_TOKEN']}"}
SECRET = os.environ["ZEROSETTLE_WEBHOOK_SECRET"]
app, seen = FastAPI(), set()                       # seen: use your database in production

def send_approval(to, jpg_path, caption):          # photo + price caption, one bubble; 403 attachment_before_reply until they have replied once
    with open(jpg_path, "rb") as f:
        image = base64.b64encode(f.read()).decode()
    return requests.post(f"{BASE}/api/imessage/send", headers=AUTH, timeout=120, json={
        "to": to, "image_b64": image, "image_format": "jpg", "text": caption}).json()

def verify(header, raw, tolerance=300):
    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) > tolerance:
        return False
    expected = hmac.new(SECRET.encode(), f"{t}.".encode() + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, v1)

def handle(msg):
    items = pending_line_items(msg["handle"])      # your shop system: items awaiting a yes
    answer = (msg.get("text") or "").strip().strip(".!").upper()
    if len(items) != 1 or msg.get("kind", "message") != "message" or answer not in ("YES", "NO"):
        return notify_advisor(msg)                 # your shop system: questions, photos, STOP
    items[0].record_decision(answer, message_id=msg["id"], ts_ms=msg["ts_ms"])
    if answer == "YES":                            # "like" tapback on their YES
        requests.post(f"{BASE}/api/imessage/reactions", headers=AUTH, timeout=90,
                      json={"message_id": msg["id"], "reaction": "like"})

@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)
    msg = payload.get("message")
    if payload["event"] != "message.received" or msg is None:
        return Response(status_code=200)           # own sends echo as message.sent
    if (msg["id"], payload["event"]) in seen:
        return Response(status_code=200)           # retries repeat the id and event
    seen.add((msg["id"], payload["event"]))
    tasks.add_task(handle, msg)                    # answer 2xx within 10 s, work afterwards
    return Response(status_code=200)

Register the receiver with PUT /api/imessage/webhook and keep the secret it returns (shown once); the webhook steps cover testing. A photo send normally returns in a few seconds but can take up to about 105, so run send_approval from a job queue.

Keep the approval with the repair order

Copy the two stored messages, and the photo file you uploaded, into the repair order:

bash
BASE=https://portal.zerosettle.io            # ZEROSETTLE_TOKEN is exported in your shell
curl -s -H "Authorization: Bearer $ZEROSETTLE_TOKEN" \
  "$BASE/api/imessage/messages?handle=%2B15555550123&after_id=0&limit=500"
curl -s -H "Authorization: Bearer $ZEROSETTLE_TOKEN" \
  "$BASE/api/imessage/messages/45/image" -o ro-88213-front-pads.jpg

California's Bureau of Automotive Repair lists text among the ways to reach a customer about added work, and its Write It Right guide says what to record:

What California's Write It Right guide asks a shop to record when added work is authorized by text, and where each item comes from (checked October 8, 2026)
Item to recordWhere it comes from
Date and time of the authorizationts_ms on the customer's YES
Name of the person authorizingYour repair order; the thread shows a number, not a name
Phone number (or email) contactedhandle on every message in the thread
Added parts and labor, their cost and the revised totalYour revised work order; BAR says to give the customer all of it, so the example caption has the parts, price and new total
Texts that supplement the estimate, “uniquely identified and maintained as part of the same transaction”The photo message and the reply, filed with their message ids

BAR also asks you to document the authorization on both the work order and the invoice. It calls the guide “not all-inclusive”, and other states set their own rules; this is not legal advice.

Limits and consent for repair approvals

A repair visit uses ZeroSettle's new-contact allowance once: the drop-off text to a customer the number has never messaged. Approval and pickup texts never count as new contacts, though on every plan they must stay within two unanswered messages, and the photo needs the customer to have replied once.

ZeroSettle limits that apply to repair approvals, per number (API docs, checked October 12, 2026)
LimitProGrowth
New contacts per day (3 AM to 3 AM Eastern)550
New contacts per rolling 15 minutes—3
Unanswered messages to one contact2, then 403 awaiting_reply2, then 403 awaiting_reply
Photos and voice messagesOnly to contacts who have repliedOnly to contacts who have replied
Messages sent and receivedUnlimitedUnlimited

Source: ZeroSettle iMessage API docs, “Errors, limits, and compliance”. Anyone who texted the line first never counts as a new contact.

The limit math for a Monday with 25 drop-offs

  • On Pro, 5 drop-off texts to customers new to the number can go out each day. The 6th returns 429 new_contact_limit with retry_after_s.
  • If all 25 are new to the number, Pro refuses texts 6 to 25 until 3 AM Eastern (midnight Pacific), long after those cars are gone. Skipping the drop-off text doesn't help: a photo can't be the first message.
  • The fix: a QR code at the counter that opens Messages to the shop's number, since customers who text first never count as new contacts; see how to get customers to text you first.
  • On Growth, each number allows 50 a day but only 3 per 15 minutes, so queue the 7:30 AM rush.
  • On any plan, after Mike's OK the 10:41 photo is one unanswered message (texts sent within 5 minutes of each other count as one), so a second separate text is fine but a third before he replies returns 403 awaiting_reply. Call instead.

Collect text consent on the repair order, tied to this job. A coupon for the next brake job is marketing, a separate consent: ZeroSettle's docs note that marketing texts need written consent under the TCPA (the FCC's rule, 47 CFR 64.1200) and say to send nothing promotional before 8 am or after 9 pm in the customer's time zone. Whether a given text counts as marketing depends on its content and your situation; this isn't legal advice.

“Stop, just do the oil change” is an opt-out

A text with the word STOP in it, or a clear request like “take me off your list”, opts the customer out of every number on your account. The handler above passes that text to the advisor, and the pickup text now returns 403 recipient_opted_out, so call. Texting START opts them back in; see how STOP and opt-outs work.

When ZeroSettle is not a fit for repair approvals

ZeroSettle is the wrong tool for repair approvals in these cases:

  • Your state requires a signature for added work. BAR's guide lists text as a valid way to authorize added work in California; where a signature is required, a text YES may not be enough on its own; check your state's rules.
  • Many customers use Android. iMessage only reaches people on an iPhone or another Apple product (Apple Support), and ZeroSettle has no SMS fallback. ZeroSettle checks every first message with Apple, and a number that isn't on iMessage gets nothing (422 not_on_imessage) and uses no new-contact slot.
  • You want to send inspection videos. Video isn't supported; photos and voice messages are (see what an iMessage API can send).
  • Your inspection software already gets approvals by link and customers use it.

Related setups: technician on-the-way texts, text-a-photo quotes and dealership lead follow-up, all on the use-case hub.

How much does iMessage for repair approvals cost?

Repair approvals cost the plan price: ZeroSettle charges no per-message fee, and messages are unlimited.

ZeroSettle plans for repair approvals (zerosettle.io, checked October 12, 2026)
PlanPriceFits
Pro$49/monthOne number, 5 new contacts a day, unlimited messages with people who message you first, no setup fee, self-serve
GrowthFrom $599/mo3+ numbers working as one, 50 new contacts a day per number, set up after a demo

Both plans carry a 30-day money-back guarantee on the subscription. Approvals sent by advisors or your shop system use no AI credits. See plans and pricing, or get a number.

Frequently asked questions

Is a text YES a valid repair authorization?

That depends on your state's repair-order rules. California's Write It Right guide counts a text as electronic authorization if you record the items in the table above; other states set their own rules, so check yours. This isn't legal advice.

Can a customer approve with a 👍 tapback instead of typing YES?

A tapback reaches your webhook as reaction.received, with target_id pointing at the photo it was placed on. The handler above ignores it. Whether it counts as approval is your call and your state's; the safer default is to ask for a typed YES.

Can we text our digital inspection report link at drop-off?

It sends, but until the customer replies or saves your number, their iPhone shows the link as plain text, and the API flags a first-contact link with warning.code: "link_not_tappable". Send the photo and price first, and the link after they answer.

What is the closest thing to a video of the problem?

A photo with a caption, then a short voice message from the tech: up to 6 MB, delivered as Messages' audio bubble with a waveform. A voice message has no caption, so it goes as its own message; send it within 5 minutes of the photo and the two count as one unanswered message.

Sources

  1. ZeroSettle iMessage API docs: “Send a message”, “Attachments”, “Read messages”, “Webhooks, step by step” and “Errors, limits, and compliance”. Checked October 12, 2026.
  2. ZeroSettle homepage: Pro and Growth plan cards. Checked October 8, 2026.
  3. Write It Right: Documentation and authorization requirements for automotive repair dealers, California Bureau of Automotive Repair (updated January 2026). 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, paragraph (a)(2), the FCC's TCPA rule (Legal Information Institute, Cornell Law School). 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 and the zerosettle.io homepage as of October 12, 2026. Outside facts come from the pages listed under Sources, on the dates shown there. Eastside Auto is a fictional shop, and this page is not 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.