Use cases

Text-a-photo quotes over iMessage for junk removal and the trades

ZeroSettle gives junk-removal, moving and trades businesses an iMessage number that customers text a photo of the job to, so an estimator can reply from the ZeroSettle console with a price range and a booking link. Because the customer texts first, inbound quote requests are never limited and the booking link in the reply is tappable. Ask for one photo per text: if a customer attaches several photos to one message, only the first is delivered, and a video arrives without its file.

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.
  • The flow: a “Text a photo for a quote” sms: link or QR code opens Messages with “Quote please” filled in; the photo lands in the console and, if you want, on your webhook.
  • Photo rules: one photo per text. iPhone HEIC photos arrive as JPEG; video and documents arrive without the file.
  • Price: Pro is $49/month for one number. Growth, for several numbers working as one, is from $599/mo.
  • Main limit: none on customers who text first. Texting a web-form lead first makes them a new contact: 5 a day per number on Pro, 50 on Growth.
  • Not for: customers on Android, because ZeroSettle has no SMS fallback.

Who uses iMessage for photo quotes?

Photo quotes over iMessage suit businesses that price small jobs by looking at them: junk removal, movers, landscapers, plumbers, HVAC techs, roofers, pressure washers and handymen. A photo in the thread stands in for an on-site estimate that burns drive time on a job that may never book, a phone quote that is a guess, or a web form that leaves the customer waiting while a competitor answers first.

The same photo-in pattern runs in other businesses too: insurance claim photos, tenant maintenance requests and auto repair approvals. The use-case hub lists the rest.

How iMessage photo quotes work, step by step

In a ZeroSettle photo quote, the customer starts the thread and a person prices the job; ZeroSettle carries the messages and stores the photos.

  1. Publish the link. An sms: link labeled “Text a photo for a quote”, with “Quote please” as the body, goes on your website. Truck wraps and yard signs get a QR code of the same link, which customers scan with the iPhone Camera app.
  2. The customer texts first. That never counts against ZeroSettle's new-contact limit, and it makes your number a known sender, so links you send back are tappable.
  3. The photo arrives in the console and, if you registered a webhook, as a signed message.received event with an image object. Your code fetches the file from GET /api/imessage/messages/{id}/image with your API token and attaches it to a CRM lead.
  4. The estimator asks what the photo can't show (stairs, access, zip code), then replies with a range and the booking link. Each reply leaves once the line hands it to Apple, normally 2–5 seconds after Send.

Keep the line managed by API, the default: while the hosted AI agent manages a line, your estimators' sends from it are refused.

What arrives when a customer texts a photo

ZeroSettle delivers the first photo of each message, with the file, so put “one photo per text” next to the link and in your first reply.

What a ZeroSettle line receives from a customer (API docs, checked October 12, 2026)
The customer sendsWhat reaches the console and webhook
An iPhone photo in HEICA JPEG, converted by ZeroSettle
A JPEG, PNG or GIF within 4 MB and 4032 pxThe file as sent
A larger photoA JPEG, converted by ZeroSettle
A Live PhotoIts still
Several photos in one messageThe first photo only
A voice messageAn .m4a (AAC) recording with its length in duration_ms
A video or a documentThe message text, no file

iPhones save photos as HEIF (HEIC files) when Settings > Camera > Formats is set to High Efficiency (Apple Support). More on media: what an iMessage API can send and receive.

Example conversation: a junk-removal photo quote

This photo-quote thread runs from first text to booking in the ZeroSettle console. Both photos arrive because the customer sends them as two texts.

  1. Sat 8:41 AM
  2. Customer: Quote please
  3. Customer: [Photo: a garage full of boxes and an old couch]
  4. Business: Thanks! Send any more photos one per text. Is it all ground floor, no stairs? And roughly what zip?
  5. Customer: ground floor, 94110
  6. Customer: [Photo: a side yard with old fence panels]
  7. Business: That's about half a truck, $280–$340 all in. We can come Thursday 9–11am: book.example.com/haulright
  8. Customer: booked!❤️
Illustrative example with a fictional junk-removal company, HaulRight. Blue bubbles are sent from HaulRight's number by an estimator in the console; gray bubbles are the customer's. Bracketed lines stand for photos. The link is tappable because the customer texted first.

Setup: an sms: link and a photo inbox

Photo-quote setup is one link, plus a webhook receiver only if you want photos in your CRM. Estimators who answer from the console need no code.

html
<a href="sms:+18055550100&body=Quote%20please">Text a photo for a quote</a>

Use your ZeroSettle number. The link opens Messages on iPhone and Mac; on Android it opens the SMS app, so keep your quote form for those visitors. How to get customers to text you first covers QR codes and where to put them.

The receiver below checks X-ZeroSettle-Signature with the docs' verifier, answers 200 at once and fetches the photo in the background: ZeroSettle allows 10 seconds for a 2xx, then retries 3 more times (after 1, 5 and 20 seconds) with the same message id.

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

BASE = "https://portal.zerosettle.io"
TOKEN = os.environ["ZEROSETTLE_TOKEN"]
SECRET = os.environ["ZEROSETTLE_WEBHOOK_SECRET"]
app = FastAPI()
seen: set[int] = set()                     # use your database in production

