Conversations and replies

When a contact writes back, you read the conversation and reply. Replies are sent before the route answers, and the response is the stored message. That's different from POST /sms, which queues a new outbound text and reports the result later.

  • Texts from contacts arrive on the contact.msg.received webhook. A STOP reply also sends contact.msg.opt_out. See Inbound messages.
  • Website chat visitors are answered with Reply to a web chat.

Routes

Method Route Scope What it does
GET /phone/public/sms/{sms_id} contacts:read Get one stored text
GET /phone/public/sms/thread/{contact_id} contacts:read Get a contact's text thread
POST /phone/public/sms/reply sms:send Send a text or MMS reply
POST /chat/public/reply chat:write Reply to a web chat
GET /chat/public/sites chat:read List your chat sites

Get SMS record

GET /phone/public/sms/{sms_id} returns one stored message: an inbound text, or a reply sent from the inbox or this API. Texts sent with POST /sms aren't stored here.

curl https://api-v2.dropcowboy.com/phone/public/sms/8945b5d3-d4b9-435e-ab6d-a21bb5b9b628 \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
{
  "data": {
    "sms_id": "8945b5d3-d4b9-435e-ab6d-a21bb5b9b628",
    "sms_type": "inbound",
    "team_id": "3f6c2a1e-8b4d-4c7a-9e2f-5a1b3c4d6e7f",
    "contact_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
    "from": "+13125550142",
    "to": "+12125550100",
    "sms_body": "Here is the photo of the damage.",
    "created_at": 1774041600000,
    "mms_media": [
      {
        "media_id": "a9c3e8f1-4b2d-4a7c-8e9b-1c2d3e4f5a6b",
        "ext": "jpg",
        "content_type": "image/jpeg",
        "size": 184220,
        "scan_status": "clean",
        "url": "https://files.example.com/a9c3e8f1-4b2d-4a7c-8e9b-1c2d3e4f5a6b.jpg?X-Amz-Expires=86400"
      }
    ]
  },
  "meta": { "request_id": "c7238eeb-141a-492c-84e8-d71321d19b99" }
}

sms_type is inbound or outbound, and created_at is epoch milliseconds. Fields that were never set are left out.

Each file in mms_media has a url once its scan_status is clean. For an inbound file, the url is a signed link that works for 24 hours, so fetch the record again for a fresh one. For a file you sent in a reply, it's the mms.dropcowboy.com copy, which works for 3 days after sending.

An sms_id that doesn't exist, or belongs to another account, answers 404.

Get SMS thread

GET /phone/public/sms/thread/{contact_id} returns the stored messages for a contact, inbound and outbound, newest first.

Field Type Required Description
skip integer No How many to skip, 0 or more. Default 0.
limit integer No How many to return, 1 or more. Default 50.
curl "https://api-v2.dropcowboy.com/phone/public/sms/thread/5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d?limit=10" \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
{
  "data": {
    "smss": [
      {
        "sms_id": "bb8a4f80-2cd2-4048-9dca-b75cab74b974",
        "sms_type": "inbound",
        "contact_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
        "from": "+13125550142",
        "to": "+12125550100",
        "sms_body": "Thanks! Can you send more info?",
        "created_at": 1774042500000
      }
    ]
  },
  "meta": { "request_id": "ee87822c-45bb-43bb-b437-89cf458c111e" }
}

Each message has the same fields as Get SMS record. A contact with no messages, or one on another account, returns an empty smss. A skip or limit that isn't a whole number in range answers 400.

Reading the thread marks the contact's unseen messages as seen, with seen_by set to null. The response still shows each message's seen_at from before this read.

Send reply

POST /phone/public/sms/reply sends one text or MMS from a number on your account, then answers with the stored message.

Field Type Required Description
phone_number string Yes Recipient in E.164. If no contact has this number, one is created.
caller_id string Yes The sender. Must be a texting-enabled number on your account.
sms_body string Unless media is sent The text, or the caption for media. Up to 1,600 characters.
media_urls string[] No https URLs to send as MMS. The rules are in MMS.
media_ids string[] No Media API ids to send as MMS
contact_id string No The contact to record the message against
curl -X POST https://api-v2.dropcowboy.com/phone/public/sms/reply \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+13125550142",
    "caller_id": "+12125550100",
    "sms_body": "Here is the floor plan you asked about.",
    "media_urls": ["https://files.example.com/floor-plan.png"]
  }'
{
  "data": {
    "sms_id": "f3fe47ad-c169-4f00-8993-68025a9195a1",
    "sms_type": "outbound",
    "contact_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
    "from": "+12125550100",
    "to": "+13125550142",
    "sms_body": "Here is the floor plan you asked about.",
    "parts": 1,
    "delivered": true,
    "reason": "delivered",
    "media_urls": [
      "https://mms.dropcowboy.com/3f6c2a1e-8b4d-4c7a-9e2f-5a1b3c4d6e7f/0b7c9e2d-4f1a-5d3b-9e6c-2a8f4b1d7c35.png"
    ]
  },
  "meta": { "request_id": "78af89a4-2fcb-4956-bd6f-5053d50869d5" }
}

