MyComm docs
Menu

Sending

Sending messages

Every field of a send, which ones are required, and how batches, replies, scheduling and cancelling work.

POST /v1/messages needs three things: who sends, who receives and what to say. Everything else is optional.

The request#

Field Type Needed What it does
line_id string (uuid) One of line_id, channel_id, from when the key is not limited to one line The line to send from. A key limited to one line may leave all sender fields out.
channel_id string (uuid) One of line_id, channel_id, from when the key is not limited to one line Send from this channel, on the line it is the primary sender of, else on its default line.
from string One of line_id, channel_id, from when the key is not limited to one line A channel address (for SMS, the number); resolves like channel_id.
channel_type string Optional Only to choose between channels of different types that share the from address.
prefer sms | whatsapp Optional The channel type to try first when the line picks the sender; beats the line's prefer. When that type has no healthy sender or cannot reach the recipient, the line's next type sends.
to string Exactly one of to, contact_id Recipient address in the channel's format (a phone number in any accepted format for SMS). Not together with contact_id.
contact_id string (uuid) Exactly one of to, contact_id Send to this contact's address for the channel. Not together with to.
content object At least one of content, body, media_ids, template_id The full content form. type is text, media, template or a channel-specific type refused with CHANNEL_CAPABILITY_UNSUPPORTED. Excludes body, media_ids, template_id and variables.
body string At least one of content, body, media_ids, template_id Shorthand for text content. May go with media_ids.
media_ids array of uuid At least one of content, body, media_ids, template_id Shorthand for media content: ids from POST /v1/media. One item today.
template_id string (uuid) At least one of content, body, media_ids, template_id Shorthand for template content.
variables object Needed when template_id is sent Values for the template's variables. Needs template_id.
channel_options object Optional Options only some channels understand; ignored with a warning elsewhere.
smart_replace boolean Optional Replace characters that force UCS-2 encoding with plain look-alikes where it saves segments.
category transactional | marketing Needed when lane is bulk Required when lane is bulk (CATEGORY_REQUIRED).
lane high | normal | bulk Optional How urgently the message is sent. bulk is paced behind other traffic and needs category.
send_after string (date-time) Optional At most 30 days ahead (SEND_AFTER_TOO_FAR). A time in the past means now.
expires_in integer 60–604800 Optional Seconds after which an unsent message is dropped, 60 to 604800 (7 days).
reference_id string Optional Your own id for this message, 1 to 100 characters without spaces. Unique per account forever (REFERENCE_ID_EXISTS).
metadata object Optional Up to 50 keys, 2 KB as JSON, scalar values only. Returned on the message and in webhooks.
shorten_links boolean Optional Replace each URL with a short link so clicks are counted.
dry_run boolean Optional Validate, render and compute segments and holds without creating anything (answers 200).

