iBlueText API

Base URL https://ibluetext.com/api/v1 · JSON over HTTPS.

Introduction

The iBlueText API lets you validate which numbers are on iMessage and send real blue-bubble messages from your dedicated line — straight from your own app, backend, or CRM. Every request is authenticated with an API key and returns JSON.

Which API for what

All endpoints share the same base URL and Bearer-key auth — they're grouped here by purpose so you pick the right one. Full details for each are in the sections below.

PurposeEndpoint
iPhone Lookup — is a number on iMessage?POST/GET /api/v1/lookup
Send — send an iMessage/SMSPOST/GET /api/v1/send
SMS Verify / 2FA — send & check a codePOST /api/v1/verify · /siteverify · /sites
Campaigns — one message to a whole list (iMessage, SMS, auto-detect or WhatsApp), with statsPOST/GET /api/v1/campaigns
WhatsApp — send from your linked or hosted WhatsApp number; warm-up + reply-rate statsPOST/GET /api/v1/whatsapp
WhatsApp Lookup (beta) — is a number on WhatsApp?POST/GET /api/v1/wa_lookup
Contact records — fields, tags, opt-out, lead scoreGET/POST /api/v1/contacts?phone=
Sequences — enroll / stop drip follow-upsGET/POST /api/v1/sequences
Lines — your lines, caps and healthGET /api/v1/lines
Webhooks — set up your reply bot with only a keyGET/POST /api/v1/webhooks
Conversations — read full in/out message history (for CRMs / agents)GET /api/v1/conversations
Verified contacts — export widget-verified numbersGET /api/v1/contacts
Balance & usage — credits + per-line daily capsGET /api/v1/balance

Get a key under Integrations → API keys. Every request uses Authorization: Bearer <key> and only ever sees your own account's data.

For AI agents 🤖

iBlueText is built to be set up by an AI agent. A human verifies once (the only human step), then your agent does the rest via the API. There's a machine-readable summary at ibluetext.com/llms.txt, the full OpenAPI 3.1 spec at ibluetext.com/openapi.json, and an MCP server on npm, ibluetext-mcp (30 tools: sending, campaigns, WhatsApp, lookups, conversations and lead scores, AI pause and Quick answers, contacts, sequences, webhooks, verification). Add it to Claude, Cursor or any MCP client: command npx -y ibluetext-mcp with the environment variable IBT_API_KEY set to your API key.

Paste this to your agent (Claude or any) to get started:

I'm setting up iBlueText (https://ibluetext.com) for SMS/iMessage.
Read https://ibluetext.com/llms.txt and https://ibluetext.com/docs/.
I'll verify I'm human via SMS and give you my API key
(send it as: Authorization: Bearer <key>). Then:
  1) create a verify-widget site (POST /api/v1/sites) and give me the embed snippet,
  2) help me run an iPhone lookup (POST /api/v1/lookup) on my list,
  3) set up sending (POST /api/v1/send) as I describe.

Your agent can create widget sites, run lookups, send messages, and pull contacts entirely through the API — see the endpoints below.

Authentication

Create a key under Settings → API keys, then send it as a Bearer token on every request. Keep keys secret — anyone with a key can spend your credits.

Authorization: Bearer ibt_live_xxxxxxxxxxxxxxxxxxxxxxxx

Quickstart

Check whether three numbers are on iMessage:

curl https://ibluetext.com/api/v1/lookup \
  -H "Authorization: Bearer $IBT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"numbers": ["+18135550142", "+17275550199", "+13055550123"]}'
{ "id": 8412, "status": "queued", "total": 3,
  "poll_url": "https://ibluetext.com/api/v1/lookup?id=8412" }

Then poll the poll_url until status is done.

POST/lookup

Submit numbers to validate. Numbers are de-duplicated and must include a country code. One lookup credit is spent per number (your first 100 are free).

Body

{ "numbers": ["+18135550142", "+17275550199"] }   // array, or comma-separated string

Response 201

{ "id": 8412, "status": "queued", "total": 2, "poll_url": "…/lookup?id=8412" }

GET/lookup?id=<id>

Fetch job progress and results. imessage is true (blue bubble), false (SMS), or null (not yet checked).