def verify(header, raw):
    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 save_photo(msg):                        # swap the file write for your CRM call
    r = requests.get(BASE + msg["image"]["url"],
                     headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
    r.raise_for_status()
    ext = msg["image"].get("content_type", "image/jpeg").split("/")[-1]
    pathlib.Path("leads").mkdir(exist_ok=True)
    pathlib.Path("leads", f"{msg['id']}.{ext}").write_bytes(r.content)

@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") or {}
    if payload["event"] == "message.received" and msg.get("image") and msg["id"] not in seen:
        seen.add(msg["id"])                 # retries repeat the id
        tasks.add_task(save_photo, msg)
    return Response(status_code=200)

Register the receiver with PUT /api/imessage/webhook and store the signing secret, which is shown once. Apart from ZeroSettle's built-in Close integration, CRMs are connected through webhooks like this one; see the webhook steps in the ZeroSettle docs.

Limits and consent for photo quotes

ZeroSettle never limits customers who text your number first, so every quote request in a storm week comes through. Your sends go out one at a time, and texting a web-form lead first makes them a new contact.

ZeroSettle limits for photo quotes, per number (API docs, checked October 12, 2026)
SituationProGrowth
Customer texts first; your replies in that threadUnlimitedUnlimited
Your texts in a row with no reply from the customer2 (texts within 5 minutes of the first count as one), then 403 awaiting_reply
You text a web-form lead first5 a day50 a day, 3 per 15 min
Several sends at onceOne at a time; line_busy after about 12 s of waiting
  • 80 photo requests on a Saturday: none count as new contacts.
  • Three estimators hit Send at once: at the normal 2–5 seconds per text, the third waits about 4–10 seconds, inside the window before line_busy.
  • 25 web-form leads texted first on Pro: 5 go out that day; the other 20 get 429 new_contact_limit until the 3 AM Eastern reset. Ask for a photo in plain text: a link to someone who hasn't texted you isn't tappable.
  • An unanswered quote and follow-up, on either plan: that is the 2, so a third text waits for the customer's reply.

A quote request is not a marketing opt-in

The customer texted you about this job, so keep follow-ups to this quote. A seasonal promotion likely needs its own yes (ZeroSettle's docs say US marketing texts need written consent), and send nothing promotional before 8 am or after 9 pm in the customer's time zone. When a customer texts STOP, or a clear request like “take me off your list”, ZeroSettle stops every number on your account from reaching them until they text START; the TCPA consent guide has the rest. This isn't legal advice.

When ZeroSettle is not a fit for photo quotes

ZeroSettle is the wrong tool for photo quotes in these cases:

  • You quote from video walkthroughs. Inbound video and documents arrive without the file.
  • You want the hosted AI agent and your estimators on one number. While the agent manages a line, your own sends from that line are refused.
  • Most of your customers use Android. iMessage goes only to iPhone and other Apple users, and ZeroSettle has no SMS fallback.
  • You need a group thread with the homeowner, a property manager and your crew lead. ZeroSettle conversations are one-to-one.
  • You want to send quotes to people who didn't ask. ZeroSettle's terms forbid unsolicited messaging, and ZeroSettle suspends lines used for it.

How much does iMessage for photo quotes cost?

Photo quotes your team answers cost only the plan price: messages and photos are unlimited, with no per-message fee.

ZeroSettle plans (zerosettle.io and API docs, checked October 12, 2026)
PlanPriceFits
Pro$49/monthOne number to start, self-serve, 5 new contacts a day, unlimited messages with people who message you first, 1,000 AI credits a month
GrowthFrom $599/mo3+ numbers working as one, 50 new contacts a day per number, 5,000 AI credits a month; book a demo and ZeroSettle sets it up

Neither plan has a setup fee, and both carry a 30-day money-back guarantee on the subscription. AI credits apply only to texts the hosted AI agent sends (packs: 1,000 for $50 or 5,000 for $200); a line your estimators answer uses none. Compare ZeroSettle's plans or get a number.

Frequently asked questions

Can the estimator send a marked-up photo back?

Yes. Circle what the price covers in any photo editor, then send the image from the console with the range as its caption; the photo and caption arrive in one bubble. Over the API it is image_b64 on POST /api/imessage/send, up to 5 MB.

What if a photo message arrives without a file?

A message with kind: "image" and image: null is a photo whose download never finished. Ask the customer to send it again in its own text.

How long does ZeroSettle keep a customer's job photos?

ZeroSettle keeps messages indefinitely for now, photos included. The console shows them in the thread and your code can fetch any of them from its message's /image URL, so the crew can pull up the job photos on the day.

What if a customer texts “can you stop by Thursday?”

ZeroSettle treats any text with the word “stop” in it as an opt-out, “stop by” included, and refuses sends to that customer from every number on your account. They opt back in by texting START on its own; if they ask you another way, such as on a call, ZeroSettle can clear it.

Sources

  1. ZeroSettle iMessage API docs: “Send a message”, “Attachments”, “Customer support”, “AI agent”, “Webhooks, step by step” and “Errors, limits, and compliance”. Checked October 8, 2026.
  2. ZeroSettle homepage: plans. Checked October 8, 2026.
  3. ZeroSettle iMessage API Terms of Service, sections 3 (“No Unsolicited Communications”) and 13 (Compliance and Client Responsibility). Last modified September 28, 2026.
  4. Using HEIF or HEVC media on Apple devices, Apple Support. Checked October 8, 2026.
  5. What is the difference between iMessage, RCS, and SMS/MMS?, Apple Support. Checked October 8, 2026.
  6. Scan a QR code with your iPhone or iPad, Apple Support. 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. HaulRight and Jess T. 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.