Vocatech

Recipes

Send a text

Written beside the API’s code, and read from it every hour.

Plain text

Send from a texting number on your account to a US or Canadian number. Try a dry run first: with "test": true the API checks everything and sends nothing. A dry run is never sent or billed, but it counts as a request toward the rate limits.

# 1. Dry run: checks everything, sends nothing
curl -X POST https://api.vocatech.com/v1/messages \
  -H "Authorization: Bearer $VOCATECH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "text",
    "from": "7185550100",
    "to": "7185550142",
    "message": "Hi Jordan, your appointment is confirmed for Friday at 9 AM.",
    "test": true
  }'

# 2. Send it: the same body without "test"
curl -X POST https://api.vocatech.com/v1/messages \
  -H "Authorization: Bearer $VOCATECH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "text",
    "from": "7185550100",
    "to": "7185550142",
    "message": "Hi Jordan, your appointment is confirmed for Friday at 9 AM."
  }'
Dry run response200 OK
{
  "status": "test",
  "message": {
    "mode": "api",
    "platform": "text",
    "from": "7185550100",
    "to": "7185550142",
    "name": "Vocatech Text",
    "email_recipient": null,
    "valid": true,
    "messages_counted": 1
  }
}
Send response201 Created
{
  "status": "created",
  "message": {
    "message_id": "msg_7f3a9c",
    "mode": "api",
    "platform": "text",
    "from": "7185550100",
    "to": "7185550142",
    "name": "Vocatech Text",
    "email_recipient": null,
    "message_sent": true,
    "email_sent": false,
    "created_at": "2026-09-24T09:15:02-04:00",
    "messages_counted": 1
  }
}

Check message.message_sent. A 201 with message_sent: false was taken but did not go out. messages_counted is the number of parts the text is paced and billed as.

What the status means

StatusMeansDo this
200A dry run passed (status: "test"), or a Webex number posted the text into the contact's existing conversation (status: "sent").After a dry run, send for real. Otherwise check message_sent.
201Sent (status: "created").Check message_sent.
202Queued (status: "queued"). API Messaging keys only: it goes out in order at the key's pace.Track it with GET /v1/messages/queue.
400A field is wrong: a number, the length, an attachment link.Fix it. The message names it.
403Refused: from is not a texting number on your account, the recipient opted out, or the text is over the carrier's limit.Do not retry as is.
422WhatsApp sending through the API is not available.Send WhatsApp from Webex or the portal.
429A limit was reached.Wait Retry-After seconds.
502The messaging service answered and did not send.Read the message. Retry later if it is temporary.
503The messaging service could not be reached. Nothing was sent.Safe to retry.
504The send was not confirmed. It may have gone out.Check GET /v1/messages before you retry.

Pictures and files (MMS)

Add media: up to 10 https links to files your server hosts on a public address. Every file goes out in one MMS, and an MMS counts as one message however long its text. Each link must end in one of these: jpg, jpeg, png, gif, bmp, webp, mp4, 3gp, mov, mp3, wav, amr, ogg, pdf, vcf or txt. Carriers cap MMS size, often between 500 KB and 1 MB, so keep files small.

curl -X POST https://api.vocatech.com/v1/messages \
  -H "Authorization: Bearer $VOCATECH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "text",
    "from": "7185550100",
    "to": "7185550142",
    "message": "Here is the map to our new office.",
    "media": ["https://YOUR_DOMAIN/files/office-map.png"]
  }'
Response201 Created
{
  "status": "created",
  "message": {
    "message_id": "msg_7f3a9d",
    "mode": "api",
    "platform": "text",
    "from": "7185550100",
    "to": "7185550142",
    "mms": true,
    "attachments_sent": 1,
    "mirrored_to_webex": false,
    "message_sent": true,
    "created_at": "2026-09-24T09:18:40-04:00",
    "messages_counted": 1
  }
}

Long texts

A text can run to 1,600 characters. Carriers split it into parts: 160 characters fit in one part, and 153 fit in each part once it splits. One character outside the basic set, such as an emoji or a curly quote, moves the whole text to 70 per part, 67 once it splits. messages_counted says how many parts you sent.

Delivery receipts

A 201 means the text left us. Whether the phone got it arrives later as a message.status_updated webhook. GET /v1/messages lists texts as delivered and does not carry carrier receipts.

WhatsApp

Sending WhatsApp through the API is not available. "platform": "whatsapp" answers 422, dry runs included. WhatsApp history is in GET /v1/messages, and replies go out from Webex or the portal.

Response422 Unprocessable Entity
{
  "error": {
    "code": 422,
    "type": "UNPROCESSABLE_ENTITY",
    "message": "Sending WhatsApp messages through the API is not available yet. WhatsApp history is in GET /v1/messages; reply from Webex or the portal."
  }
}