{ "id": 8412, "status": "done", "total": 2, "completed": 2, "iphones": 1,
  "results": [
    { "number": "+18135550142", "imessage": true,  "checked": true },
    { "number": "+17275550199", "imessage": false, "checked": true }
  ] }

POST/send

Queue an iMessage or SMS from your active sending line. Requires a provisioned line. Recipients who replied STOP are blocked automatically.

Choosing iMessage, SMS or WhatsApp: the endpoint picks the app. POST /api/v1/send sends iMessage or SMS from your line; channel is auto (default: iPhones get iMessage, everyone else SMS), imessage or sms. To send WhatsApp, use POST /api/v1/whatsapp with the same to and body; with a hosted WhatsApp number on your line it goes from that same number. For a whole list, POST /api/v1/campaigns takes channel: auto, imessage, sms or whatsapp.

Body

{ "to": "+18135550142",
  "body": "Hi {firstname}! Your order shipped 🎉",
  "channel": "auto",            // optional — auto (default), imessage or sms. WhatsApp: use /whatsapp
  "first_name": "Sarah",       // optional — fills {firstname}
  "line_id": 12,                // optional — defaults to your first active line
  "effect": "confetti",         // optional — iMessage effect (see below)
  "typing": true,               // optional — show the "…" typing bubble first
  "send_at": "2026-10-20T09:30:00-04:00",  // optional — schedule it (up to 30 days ahead)
  "reply_to": "in_3909" }       // optional — iMessage "reply" bubble quoting that message of theirs

A key limited to one line always sends from that line (another line_id returns 403). Scheduled messages wait until send_at, then normal pacing applies.

Response 201

{ "id": 99213, "to": "+18135550142", "status": "queued", "line_id": 12, "send_at": null, "poll_url": "…/send?id=99213" }

POST/campaigns

Send one message to a whole list, exactly like the Campaigns page (it is the same engine). Opt-outs and duplicates are removed, every {merge} field must match a recipient field (otherwise 422 and nothing is sent), people you already text stay on their line, and new people are spread across your lines. channel: imessage, sms, auto (looks each number up first, then sends iMessage or SMS; status resolving until then) or whatsapp (from your WhatsApp numbers: each number's warm-up applies and anyone who never replied to your last 3 WhatsApp messages is skipped and counted in note).

curl -X POST https://ibluetext.com/api/v1/campaigns -H "Authorization: Bearer $IBT_KEY" -H "Content-Type: application/json" -d '{
  "name": "October promo",
  "message": "Hey, is this {first_name}?",
  "channel": "imessage",
  "line_id": "all",
  "recipients": [ { "phone": "+18135550142", "first_name": "Sam", "company": "Acme" }, { "phone": "+17275550199", "first_name": "Ana" } ]
}'
→ 201 { "ok": true, "campaign_id": 88, "total": 2, "channel": "imessage", "status_url": "https://ibluetext.com/api/v1/campaigns?id=88" }

GET  /api/v1/campaigns            → { "campaigns": [ { "id": 88, "name": "October promo", "channel": "text", "status": "sending", "total": 2, "sent": 1, "replies": 0 } ] }
GET  /api/v1/campaigns?id=88      → { "campaign": { …, "stats": { "sent": 1, "delivered": 1, "read": 0, "failed": 0, "waiting": 1, "held_by_daily_cap": 0 }, "replies": 0, "reply_rate": 0, "note": null } }
POST /api/v1/campaigns { "id": 88, "action": "pause" }   (or "resume")

recipients can also be CSV text with a header row ("first_name,phone\nSam,+1813…"). Optional: wa_session_id (WhatsApp: one number), media (same as /send), effect (iMessage). No active line → 402 (a human adds one). A key limited to one line can only use that line and can't run WhatsApp campaigns.

GET/send?id=<id>

{ "id": 99213, "to": "+18135550142", "channel": "imessage", "status": "delivered", "sent_at": "2026-06-17 14:02:11", "error": null, "send_at": null, "read_at": "2026-06-17 14:05:40" }

read_at is set once they read an iMessage (only if they have read receipts on); otherwise it stays null.

Status values: queued → sent → delivered, or failed (with the reason in error).

