Texts

POST /sms sends one text to one person. Add files to send an MMS, or a template_id to send RCS. The recipient fields, foreign_id, callback_url and merge fields work the same on every send; see Sending basics. To answer someone who texted you, use Send a reply instead.

A 202 means accepted, not delivered. The result of each text arrives on the contact.sms.status webhook. Calling hours, the contact frequency limit and retries are explained in Send lifecycle. What each reason_code means, and what to do about it, is in Outcomes and what to do.

Send a text

POST /sms. With OAuth, the token needs the sms:send scope.

Field Type Required Description
body string One of body, template_id or media The message, up to 1,600 characters. With media, it's the caption. Supports merge fields.
template_id string One of body, template_id or media An RCS template built in the dashboard. Find yours with GET /template/public/templates?type=rcs.
phone_line_id string No The phone line to send from. Its numbers are the sender and its approved texting campaign is used. List lines with GET /phone/public/lines.
media_urls string[] No https URLs of files to send as MMS. See MMS.
media_ids string[] No Media API ids to send as MMS. See MMS.
curl -X POST https://api-v2.dropcowboy.com/sms \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Idempotency-Key: 0d7e3b9f-2a6c-4e1d-8f5b-7c9a3e1d5b42" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+13125550142",
    "phone_line_id": "e2b6f9a3-5c1d-4e8b-a4f7-9c3e1b5d7a28",
    "body": "Your order is ready for pickup. Reply STOP to opt out.",
    "callback_url": "https://hooks.example.com/dropcowboy/outcome"
  }'
{ "status": "queued", "message_id": "8e4a2c6f-9d1b-4f7e-b3a5-6c9e1f4d2b78" }

message_id is a receipt for the request. It doesn't appear on webhooks, and the text isn't stored for reading back. The result arrives on contact.sms.status and your callback_url; see Where results arrive.

A text without opt-out language gets Reply STOP to opt-out added to the end. Put your own opt-out wording in body to control it. A text is billed per 153 characters.

Texts aren't held for calling hours. Outside the contact's allowed hours the send fails with 4011 and isn't retried, so send again inside the window. See Calling hours.

Send from a phone line

Send from a phone_line_id whose line has an approved texting campaign. The line picks the sender number and the registered campaign. Without a phone line, we pick a sender from your account's own texting numbers. Texts never go out from numbers shared with other accounts.

Code Cause What to do
3013 phone_line_id isn't one of your phone lines Check the id with GET /phone/public/lines.
3029 The phone line has no texting campaign Attach the line to an approved texting campaign, or send from another line.
6009 Your account needs a registered brand and campaign to text Register your brand and texting campaign in the dashboard, wait for approval, then send.
3025 The line's texting campaign reached its daily carrier limit Resend after midnight Pacific time.
3026 We couldn't check the daily limit Retry.

Send RCS

With template_id, the message goes as RCS. Where the recipient's phone can't receive RCS, the template's fallback text is sent instead. If you send both body and template_id, the template wins. A template_id that isn't one of your RCS templates fails with 3999 (Other).

{
  "contact_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
  "phone_line_id": "e2b6f9a3-5c1d-4e8b-a4f7-9c3e1b5d7a28",
  "template_id": "3a7d1f9c-5e2b-4c8a-9f6d-1b4e7a2c5d93"
}

MMS

Add files with media_urls, media_ids, or both, to send an MMS. body becomes the caption and is optional. Send a reply takes media with the same rules.

{
  "contact_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
  "phone_line_id": "e2b6f9a3-5c1d-4e8b-a4f7-9c3e1b5d7a28",
  "body": "Your order is ready. Show this at pickup.",
  "media_urls": ["https://files.example.com/pickup-code.png"]
}

Choose the files

Limit Value
Files per message, across both fields 10
Size per file 1 MiB
Size per message 5 MiB
URL length 2,048 characters
Time to fetch a URL 10 seconds per file, 15 seconds in total

We accept JPEG, PNG, GIF, WAV and MP3. We read the type from the file's bytes, not from its name or Content-Type.

For media_urls, host each file at a public https URL that answers 200 without a redirect. Don't put a user name or password in the URL. We fetch the file once, when the message is sent.

media_ids take audio only, because the Media API stores audio files. Send images with media_urls. A media_id must be yours, not deleted, and fully uploaded.

