Messages

Get Message

Get a specific message by ID.

If this message is a reply to another message, the quoted_message field will contain a preview of the original message.

Required Scopes: messages:read

get/api/v1/messages/{message_id}

Request

  • Base URL: https://api.sendseven.com
  • URL: https://api.sendseven.com/api/v1/messages/{message_id}
  • Auth: HTTP bearer

Path parameters

message_idstring required

Response

Successful Response

idstring required
conversation_idstring nullable

Conversation the message belongs to. May be null for campaign and flow messages sent to a contact that has no conversation on that channel (campaigns never create conversations; flow messages are attached once the contact replies).

tenant_idstring nullable
platformstring nullable
channel_idstring nullable
contact_idstring nullable
contact_method_idstring nullable
direction'inbound' | 'outbound' required

Message direction (lowercase to match MySQL ENUM values)

message_type'text' | 'image' | 'video' | 'audio' | 'document' | 'location' | 'contact' | 'sticker' | 'system' | 'quick_reply' | 'interactive' | 'carousel' | 'button' | 'file' | 'reaction' | 'referral' | 'whatsapp_template' | 'kb_response' | 'notification' | 'call' | 'order' required

Message content type (lowercase to match MySQL ENUM values).

MUST stay in lockstep with app.models.message.MessageType AND the MySQL ENUM column messages.message_type. Validation here is what gates the API response — any value the DB column accepts MUST be a member of this enum, or MessageResponse.model_validate raises ValidationError and the whole list endpoint 500s. Bug found 2026-05-30: prod conversations carrying button rows from a template-quick-reply click crashed GET /api/v1/messages.

Interactive message types:

  • quick_reply: Cross-platform quick reply buttons (Messenger, Instagram, Telegram)
  • interactive: WhatsApp interactive messages (buttons/lists)
  • carousel: Multi-card carousel messages
  • button: WhatsApp legacy template-button reply (callback button payload)

Adapter-level types:

  • file: Generic file attachment fallback (Messenger/Instagram unknown content_type)
  • reaction: Emoji reaction (merged into target message metadata)
  • referral: Messenger m.me deep link / ad referral event

Worker-emitted types:

  • whatsapp_template: Campaign-dispatched WhatsApp template message
  • kb_response: Team-chat AI knowledge-base response
  • notification: System notification message
fromstring nullable
tostring nullable
contact_infoobject nullable

Provider-echoed sender profile captured from the inbound channel webhook at receipt time (inbound message.received only; not a DB lookup). Fields vary by channel: WhatsApp {name, phone}, Telegram {name, username, profile_picture}, Messenger {name, profile_picture}, Instagram {name?, username, profile_picture}. NOTE: phone (WhatsApp-only) is deprecated — use data.contact_method.value for the authoritative DB-sourced identifier of the method actually used.

textstring nullable
status'pending' | 'sent' | 'delivered' | 'read' | 'failed' required

Message status (lowercase to match MySQL ENUM values)

related_message_idsstring[]

IDs of the additional messages created by the same send request, in delivery order. Non-empty only when a send carried several attachments on a channel that delivers one attachment per message — the response body itself is the FIRST message (it holds the caption and attachments[0]), and each id here is a further message carrying the next attachment. Each has its own status and its own delivery webhooks, so poll them individually via GET /api/v1/messages/{id}. Empty array for every other message.

metaobject nullable
content_formatstring

How this message's body is formatted for the send pipeline: markdown (canonical markdown, rendered per-channel) or plain (human text, passed through untouched). Derived from meta.content_format; defaults to plain for every message that did not explicitly opt into markdown.

header_media_urlstring nullable

Resolved media URL for an interactive image/video header

header_media_attachment_idstring nullable

Durable attachment id for an interactive image/video header

external_idstring nullable
reply_to_external_idstring nullable

Platform message ID of quoted message

created_atstring required
sent_atstring nullable
delivered_atstring nullable
read_atstring nullable
is_internalboolean nullable
error_messagestring nullable
error_codestring nullable

Machine-readable failure code for failed messages, e.g. 'contact_blocked' (the contact is blocked) or a provider error code.

flow_run_idstring nullable

FlowRun UUID — set when this message was sent by an automation flow. Frontends use this (together with flow_name) to render a 'Sent by flow' badge in the conversation timeline.

flow_step_idstring nullable

FlowRunStep UUID — identifies the specific Send-node step inside the flow that produced this message. Optional companion to flow_run_id.

flow_namestring nullable

Display name of the flow that sent this message. NOT stored on the message row — populated by the API layer via a single batch lookup against flow_runs JOIN flows and only set when flow_run_id is non-null.

edited_atstring nullable

When this message was last edited by the sender (real column on messages, stamped when an edit event is applied). Non-null is the ONLY signal that a message was edited — text is silently replaced in place, so without this a client cannot tell an edited message from an original one. Null for every message that was never edited (the overwhelming majority).

previous_textstring nullable

Body this message had immediately BEFORE its most recent edit. Not stored on the message row — resolved by the API layer from the newest APPLIED message_revisions row in one batched lookup per page. Null means 'edited, but the superseded body is unknown': most edits arrive before/without the message they target, so the original body was often never captured. Clients must therefore treat null as valid and fall back to a plain non-interactive 'edited' marker instead of an edit-history UI.

revision_countinteger

Number of edits that have actually been APPLIED to this message. Pending revisions (an edit whose target message we never resolved) are deliberately excluded — they describe no visible change to this row. 0 for un-edited messages; a value >1 tells the client the body was edited repeatedly, so previous_text is only the last step of a longer history.

Example response

{
  "attachments": [],
  "channel_id": "channel-123-456-789",
  "contact_id": "contact-123-456-789",
  "conversation_id": "conv-123-456-789",
  "created_at": "2025-01-15T10:30:00Z",
  "delivered_at": "2025-01-15T10:30:05Z",
  "direction": "inbound",
  "from": "+1234567890",
  "id": "msg-123-456-789",
  "message_type": "text",
  "platform": "whatsapp",
  "status": "delivered",
  "tenant_id": "tenant-123-456-789",
  "text": "Hello, I need help!"
}

Changes

No recorded changes to this operation across all 3 revisions of this API.