All docs

How Conversations Start

senderZ only sends iMessage to people who have messaged you first. Why that rule exists, and how invite links let customers start the conversation.

senderZ follows one rule that shapes everything else:

An iMessage line never sends to a number that has not messaged it first.

If you send to someone who has never texted you, the message still goes out — it just goes over SMS instead of iMessage. Nothing is silently dropped.

Why

Apple suspends accounts that behave like spam operations. The pattern its systems flag is consistent: a young account, high volume, low recipient diversity, and traffic that only ever flows outward.

The single most dangerous element is the report rate. A handful of "Report Junk" taps can end an account within hours — and that button is only one tap away in one situation: a message from someone the recipient has never talked to. Once they have messaged you, the affordance is gone and the conversation is, by definition, wanted.

So rather than trying to look less like spam, senderZ removes the condition that creates the risk. The side effects are all good ones: the send-to-receive ratio stays healthy on its own, there is never a burst to unfamiliar numbers, and every conversation carries proof of consent.

Contact states

Every contact sits in one state per account:

| State | Meaning | iMessage | |---|---|---| | unknown | Never heard from them | Sends over SMS | | invited | You shared a link; they have not replied yet | Sends over SMS | | engaged | They messaged you | ✅ | | dormant | Engaged, but quiet for 90+ days | ✅ (transactional) | | opted_out | They sent STOP | Blocked entirely |

Only inbound messages create engagement. Sending to someone never grants the right to send to them, so the rule cannot bootstrap itself.

Check a contact's state before you send:

GET /v1/conversations/{phone_number}/state
{
  "data": {
    "phone_number": "+15551234567",
    "state": "engaged",
    "first_inbound_at": "2026-07-14T18:02:11.480Z",
    "can_imessage": true
  }
}

An invite is how someone becomes engaged. It opens their Messages app, pre-addressed to your line, with a short opener already written — they just hit send.

POST /v1/invites
label string

Internal name so you can tell invites apart, e.g. Storefront QR.

opener string

The message they will send you. ASCII only, 60 characters max. A short tracking code is appended automatically.

contact_number string

E.164. Set only when minting an invite for one specific person.

{
  "data": {
    "ref": "a7k2np",
    "link": "https://go.senderz.com/a7k2np",
    "number": "+15551230000",
    "opener": "Hi!",
    "label": "Storefront QR",
    "created_at": "2026-08-02T19:41:07.221Z"
  }
}

Use link as a button href, or encode it as a QR code to print. It works on both iOS and Android.

Where invites go

Anywhere your customers already are — a website button, an email signature, an order confirmation, an invoice footer, a storefront sign, a receipt, a vehicle. Physical placements convert best, since scanning a QR hands off to Messages directly while some in-app browsers block the transition.

Tracking

GET /v1/invites returns three counters per invite:

  • scan_count — the link was opened
  • use_count — their message actually arrived
  • engaged_count — people now reachable on iMessage because of it

The gap between scans and sends is your drop-off.

Errors

If iMessage is explicitly requested for a contact who has never messaged you and no SMS rail is configured, the send is blocked rather than quietly rerouted:

{
  "error": "Message blocked",
  "code": "inbound_first_required"
}

Share an invite link with that contact, or send with channel: "sms".