Scanning

We copy each file to mms.dropcowboy.com and scan it for malware. The message goes out only when every file is clean. A scan can take up to 45 seconds; after that the send fails with 3038. Retry it: a retry of the same file on the same UTC day reuses the finished scan.

The copies are deleted after 3 days. Your media_id in the Media API isn't affected.

Caption, opt-out text and billing

  • The caption is sent exactly as written. No opt-out text is added to an MMS. If your texting campaign needs opt-out wording, put it in the caption.
  • An MMS with no caption goes out as media only.
  • An MMS is billed per file, and the caption adds nothing. Three files cost three MMS units.
  • If the phone line's texting campaign asks contacts to opt in first, a contact who hasn't opted in gets the opt-in request as a plain text without the media, billed per 153 characters. The MMS is sent, and billed per file, after they opt in.
  • MMS works to any country your account doesn't block, at that country's MMS rate.
  • You can't combine media with template_id, and you can't send media on voice routes. Both fail with 3032.

MMS results

Media is checked after the 202, so media problems arrive with the result. An MMS that went out reports campaign_type: mms. status: success means the carrier accepted the message, not that the phone displayed it.

Code Cause What to do
3032 Bad media_urls or media_ids, more than 10 files, or media combined with a template or a voice route Fix the request.
3033 A media_id isn't found Check the id with GET /media/public/media.
3034 A URL didn't answer 200 in time, or redirected Retry once the URL answers 200 without a redirect.
3035 A file, or the files together, are too large Send smaller files.
3036 Not JPEG, PNG, GIF, WAV or MP3 Convert the file.
3037 The malware scan flagged a file Don't retry.
3038 The scan didn't finish in time Retry.
3039 The scan failed Retry later.
3999 Storing a file failed Retry later.

Receive MMS

An inbound MMS arrives on contact.msg.received, with each file's id, type, size and scan status under sms.media. The event has no download link, because files are scanned after they arrive. Fetch the message with Get an SMS record a little later; each clean file has a url. See Inbound messages.

Common problems

  • The text failed with 4011. It was outside the contact's calling hours. Send it again inside the window.
  • The text failed with 3029 or 6009. Your phone line or account isn't set up for texting yet. See Send from a phone line.
  • The text failed with 6005 or 6011. The contact opted out, or there's no consent on file. Don't text them until they opt in; see Consent.
  • The text failed with 3025. The line's texting campaign hit the daily limit carriers set for it. Resend after midnight Pacific time, or send from a line on another texting campaign.
  • The text failed with 3020. It had no body, no template_id and no media.
  • An MMS failed with 3038. The malware scan took too long. Retry.

For more symptoms and fixes, see Troubleshooting. Every code is in Outcomes.


While Drop Cowboy provides tools to support compliance efforts, customers remain solely responsible for obtaining proper consent, maintaining opt-out lists, and complying with all federal and state telemarketing regulations. Consult with your legal counsel to ensure your specific use case and consent mechanisms comply with applicable laws.

Code samples

The requests from this page, ready to copy. Set DC_KEY and DC_SECRET to your API key pair first.

Send a text
curl -X POST https://api-v2.dropcowboy.com/sms \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Idempotency-Key: 0d7e3b9f-2a6c-4e1d-8f5b-7c9a3e1d5b42" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+13125550142",
    "phone_line_id": "e2b6f9a3-5c1d-4e8b-a4f7-9c3e1b5d7a28",
    "body": "Your order is ready for pickup. Reply STOP to opt out.",
    "callback_url": "https://hooks.example.com/dropcowboy/outcome"
  }'
Send an RCS text from a template
curl -X POST https://api-v2.dropcowboy.com/sms \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
    "phone_line_id": "e2b6f9a3-5c1d-4e8b-a4f7-9c3e1b5d7a28",
    "template_id": "3a7d1f9c-5e2b-4c8a-9f6d-1b4e7a2c5d93"
  }'
Send an MMS
curl -X POST https://api-v2.dropcowboy.com/sms \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
    "phone_line_id": "e2b6f9a3-5c1d-4e8b-a4f7-9c3e1b5d7a28",
    "body": "Your order is ready. Show this at pickup.",
    "media_urls": ["https://files.example.com/pickup-code.png"]
  }'