GET/lines

Your sending lines with live health and today's usage. A key limited to one line (Integrations → API keys → "Only …") sees just that line.

{ "lines": [ { "id": 14, "label": "Main", "phone": "+17273016057", "status": "active",
    "health": { "last_sent_at": "…", "sent_today": 41, "failed_today": 0, "last_failed_at": null, "last_error": null },
    "caps": { "new_contacts_per_channel_today": 50, "used": { "imessage": 12, "sms": 3 }, "replies": "unlimited" },
    "send_window": { "start_hour": 9, "end_hour": 20, "timezone": "America/New_York" }, "webhook_url": null } ],
  "key_limited_to_line": null }

iMessage effects, typing & reactions

The things that make a text feel like it came from a person on an iPhone. They only exist in iMessage: if a message ends up going by SMS (Android), the text still sends and the extra is simply skipped.

Effects — "effect" on /send

Full-screen: confetti, balloons, fireworks, love (hearts), celebration, lasers, echo, spotlight. Bubble: slam, loud, gentle, invisible_ink. The effect plays when the recipient opens the message on an iPhone. Also in the dashboard: the ✨ picker in the Inbox and on Campaigns.

Typing bubble — "typing" on /send

true shows the "…" bubble for a couple of seconds (scaled to the message length) before it arrives; or pass seconds, 1–8. Only in an existing iMessage conversation — a first message to someone has no thread to type into, so it just sends. Built-in AI replies already do this automatically.

POST/react — tapbacks

React to a message someone sent you. Use the message_id from the webhook or the conversations API, or pass to to react to their latest message.

{ "message_id": "in_3909", "reaction": "love" }
{ "to": "+18135550142", "reaction": "like", "line_id": 12 }   // line_id optional

Reactions: love ❤️, like 👍, dislike 👎, laugh 😂, emphasize ‼️, question ❓. Prefix with - (e.g. "-love") to take it back. Response 201: { "id": 51, "status": "queued", "reaction": "love", "message_id": "in_3909", "poll_url": "…/react?id=51" }; GET /react?id=51 → queued / sent / failed with an error. iMessage conversations only (an SMS thread fails with a clear error). Reactions are free and don't count toward line limits. In the dashboard, hover any incoming message in the Inbox (tap on a phone) to react.

SMS Verify — widget & API

Add "verify you're human via SMS" to any form. Two ways:

Drop-in widget (easiest)

Create a site under SMS Verify to get a site_key, then paste this above your form's submit button:

<div class="ibt-verify" data-sitekey="ibt_site_xxx" data-theme="auto"></div>
<script src="https://ibluetext.com/v.js" async></script>

The widget gates your submit button until verified, then injects a hidden ibt_token field. Themes: auto / light / dark. It also emits an ibt-verified DOM event.

Where to place it: any form that collects a phone (signup, contact, checkout); the "add your phone" step after a Google/Apple login (social login gives you an email but no verified phone); or as a gatekeeper — render it on a page and only grant access (or call your backend) once the ibt-verified event fires or /siteverify confirms the token.

Capturing name & email: the widget auto-reads the name + email from the surrounding form and attaches them to the verified contact (so a contact form becomes name+email+verified-phone). Add data-require="name,email" to require those fields before the user can verify, or data-name-field/data-email-field (CSS selectors) to target specific inputs. Omit for phone-only capture.

Exactly where in the page:

For one-tap autofill (recommended): add autocomplete tokens to your own fields — name field autocomplete="name", email field type="email" autocomplete="email". The widget's phone input already has autocomplete="tel". With all three, iOS "AutoFill Contact" fills name + email + phone in one tap, and the verification code one-tap-fills on iPhone / auto-fills zero-tap on Android Chrome. The code field needs nothing extra — the widget handles it.

REST API (server-side)

# 1. start — send a code (server uses your secret API key)
curl https://ibluetext.com/api/v1/verify \
  -H "Authorization: Bearer $IBT_KEY" -H "Content-Type: application/json" \
  -d '{"phone":"+18135550142","page_url":"https://you.com/signup"}'
# -> { "id": 5512, "status": "queued" }