The table is generated from the API contract, so it always matches the reference. The rules that depend on other fields:

  • Recipient: exactly one of to and contact_id.
  • Content: at least one of content, body, media_ids and template_id. content is the full form; body and media_ids are the shorthand for text and image messages. content together with a shorthand field is refused with CONTENT_CONFLICT. variables only goes with template_id.
  • Sender: four optional fields say who sends. line_id names the line; the line picks its first healthy sender and may move the message to the next one later. channel_id (or from, the channel's address) forces one channel: the message is sender_pinned and never moves. Without line_id it goes out on the line that has that channel as its primary, else on the channel's default line, so with a line-limited key send line_id too. channel_type only chooses between channels that share a from address (FROM_AMBIGUOUS). A key limited to one line may leave all four out.
  • Line errors: LINE_REQUIRED when nothing names a line and the key is not limited to exactly one; CHANNEL_LINE_MISMATCH when the fields point at different lines or the channel is not a sender of the line; LINE_HAS_NO_SENDERS when the line has no enabled sender; LINE_ARCHIVED when the line is archived. A line whose senders are all unhealthy still accepts the message and holds it (hold_reason: no_sender_available) until one recovers.
  • Category: required when lane is bulk, and always in a batch.

A request that breaks the shape rules answers 422 VALIDATION_FAILED with every broken field in details.fields, so you fix them in one round trip. Rules that depend on state, such as an opt-out or a number that is not valid, have their own codes.

A text message#

{
  "line_id": "0198e0b4-9411-7f60-a2d9-1c8b6e3a4d03",
  "to": "+923001234567",
  "body": "Your order #1094 is ready for pickup.",
  "category": "transactional",
  "reference_id": "order-1094",
  "metadata": {"order_id": "1094"}
}

Phone numbers may be written in any accepted format; MyComm normalises them to E.164. A number that cannot be parsed answers 422 INVALID_NUMBER.

Content forms#

You want Send
Text body
An image (MMS) upload with POST /v1/media, then media_ids: [id] (one item today), with or without body
A saved template template_id and variables
Something channel-specific content and channel_options; a content type or option the channel cannot carry answers 422 CHANNEL_CAPABILITY_UNSUPPORTED

Count segments before you send: characters outside the GSM-7 alphabet switch a message to UCS-2 and shorten each segment. dry_run returns the computed encoding and segments; smart_replace: true swaps look-alike characters that force UCS-2.

Lane and category#

category says why you are sending; lane says how urgently.

Message Category Lane expires_in
One-time code transactional high 600 (10 minutes)
Appointment reminder transactional normal 86400 (24 hours)
Invoice transactional normal 259200 (72 hours)
Promotion marketing bulk 86400 (24 hours)

marketing is refused for recipients who opted out (RECIPIENT_OPTED_OUT) and in countries where it gets SIMs blocked (MARKETING_NOT_ALLOWED). Pick the category the recipient would expect; content that reads as a promotion but is sent as transactional is refused with CATEGORY_MISMATCH.

Scheduling and expiry#

send_after holds the message until that time, at most 30 days ahead (SEND_AFTER_TOO_FAR); a time in the past means now. expires_in (60 to 604800 seconds) drops a message that has not been sent by then, so an old one-time code is never sent late.

Idempotency and your own ids#

Send an Idempotency-Key on every send so a retry cannot send twice; the next page has the rules. reference_id is your own id for the message, unique per account forever. Reusing one answers 409 REFERENCE_ID_EXISTS with the existing message in details.message, which makes it a second safety net, but it does not replace the idempotency key.

Send many#

POST /v1/messages/batch takes up to 1,000 messages and optional defaults that fill any field an item leaves out. Each item gets its own result in results[], with the index of the item, so one bad number does not stop the others. A batch needs a category, in the item or in defaults. Send "dry_run": true to check a whole batch first.

{
  "defaults": {"category": "transactional"},
  "messages": [
    {"to": "+923001234567", "body": "Your order #1094 is ready.", "reference_id": "order-1094"},
    {"to": "+923007654321", "body": "Your order #1095 is ready.", "reference_id": "order-1095"}
  ]
}

Reply in a conversation#

POST /v1/conversations/{id}/messages replies on the conversation's line to its contact, so you do not repeat the sender or the recipient; it goes out through the conversation's channel while that is a healthy sender of the line. It needs messages:send and conversations:write.

Cancel and retry#

POST /v1/messages/{id}/cancel cancels a queued message, held and scheduled ones included (200, status: cancelled). A leased message is already on the phone, so the call is a request: it answers 202 with the message still leased and cancel_requested_at set, and the phone's next report decides the outcome. A message that was sent answers 409 NOT_CANCELLABLE.

POST /v1/messages/{id}/retry queues a failed or expired message again under the same id and reference_id, up to five manual retries (409 RETRY_LIMIT_REACHED). A message that failed with UNCONFIRMED may already have been delivered, so the retry needs acknowledge_possible_duplicate: true. Many failures are retried by the platform without your help; see the message lifecycle.

Warnings#

A successful answer carries warnings[]: things that did not stop the message but that you may want to act on, for example CHANNEL_OPTIONS_IGNORED when you sent options the channel does not use.