OdyDocs

Messages

Send SMS and MMS from your business number, and search every message body and call transcript.

Messages are activities inside a conversation. Sending through the API is exactly like a teammate typing in the app: the text goes out from your business number, appears in the thread, and the reply comes back to the same thread (and to your message.received webhook).

Before you can text

US carriers require a 10DLC registration before a local number can send texts. Until the campaign is approved, a live POST /messages returns 409 failed_precondition, and the message says exactly what's missing (not filed, in carrier review, waiting for a PIN, or rejected). Check canText with GET /messaging. Some newly filed numbers can text earlier because Ody temporarily attaches them to its own approved campaign while yours is reviewed. Test keys skip this gate, since nothing is sent.

#Send a message

POST/v1/api/messages

Sends an SMS, or an MMS when you include mediaUrls. Address it in one of two ways:

  • conversationId: reply in an existing thread, from that thread's number.
  • to: an E.164 number. Ody finds or creates the thread from your workspace number, or from from if you own that number.

Delivery is asynchronous. The response returns once the carrier has accepted the message; the final outcome arrives as message.delivered or message.failed.

With a test key (ody_test_…) the message is recorded with deliveryStatus: "simulated" and nothing is sent.

Scope: messages:send · supports Idempotency-Key

Body

  • body string required — The text. Longer messages are split into segments by the carrier.
  • conversationId uuid — Reply in this thread. Use this or to.
  • to string — Recipient in E.164. Use this or conversationId.
  • from string — One of your workspace numbers, used when starting a thread with to. Ignored for conversationId (a reply always goes out from the thread's number). Defaults to your workspace number.
  • mediaUrls array of URLs — Up to 10 publicly fetchable images or files, sent as MMS.

Returns 201 with { message: Activity, conversationId, simulated? }.

Errors: 409 when the workspace has no active number or texting isn't registered yet; 404 for an unknown conversationId.

curl https://api.ody.co/v1/api/messages \
  -H "Authorization: Bearer $ODY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: appt-7731-reminder" \
  -d '{
    "to": "+15125550123",
    "body": "Hi Ada, reminder: your appointment is tomorrow at 10am. Reply C to confirm."
  }'
Response
{
  "message": {
    "id": "5e7f…",
    "type": "message",
    "direction": "outbound",
    "body": "Hi Ada, reminder: your appointment is tomorrow at 10am. Reply C to confirm.",
    "media": [],
    "deliveryStatus": "queued",
    "createdAt": "2026-10-14T18:10:00.000Z"
  },
  "conversationId": "c4f1a2b3-5d6e-4f70-8a9b-0c1d2e3f4a5b"
}

#Search messages

GET/v1/api/messages/search

Case-insensitive search across message bodies and stored call and voicemail transcripts, newest first. It's the same retrieval the MCP search_messages tool uses.

Scope: conversations:read

Query parameters

  • q string required — Text to find.
  • limit integer — 1–100, default 25.

Returns { results: [{ activityId, conversationId, type, direction, body, participants, numberE164, createdAt }] }.

curl "https://api.ody.co/v1/api/messages/search?q=refund&limit=10" \
  -H "Authorization: Bearer $ODY_API_KEY"
Last updated Wed Oct 14 2026 00:00:00 GMT+0000 (Coordinated Universal Time)Something wrong on this page? Tell us