# 2. check — confirm the code the user typed
curl "https://ibluetext.com/api/v1/verify?action=check" \
  -H "Authorization: Bearer $IBT_KEY" -H "Content-Type: application/json" \
  -d '{"id":5512,"code":"481920"}'
# -> { "verified": true, "token": "…", "phone": "+18135550142", "channel": "imessage" }

The code is delivered by iMessage (iPhone) or SMS (Android) automatically. Each verification logs a proof-of-consent record (number, time, IP, page, consent text).

POST/siteverify

Confirm a widget token on your backend with your secret key (like Cloudflare's siteverify). Tokens are single-use.

curl https://ibluetext.com/api/v1/siteverify \
  -H "Content-Type: application/json" \
  -d '{"secret":"ibt_secret_xxx","token":""}'
# -> { "success": true, "phone": "+18135550142", "channel": "imessage", "verified_at": "…" }

GET/contacts

Pull your verified contacts — phone, name, email, channel, and verified time — to sync into your own database or CRM. Paginate with next_since.

curl "https://ibluetext.com/api/v1/contacts?since=0&limit=200" \
  -H "Authorization: Bearer $IBT_KEY"
# -> { "contacts":[ { "id":91, "phone":"+18135550142", "name":"Sarah Lee",
#        "email":"sarah@x.com", "channel":"imessage", "verified_at":"…" } ],
#      "count":1, "next_since":91 }

You can also download everything as CSV from SMS Verify → Download contacts.

Billing & the 402 "out of credits" error

The first 100 credits are free for testing. 1 credit = $0.005, spent by either one iPhone lookup or one SMS verification (shared pool). Separately, your first 10 SMS verifications are free (no card) — a distinct trial bucket from the 100 lookup credits. Sending iMessage/SMS instead needs an active line subscription ($99/mo + $249 setup), not credits.

When you run out, the paid endpoints return HTTP 402 with a machine-readable body so an AI agent knows exactly what to do:

{ "error": "payment_required",
  "reason": "insufficient_credits",      // or "subscription_required"
  "message": "…buy credits to continue.",
  "action": "lookup",                     // or "verify" / "send"
  "needed": 250, "available": 0, "unit_price_usd": 0.005,
  "human_action_required": true,          // a HUMAN must pay — agents can't enter a card
  "buy_url": "https://ibluetext.com/portal/billing.php" }

Agents: on a 402, stop and tell your human to pay at buy_url, then resume. Buy credits · Subscribe a sending line.

Check your wallet + per-line daily usage anytime: GET /api/v1/balance → credits, free-trial + free-daily lookups remaining, and each line's new-contact / non-replier counts. Poll it to warn before $0 or a daily cap.

Sending limits

To keep your number safe from carrier/Apple bans, each line is capped per customer-local day (resets 4am your time):

Webhooks — connect your own reply bot

Every text that comes in on your line is POSTed to your server within seconds — campaign replies, new conversations, photos and voice memos, opt-outs. Use it to let your own system answer with your customers' data: appointment confirmations ("Reply Y to confirm"), showings, order status, account balances — anything our built-in AI can't know. Your bot (written by your developer, or by your own AI assistant) replies through POST /api/v1/send, and the whole conversation still shows in your Inbox so you can jump in anytime.

Where to set it: Settings → Webhook for the whole account, or Lines → Reply webhook for one line (overrides the account webhook — handy when each line serves a different business or campaign). Tick "Replies handled by my webhook" to switch our built-in AI off, so nobody ever gets two answers. Send test posts a message.test event so you can check your endpoint.

What your bot needs — 3 things: (1) the webhook URL — this comes from your bot (where it receives texts); (2) your signing secret — Settings → Webhook, with a Copy button; (3) an API key so it can reply — Settings → API keys (shown once).

Let your AI set it up — one API key, no logins

Easiest of all: make an API key, give it to your AI assistant or developer, and say "connect my reply bot to iBlueText — see ibluetext.com/docs/#webhooks-api". With that one key it can read your signing secret and line IDs, save its own webhook URL, and send itself a test. It never needs your password, and the key can only touch webhook settings here (no billing, no account details). Revoke the key in Settings any time.

GET/api/v1/webhooks — signing secret, account webhook, and every line (id, number, reservation, webhook)

curl https://ibluetext.com/api/v1/webhooks -H "Authorization: Bearer $IBT_KEY"

{ "signing_secret": "09a936…",
  "account_webhook": { "url": null, "replies_handled_by_webhook": false },
  "lines": [ { "line_id": 2, "label": "bluesend03", "phone": "+17276439408",
               "reserved_until": "2026-10-15T13:00:00Z", "reserved_note": "BCI campaign",
               "webhook_url": null, "replies_handled_by_webhook": false } ],
  "events": ["message.reply", "message.optout", "message.test"], ... }

POST/api/v1/webhooks — save the URL (and optionally test it)

curl -X POST https://ibluetext.com/api/v1/webhooks -H "Authorization: Bearer $IBT_KEY" \
  -H "Content-Type: application/json" -d '{
  "url": "https://yourbot.com/sms-webhook?key=your-own-secret",
  "lines": "reserved",                  // "account" (default) | "all" | "reserved" | [2, 4, 9]
  "replies_handled_by_webhook": true,   // default true — built-in AI stays quiet
  "test": true                          // POST a message.test to the URL after saving
}'
→ { ...same as GET..., "updated": 7, "test": { "ok": true, "http_code": 200 } }
POST https://yourapp.com/sms-webhook?key=your-own-secret
Content-Type: application/json
X-IBT-Signature: 5d41402abc4b2a76b9719d911017c592...   // hex HMAC-SHA256 of the raw body

{ "event": "message.reply",              // or "message.optout" (STOP etc.), "message.test"
  "message_id": "in_48213",              // unique per inbound message, identical on retries
  "from": "+18135550142",                // E.164
  "to": "+17276439408",                  // your line that received it
  "line_id": 2,
  "channel": "imessage",                 // "imessage" | "sms"
  "body": "Yes please!",
  "attachments": [ { "url": "https://ibluetext.com/m.php?...", "mime": "image/jpeg", "name": "IMG_0142.jpg" } ],
  "received_at": "2026-10-12T14:03:22Z", // ISO-8601 UTC
  "campaign_id": 123,                    // last campaign that texted this number, or null
  "fields": { "first_name": "Jane", "shares": "40" } }  // that contact's CSV columns, or {}

Don't have a developer? Give your own AI assistant (Claude, ChatGPT) an API key and this page with "build me a reply bot for my website that answers from my customer database, and connect it using /api/v1/webhooks", or have us build it for you (from $299 setup).

Status events (opt-in)

Turn these on in Settings → Webhook, or with POST /api/v1/webhooks {"status_events":["message.delivered","message.failed"]}. They go to the same URL, signed the same way. Off by default so bots built only for replies aren't surprised.

Contact records

GET  /api/v1/contacts?phone=+18135550142   → { "contact": { "phone": "+18135550142", "fields": { "first_name": "Sam" }, "tags": ["vip"], "opted_out": false, "last_activity": "2026-10-15 14:02:11", "lead": { "score": 72, "tier": "hot" } } }
POST /api/v1/contacts { "phone": "+18135550142", "fields": { "first_name": "Sam" }, "tags": ["vip"], "remove_tags": ["cold"], "opted_out": false }   → 201 created / 200 updated

Lead score (lead, also on every conversation): the same 0-100 number and hot / warm / cold tier the dashboard and the app show. Replied +30, replied in the last 7 days +20 (8-14 days +10), +2 per message they sent (max +20), on the pipeline +10 (+5 with a value), tagged hot/customer/booked +10, upcoming booking +15, quiet 30+ days −20; opted out or blocked = 0. Hot is 70+, warm 40-69, cold under 40. Set fields.lead_tier to hot|warm|cold|dead to override it (comes back with "manual": true).

Fields merge (existing ones are kept). Setting opted_out:false resubscribes; only do that if the person asked to hear from you again. Adding a tag can start a tag-triggered sequence. Every call you make is listed in Portal → API log for 30 days.

Sequences (drip follow-ups)

Build them at Portal → Sequences: steps of "wait N minutes/hours/days, then text", with {first_name}-style fields. A contact stops getting steps the moment they reply (or opt out). Start them by hand, from Contacts, when someone texts a keyword, when a tag is added, or by API:

GET  /api/v1/sequences                                         → { "sequences": [ { "id": 3, "name": "New lead", "active": true, "steps": 3, "active_contacts": 12 } ] }
POST /api/v1/sequences { "sequence_id": 3, "phone": "+18135550142" }          → 201 { "ok": true }
POST /api/v1/sequences { "sequence_id": 3, "phone": "+18135550142", "stop": true } → { "ok": true, "stopped": 1 }

Every step goes out through your normal lines, so daily limits, warm-up and send hours still apply.

WhatsApp (your own number, or one we host)

Pair your own WhatsApp number by QR at Portal → WhatsApp (like WhatsApp Web), or have us host one: +$49/mo on your iMessage line's number or $99/mo for a WhatsApp-only number. WhatsApp may ban numbers used for bulk or unsolicited messages. If a hosted number is banned, its WhatsApp charge stops and your iMessage line keeps working.

Warm-up and pacing: 1 send every 4 s per number. A number can start 10 new conversations in its first 24 hours, rising evenly to 300 a day by day 21. A "new conversation" is a contact this number has never messaged and who never messaged it. Replies, and contacts who have written back, are never limited. Over the limit, a message stays queued with held_until and goes out automatically. Keep your reply rate up: open with a question like "Hey, is this {first_name}?" and don't follow up with people who never answered. ?numbers=1 shows each number's warm-up and 7- and 30-day reply rate.

3 tries: someone who has never replied gets at most 3 WhatsApp messages (sends within the same minute count as one). The 3rd returns a warning; a 4th is refused with 422 and "code": "no_reply_limit" until they reply. One reply removes the limit for that person. Auto-slow: if fewer than 15% of a number's new contacts replied over the last 7 days (once it has 20+), its new-conversation limit is halved until the rate recovers. You're emailed, ?numbers=1 shows "slowed": true, and held messages say why in held_reason. WhatsApp campaigns: pick WhatsApp list in Portal → Campaigns.

POST /api/v1/whatsapp   { "to": "+447700900123", "body": "Hi! Your order is ready.", "media_url": "https://example.com/receipt.jpg" }   → 201 { "id": 41, "status": "queued" }
GET  /api/v1/whatsapp?id=41      → { "id": 41, "to": "+447700900123", "status": "read", "error": null }
GET  /api/v1/whatsapp?numbers=1  → { "numbers": [ { "session_id": 3, "phone": "+17275550142", "status": "connected", "hosted": true,
                                     "warmup": { "day": 4, "of": 21, "new_contacts_limit_24h": 54, "used_24h": 12, "left_24h": 42, "held": 0, "next_free_at": null },
                                     "reply_rate": { "7d": { "new": 40, "replied": 17, "rate": 43 }, "30d": { … } }, "unanswered_2plus": 2, "health": "good" } ] }

Status: queued → sent → delivered → read, or failed with error (e.g. "This number is not on WhatsApp"). Incoming WhatsApp messages go to your webhook as whatsapp.reply (from, to, channel:"whatsapp", body, name, received_at). STOP opts them out of every channel.

Opt-outs

Compliance is automatic. Any reply containing STOP, UNSUBSCRIBE, CANCEL, QUIT, or END adds that number to your opt-out list, and the API will refuse to send to it (409). This is required under the TCPA — see our Terms.

Conversations API

Pull full two-way transcripts so your own app or AI agent can read what each contact said and act on it (e.g. update a CRM, or add/remove a listing on another site). iBlueText returns the raw in/out messages — your side does the judging.

GET/api/v1/conversations

Query params: phone (one conversation), since (ISO time — only conversations with newer activity, for incremental polling), limit (default 200, max 1000), include_no_reply=1 (also include contacts who never replied; default is repliers only).

curl "https://ibluetext.com/api/v1/conversations?limit=200" \
  -H "Authorization: Bearer $IBT_KEY"