The example is trimmed; the stored message also lists its files under mms_media. delivered: true means the carrier accepted the message, not that the phone received it. The links in media_urls stop working 3 days after sending. A sent reply also fires contact.msg.sent.

Files are fetched and scanned for malware before the route answers. The scan waits up to 12 seconds, then the route answers 503. A retry of the same file on the same UTC day reuses the stored copy and its finished scan.

Errors

Each call sends a new message, so check the status before you retry.

Status type ends with Cause What to do
400 invalid-parameters Bad media_urls or media_ids, more than 10 files, or a media_id that isn't found Fix the fields.
400 server-error A missing field, a number on your do-not-contact list, a known litigator, or no usable sender. detail says which. Fix the request. Don't retry a do-not-contact refusal.
402 server-error The account is past due Update billing, then retry.
403 server-error The account is inactive Reactivate the account.
409 server-error It's outside the contact's calling hours Retry inside the window.
413 payload-too-large A file is over 1 MiB, or the files total over 5 MiB Send smaller files.
415 unsupported-media-type A file isn't JPEG, PNG, GIF, WAV or MP3 Convert the file.
422 media-fetch-failed A URL didn't answer 200 in time, or redirected Retry once the URL answers 200 without a redirect.
422 media-blocked The malware scan flagged a file Don't retry.
500 server-error Storing a file failed Retry later.
502 server-error The malware scan failed Retry later.
503 media-scan-pending The scan didn't finish in time Retry after the Retry-After seconds.

Reply to a web chat

POST /chat/public/reply posts a message into a live website chat. The visitor sees it in the chat widget, and your team sees it in the shared inbox as an automation reply. Your plan needs chat on at least one seat.

Field Type Required Description
conversation_id string Yes From the chat automation triggers, such as "Chat Received"
message string Yes The reply. Markdown is allowed.
sender_name string No The name the visitor sees. Default Automation.
metadata object No Saved with the message
curl -X POST https://api-v2.dropcowboy.com/chat/public/reply \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "2b7d9f1a-4c6e-4e8a-b0d2-6f8a1c3e5b79",
    "message": "Thanks for reaching out! A teammate will follow up within the hour.",
    "sender_name": "Example Dental"
  }'
{
  "data": {
    "message_id": "6e8a0c2d-4f1b-4a3c-9e5d-7b9f1d3a5c28",
    "conversation_id": "2b7d9f1a-4c6e-4e8a-b0d2-6f8a1c3e5b79"
  },
  "meta": { "request_id": "0c2e4a6b-8d1f-4b3e-a5c7-9e1b3d5f7a41" }
}

Each call posts a new message, and this route takes no Idempotency-Key. Retrying after a success posts the reply twice, so retry only on an error.

Chat reply errors

Status type ends with Cause What to do
400 validation-error conversation_id or message is missing Add it.
402 payment-required The account is past due Update billing, then retry.
403 addon_not_enabled Your plan has no chat seat Add chat to a seat.
403 account_inactive The account is inactive Reactivate the account.
404 not-found No conversation with that id on your account Check conversation_id.

See Responses, errors and limits for everything else.

List chat sites

GET /chat/public/sites lists the websites with your chat widget. Use chat_site_id wherever a chat site is asked for, such as in chat automations.

Field Type Required Description
search_term string No Match on the site name
limit integer No How many to return
curl https://api-v2.dropcowboy.com/chat/public/sites \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
{
  "data": [
    { "chat_site_id": "1c5e9a3f-7b2d-4f8e-a6c4-3d9b7f1e5a20", "site_name": "Example Dental" }
  ],
  "meta": { "request_id": "4a8c2e6f-1b3d-4e7a-9c5f-8d2b6e4a1c37" }
}

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.

Get SMS record
curl https://api-v2.dropcowboy.com/phone/public/sms/8945b5d3-d4b9-435e-ab6d-a21bb5b9b628 \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
Get SMS thread
curl "https://api-v2.dropcowboy.com/phone/public/sms/thread/5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d?limit=10" \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"
Send reply
curl -X POST https://api-v2.dropcowboy.com/phone/public/sms/reply \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+13125550142",
    "caller_id": "+12125550100",
    "sms_body": "Here is the floor plan you asked about.",
    "media_urls": ["https://files.example.com/floor-plan.png"]
  }'
Reply to a web chat
curl -X POST https://api-v2.dropcowboy.com/chat/public/reply \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "2b7d9f1a-4c6e-4e8a-b0d2-6f8a1c3e5b79",
    "message": "Thanks for reaching out! A teammate will follow up within the hour.",
    "sender_name": "Example Dental"
  }'
List chat sites
curl https://api-v2.dropcowboy.com/chat/public/sites \
  -H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET"