API reference

Messages

Send, list, get, and cancel channel-native WhatsApp and Instagram messages.

Send a message

POST/v1/messages202 Accepted

Request body

FieldTypeNotes
No fields.

Response

FieldTypeNotes
idrequiredstring
objectrequired"message"
statusrequiredstring
channelrequired"whatsapp" | "instagram" | "whatsapp_channel"
environmentrequired"sandbox" | "production"
template_idrequiredstring | null
template_version_idrequiredstring | null
template_versionrequiredinteger | null
created_atrequiredstring<ISO-8601>
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
trace_idrequiredstring

Template sends use message.type = "template". Set message.template.language to a string WhatsApp language code such as pt_BR; do not send a nested language object.

WhatsApp recipients accept the compatible recipient.id = "+E.164" form or the additive structured form with country_calling_code and national_number. Structured inputs are checked against the international numbering plan and can return invalid_phone_country_calling_code, invalid_phone_national_number, or invalid_phone_e164. Legacy id acceptance is unchanged.

{
  "channel": "whatsapp",
  "sender": { "type": "whatsapp_phone_number", "id": "pn_..." },
  "recipient": {
    "type": "phone_e164",
    "country_calling_code": "55",
    "national_number": "11903244174"
  },
  "message": { "type": "text", "text": { "body": "Olá" } }
}
{
  "channel": "whatsapp",
  "sender": { "type": "whatsapp_phone_number", "id": "pn_..." },
  "recipient": { "type": "phone_e164", "id": "+5511999999999" },
  "message": {
    "type": "template",
    "template": {
      "name": "order_update",
      "language": "pt_BR",
      "variables": { "first_name": "Ana" }
    }
  }
}

WhatsApp media accepts image, document, audio,video, and sticker. Voice notes use the audio media path:media.kind = "audio". Provide exactly one of media.id,media.link, media.asset_id, media.inline, or media.source. Use media.asset_id for single-use or library uploads created through the Media API. A media.source with type = "tts"accepts immediately, renders caller-supplied text into an audio attachment on the media-render worker, and then delivers through the normal media path; it does not invoke an agent workflow or generate the reply text.

{
  "channel": "whatsapp",
  "sender": { "type": "whatsapp_phone_number", "id": "pn_..." },
  "recipient": { "type": "phone_e164", "id": "+5511999999999" },
  "message": {
    "type": "media",
    "media": {
      "kind": "audio",
      "link": "https://cdn.example.com/voice.ogg"
    }
  }
}
{
  "channel": "whatsapp",
  "sender": { "type": "whatsapp_phone_number", "id": "pn_..." },
  "recipient": { "type": "phone_e164", "id": "+5511999999999" },
  "message": {
    "type": "media",
    "media": {
      "kind": "image",
      "asset_id": "mda_..."
    }
  }
}
{
  "channel": "whatsapp",
  "sender": { "type": "whatsapp_phone_number", "id": "pn_..." },
  "recipient": { "type": "phone_e164", "id": "+5511999999999" },
  "message": {
    "type": "media",
    "media": {
      "kind": "audio",
      "source": {
        "type": "tts",
        "provider": "openai",
        "text": "Your order is out for delivery.",
        "voice": "marin",
        "language": "en-US"
      }
    }
  }
}

Direct quick replies are WhatsApp interactive button messages. They require one to three reply buttons and must be sent inside the customer-service window.

{
  "channel": "whatsapp",
  "sender": { "type": "whatsapp_phone_number", "id": "pn_..." },
  "recipient": { "type": "phone_e164", "id": "+5511999999999" },
  "message": {
    "type": "interactive",
    "interactive": {
      "type": "button",
      "body": { "text": "Confirm delivery?" },
      "action": {
        "buttons": [
          { "type": "reply", "reply": { "id": "confirm", "title": "Confirm" } }
        ]
      }
    }
  }
}

Send a published WhatsApp Flow with message.type = "flow". Address the flow with flow.action.parameters.flow_id (the published flow's provider_flow_id, read from GET /v1/flows/{id}) or with flow.action.parameters.flow_name — provide exactly one. flow_token is your required opaque correlation token and comes back as submission.provider_token on the flow.completed webhook; flow_cta is the required button label (max 30 chars). See the Flows guide for the full create → publish → send → complete loop.

{
  "channel": "whatsapp",
  "sender": { "type": "whatsapp_phone_number", "id": "pn_..." },
  "recipient": { "type": "phone_e164", "id": "+5511999999999" },
  "message": {
    "type": "flow",
    "flow": {
      "body": { "text": "Tap below to complete your booking." },
      "action": {
        "name": "flow",
        "parameters": {
          "flow_id": "1234567890",
          "flow_token": "booking-7f3a",
          "flow_cta": "Book now"
        }
      }
    }
  }
}

Retrieve a message

GET/v1/messages/{'{id}'}

Response

FieldTypeNotes
idrequiredstring
objectrequired"message"
channelrequired"whatsapp" | "instagram" | "whatsapp_channel"
directionrequiredstring
typerequiredstring
statusrequiredstring
environmentrequired"sandbox" | "production"
senderrequiredobject
recipientrequiredobject
providerrequiredstring | null
provider_message_idrequiredstring | null
template_namerequiredstring | null
template_idrequiredstring | null
template_version_idrequiredstring | null
template_versionrequiredinteger | null
payloadrequiredunknown | null
metadatarequiredunknown | null
error_coderequiredstring | null
error_messagerequiredstring | null
provider_errorrequiredobject | null
trace_idrequiredstring
created_atrequiredstring<ISO-8601>
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
updated_atrequiredstring<ISO-8601>
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
eventsrequiredobject[]

List messages

GET/v1/messages

Query params

FieldTypeNotes
limitrequiredinteger
Maximum number of items to return. Defaults to 20; maximum 100.
range 1..100
starting_afterstring
Opaque cursor from the previous response next_cursor.
statusstring
batch_idstring
include"payload"

Response

FieldTypeNotes
objectrequired"list"
datarequiredobject[]
has_morerequiredboolean
next_cursorrequiredstring | null

List responses omit large message bodies by default: each item returns payload: null and metadata: null. Pass include=payload when an inspector or agent needs both fields.

Phone-like sender.id and recipient.id values are returned in strict +E.164 with every subscriber digit preserved. Tyxter may maintain a separate digits-only correlation key internally, but list and retrieve responses never substitute that key for the public identity. Opaque Meta phone-number ids remain unchanged.

const page = await tyxter.messages.list({
  status: 'failed',
  include: 'payload',
});

// page.data[0].payload and .metadata now contain the stored request data.

Cancel a message

POST/v1/messages/{'{id}'}/cancel

Response

FieldTypeNotes
idrequiredstring
objectrequired"message"
channelrequired"whatsapp" | "instagram" | "whatsapp_channel"
directionrequiredstring
typerequiredstring
statusrequiredstring
environmentrequired"sandbox" | "production"
senderrequiredobject
recipientrequiredobject
providerrequiredstring | null
provider_message_idrequiredstring | null
template_namerequiredstring | null
template_idrequiredstring | null
template_version_idrequiredstring | null
template_versionrequiredinteger | null
payloadrequiredunknown | null
metadatarequiredunknown | null
error_coderequiredstring | null
error_messagerequiredstring | null
provider_errorrequiredobject | null
trace_idrequiredstring
created_atrequiredstring<ISO-8601>
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
updated_atrequiredstring<ISO-8601>
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
eventsrequiredobject[]