{
  "count": 1,
  "conversations": [
    {
      "phone": "+18139188356",
      "company_name": "FinSanity Fishing Charters",
      "link": "https://beercanisland.com/c/0XCtDU1a",
      "fields": { "company_name": "FinSanity Fishing Charters", "link": "https://beercanisland.com/c/0XCtDU1a" },
      "opted_out": false,
      "last_activity": "2026-07-11 19:39:04",
      "messages": [
        { "dir": "out", "body": "Hi is this FinSanity Fishing Charters?", "at": "2026-07-11 19:35:00", "channel": "sms", "status": "sent" },
        { "dir": "in",  "body": "Yes, it is", "at": "2026-07-11 19:37:17" },
        { "dir": "out", "body": "Here's your link: https://beercanisland.com/c/0XCtDU1a", "at": "2026-07-11 19:39:40", "channel": "sms", "status": "sent" }
      ]
    }
  ]
}

Each conversation carries the contact's uploaded fields (including company_name and link), an opted_out flag, and the ordered messages (dir is in or out). Generate a key under Integrations → API keys.

AI auto-replies

Turn on Inbound AI and your line answers incoming texts automatically — trained on your business, and optionally on per-contact data you provide. Two modes:

Give the AI a per-contact database

When you send a campaign, include a CSV with a phone column plus any fields you want. Each row is stored against that number and handed to the AI when the person replies:

company,phone,loan_amount,rate,due_date,link
Reel Deal LLC,+18135550142,$45000,6.9%,Aug 1,https://you.com/portal/abc123
Salty Dog LLC,+17275550199,$12000,8.4%,Sep 15,https://you.com/portal/def456

Now if +18135550142 texts "what's my rate and my link?", the AI replies with 6.9% and that contact's link. Guide it from your Instructions, e.g. "If a client asks about their loan, share the amount, rate, and due date from their info; if you don't have a detail, say a teammate will follow up."

Safety: replies never cross contacts

Every reply is scoped to the exact E.164 phone number that texted in — the model only ever receives that one contact's fields, so John can't get Jane's data. The AI is also instructed never to invent account details (amounts, rates, balances, links); if it doesn't have a value on file, it defers to a human instead of guessing. STOP/opt-outs are never answered.

CRM & integrations

Because everything runs over a standard REST API, iBlueText drops into any stack:

Native one-click connectors are rolling out — tell us which you need at russ@ibluetext.com.

SMPP

Already running a messaging platform? Connect to iBlueText over SMPP v3.4 — the same protocol you use with SMS carriers — and send iMessage (and green RCS/SMS) without writing to our REST API. Credentials are self-serve: create a bind under SMPP in your dashboard and it goes live within about a minute.

Hostsmpp.ibluetext.com
Port3550 — TLS only (ssmpp). Plaintext SMPP is not offered.
VersionSMPP v3.4, bind_transceiver recommended
EncodingsGSM 03.38 (0x01), Latin-1 (0x03), UCS2 (0x08)

Trunks

Each bind is a trunk, and you pick the route by choosing which bind you submit on:

Submitting

Delivery receipts

Set registered_delivery=1. Receipts come back as deliver_sm with esm_class 0x04 and the conventional text format:

id:12345 sub:001 dlvrd:001 submit date:2609221200 done date:2609221205 stat:DELIVRD err:000 text:

What you can expect depends on how the message actually travelled — which we report honestly rather than guessing:

iMessagereal confirmation → DELIVRD
RCSreal confirmation → DELIVRD
SMSno delivery receipt exists → ACCEPTD only

We deliver green — we don't select RCS. The handset and the two carriers negotiate RCS vs plain SMS per recipient. We can tell you which happened, but not choose it.

Throughput

Each bind has an agreed TPS limit; exceeding it returns ESME_RTHROTTLED (0x58) — that's flow control, not an error, so back off and retry. Size your expectations to the delivery rate, not your submission rate: you can submit far faster than messages go out, and the queue absorbs the difference. The same per-line daily caps apply to SMPP traffic as to everything else, so if you need a specific throughput, tell us up front and we'll provision enough lines.

Errors & limits

Errors return a JSON error string with a standard HTTP status:

401 invalid/missing key   ·   402 out of credits / no line
400 bad request           ·   404 not found
409 recipient opted out   ·   429 rate limited

Up to 50,000 numbers per lookup request. Sends are paced by your line's safe throughput. Need higher volume? Add more lines.

Get your API key — start free →