Messaging

A messaging line is a dedicated number for messages, separate from your calling numbers. It sends and receives iMessage, SMS, MMS and WhatsApp.

Messaging lines

  • One number per line, used only for messaging and calls from that line.
  • Unlimited iMessage, SMS, MMS and WhatsApp messages.
  • Calls from the line are included.
  • Number reputation and daily spam label monitoring on the line are included.

Calls to the line from a lead ring the agent who last spoke with them, the same way as callbacks.

Messaging lineA dedicated number for messaging, separate from your calling numbers. Unlimited iMessage, SMS, MMS and WhatsApp. Calls from the line and number reputation on it are included. $500/ line / month

Registration in your business name

  • SMS and MMS: we register the brand and the messaging campaign for each line in your business name during setup. For a subaccount, that is the client's business name. The sms capability turns on when the registration is approved, and account.capability_updated tells you.
  • WhatsApp: the line uses the client's own WhatsApp Business account, which they connect during setup. Messages show their business name, and templates are approved under their account.
  • iMessage: messages come from the line's number.

Send a message

POST/v1/messages

ParametersTypeDescription
torequired string The recipient, E.164.
fromrequired string Your messaging line.
channel auto | imessage | sms | mms | whatsapp auto sends iMessage when the recipient has it, and SMS or MMS otherwise. Default auto.
body string Up to 1,600 characters.
media_urls string[] Images or files to attach. Sent as MMS on the SMS path.
lead_id string Links the message to the lead and their last call.
template object WhatsApp only. An approved template with name, language and variables. Needed when the recipient has not written to you in the last 24 hours.
metadata object Up to 20 keys. Returned on every event for the message.
POST/v1/messages
curl https://api.liveleg.dev/v1/messages \
  -H "Authorization: Bearer $LIVELEG_API_KEY" \
  -H "Idempotency-Key: 3f9c1e7a-5b2d-4c8e-9a01-6d2f8b7e4c10" \
  -d to=+15125550147 \
  -d from=+15125550188 \
  -d channel=auto \
  -d body="Thanks for the time today, Dana. Here is the inspection checklist we talked about." \
  -d lead_id=led_44Hc2
Response
{
  "id": "msg_Rq40",
  "object": "message",
  "direction": "outbound",
  "channel": "imessage",
  "from": "+15125550188",
  "to": "+15125550147",
  "body": "Thanks for the time today, Dana. Here is the inspection checklist we talked about.",
  "media_urls": [],
  "lead_id": "led_44Hc2",
  "status": "sent",
  "send_at": null,
  "created_at": "2026-11-03T15:51:02.880Z"
}

Before a message is sent we check your do-not-call list, opt-outs and quiet hours. Blocked messages return 422 with the rule.

Message statuses

StatusMeaning
queuedHeld for the recipient's quiet hours. send_at says when it goes out. Sends message.queued.
sentAccepted for delivery on the channel. Sends message.sent.
deliveredThe recipient's phone or app confirmed delivery. Sends message.delivered.
failedNot delivered. error has the code, such as unreachable, opted_out or template_required. Sends message.failed.
receivedAn inbound message from the recipient. Sends message.received.

List the messages with a lead, newest first, to show a conversation in your app. Filter by lead_id, to, from or channel.

GET/v1/messages

curl GET/v1/messages
curl "https://api.liveleg.dev/v1/messages?lead_id=led_44Hc2&limit=20" \
  -H "Authorization: Bearer $LIVELEG_API_KEY"

Channels

  • auto sends iMessage when the recipient has it, and SMS otherwise. With media, the SMS path sends MMS.
  • imessage, sms and mms send on that channel only.
  • whatsapp sends on WhatsApp when the recipient has an account.

The channel used is on every message event.

WhatsApp

Within 24 hours of the recipient's last message, send any text or file. Outside that window, WhatsApp accepts only an approved template. Send template with its name, language and variables. A free-form message outside the window fails with template_required.

curl
curl https://api.liveleg.dev/v1/messages \
  -H "Authorization: Bearer $LIVELEG_API_KEY" \
  -d to=+15125550147 \
  -d from=+15125550188 \
  -d channel=whatsapp \
  -d body="Here are the photos from the roof inspection." \
  -d media_urls=https://files.example.com/ridgeline/roof-1.jpg
curl
curl https://api.liveleg.dev/v1/messages \
  -H "Authorization: Bearer $LIVELEG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+15125550147",
    "from": "+15125550188",
    "channel": "whatsapp",
    "template": {
      "name": "inspection_followup",
      "language": "en_US",
      "variables": ["Dana", "Thursday"]
    }
  }'

Media and MMS

Attach files with media_urls. Each URL must be public HTTPS. We fetch the file when the message is sent. Inbound files arrive in media_urls on message.received, stored with your account for as long as the message.

ChannelFile typesSize
MMSJPEG, PNG, GIF, PDF, vCard, MP4Up to 5 MB per message, up to 10 files. Larger images are resized to fit.
iMessageImages, video, PDF and other filesUp to 16 MB per file, up to 10 files.
WhatsAppJPEG, PNG, MP4, PDFUp to 16 MB per file, 1 file per message.

Quiet hours

Messages go out between 8 am and 9 pm in the recipient's local time, with the same state rules as calling hours. A message sent outside that window is queued, sends message.queued with send_at, and goes out when the window opens. Replies from the recipient are delivered to you at any time.

Opt-out words

STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE and OPT OUT all opt the number out of messages and add it to your do-not-call list. Words are matched on any channel, in any letter case. The opt-out covers every messaging line in the account or subaccount. The recipient gets one confirmation message, and you get message.opted_out and dnc.added.

START or UNSTOP opts back in to messages and sends message.opted_in. The do-not-call entry for calls stays in place.

Replies

Replies arrive as message.received with the channel, the lead and their last call. Delivery status comes as message.delivered or message.failed.

Event
{
  "id": "evt_6Mn40Yr2",
  "type": "message.received",
  "created_at": "2026-11-03T16:03:51.300Z",
  "livemode": true,
  "data": {
    "message_id": "msg_Rr51",
    "channel": "imessage",
    "from": "+15125550147",
    "to": "+15125550188",
    "body": "Got it. Can we talk Thursday?",
    "lead_id": "led_44Hc2",
    "last_call_id": "cal_8Jq2mT4x"
  }
}