API reference
Messages
List and read messages, and send new messages (queued for delivery).
x-tenant-id and an API key or OAuth token. Channel IDs are copied from Settings → Developer in the app. See Authentication.List messages
/api/public/v1/messagesReturns a paginated, filterable, sortable list of messages for the authenticated tenant.
Query parameters
pageintegerDefault
1limitintegerDefault
25sortstringtimestampcreatedAtDefault
"timestamp"orderstringascdescDefault
"desc"conversationIdstringcontactIdstringstatusstringqueuedprocessingreceivedsentdeliveredreadfailedtypestringMessage type (case-insensitive), e.g. text, image, template.
directionstringinboundoutboundfromstring (date-time)tostring (date-time)fieldsstring
Response 200Paginated list of messages
successbooleandataobject[]17 child fields
idstringconversationIdstring | nullcontactIdstring | nullchannelIdstring | nulldirectionstring | nullOne of
inboundoutboundnulltypestring | nullbodystring | nullstatusstringOne of
queuedsentdeliveredreadfailedfailureReasonstring | nulltimestampstring (date-time) | nullreplyContextobject | null3 child fields
messageIdstring | nullpreviewstring | nullfromstring | null
mediaobject | null7 child fields
typestring | nullmimeTypestring | nullfilenamestring | nullcaptionstring | nullsizenumber | nulldownloadStatusstring | nullready (file stored, use downloadUrl), pending (inbound file still downloading), failed, or unavailable (e.g. sent by URL).
downloadUrlstring | nullPublic API path to download the file (same auth headers). Null until the file is stored.
replyobject | nullInbound only: the option a customer picked on an interactive button or list message.
4 child fields
typestring | nullOne of
button_replylist_replynullidstring | nullThe id you set on the button / row.
titlestring | nulldescriptionstring | null
reactionobject | nulltemplateobject | null4 child fields
namestring | nulllanguagestring | nullcategorystring | nullparametersany
createdAtstring (date-time)updatedAtstring (date-time)
paginationobject8 child fields
pageintegerlimitintegertotalintegertotalPagesintegerhasNextPagebooleanhasPrevPagebooleannextCursorstring | nullprevCursorstring | null
metaobject1 child field
apiVersionstring
requestIdstring | nulltimestampstring (date-time)
Errors
400Malformed request or missing tenant context401Missing or invalid API key403Forbidden - developer platform disabled, key expired/disabled, or tenant mismatch422Validation failed (invalid body, unapproved template, missing variables, unsupported type)429Too many requests - rate limit exceeded500Unexpected server error
curl -X GET "https://api.inaiy.com/api/public/v1/messages" \
-H "x-tenant-id: demo" \
-H "x-api-key: wp_live_xxxxxxxxxxxxxxxxxxxx"{
"success": true,
"data": [
{
"id": "6650a1b2c3d4e5f6a7b8c9d0",
"conversationId": "664fa1b2c3d4e5f6a7b8c9d0",
"contactId": "663fa1b2c3d4e5f6a7b8c9d0",
"channelId": "662fa1b2c3d4e5f6a7b8c9d0",
"direction": "outbound",
"type": "TEXT",
"body": "Hello from the Public API!",
"status": "delivered",
"failureReason": null,
"timestamp": "2026-07-12T09:30:00.000Z",
"replyContext": null,
"media": null,
"reaction": null,
"template": null,
"createdAt": "2026-07-12T09:30:00.000Z",
"updatedAt": "2026-07-12T09:31:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 25,
"total": 87,
"totalPages": 4,
"hasNextPage": true,
"hasPrevPage": false,
"nextCursor": null,
"prevCursor": null
},
"meta": {
"apiVersion": "v1",
"sort": {
"field": "timestamp",
"order": "desc"
}
},
"requestId": "a1f4c2e0-9b6d-4a1e-8c3f-2d7e5b9a0c11",
"timestamp": "2026-07-12T09:30:00.000Z"
}Send a message (queued)
/api/public/v1/messagesQueues a text, media, interactive, or template message for asynchronous delivery via the messaging engine. Returns 202 Accepted with the queued message id. Message sending is never synchronous.
Request bodyrequired
typestringrequiredOne of
textimagevideoaudiodocumenttemplatereactioninteractivechannelIdstringrequiredChannel to send from. Copy it from Settings → Developer → Channels in the inaiy app.
contactIdstringExisting contact id. Required unless phone, igsid, or psid is provided.
phonestringE.164 phone (e.g. +919123577452). WhatsApp only. Used when contactId is omitted.
igsidstringNumeric Instagram scoped user id. Instagram only. The contact must already exist.
psidstringNumeric Page-scoped id of a Messenger contact. Messenger only. The contact must already exist and have messaged this Page.
contactNamestringOptional display name when createContactIfMissing creates a contact.
createContactIfMissingbooleanWhen true and phone is provided, create the contact if it does not exist.
Default
falseconversationIdstringOptional; auto-resolved when omitted.
textstringRequired for type=text (max 4096 chars).
mediaobjectRequired for image/video/audio/document. Provide storageKey or url.
4 child fields
storageKeystringMedia upload storageKey from POST /media/upload.
urlstringPublic https URL of the file (max 2000 chars). Meta downloads it at send time. `link` is accepted as an alias.
filenamestringDocument filename shown to the customer (max 255).
captionstringCaption for image, video, and document (max 1024). Ignored for audio.
interactiveobjectRequired for type=interactive. WhatsApp Cloud API `interactive` object; interactive.type is button, list, or cta_url. button: action.buttons (1-3) of { type: "reply", reply: { id, title<=20 } }. list: action.button (<=20) and action.sections (1-10) of { title, rows: [{ id, title<=24, description<=72 }] }. cta_url: action.name "cta_url" and action.parameters { display_text<=20, url }. body.text (<=1024) is required; header (text, or image/video/document by link) and footer.text (<=60) are optional. The customer's pick arrives as an inbound message with `reply.id`.
5 child fields
typestringOne of
buttonlistcta_urlheaderobjectbodyobject1 child field
textstring
footerobject1 child field
textstring
actionobject
replyToMessageIdstringMessage id to reply to (text/media/interactive only).
emojistringReserved for type=reaction. Reactions are not supported yet and return 422.
targetMessageIdstringReserved for type=reaction (the message being reacted to). Reactions are not supported yet and return 422.
metadataobjectArbitrary external metadata (text/media only).
templateobjectRequired for type=template.
3 child fields
namestringlanguagestringparametersobjectSend-time parameters. Shape depends on the template type (standard, authentication, carousel, limited_time_offer).
6 child fields
bodyanyPositional array or named object of body variables.
headerobjectHeader variables: { text } for text headers, { storageKey } / { link } / { id } for media headers.
buttonsobject[]5 child fields
indexintegertextstringDynamic URL button suffix.
couponCodestringCopy-code / coupon button value.
payloadstringDynamic quick-reply payload.
flowTokenstringFlow button token.
codestringOne-time code for authentication templates.
cardsobject[]Per-card parameters for carousel templates.
4 child fields
cardIndexintegerheaderobjectbodyanyPositional array or named object of card body variables.
buttonsobject[]
limitedTimeOfferobject1 child field
expirationTimeMsintegerOffer expiration timestamp in ms.
Response 202Message accepted and queued
successbooleandataobject4 child fields
idstring | nullstatusstringconversationIdstring | nulltypestring
metaobject1 child field
apiVersionstring
requestIdstring | nulltimestampstring (date-time)
Errors
400Malformed request or missing tenant context401Missing or invalid API key403Forbidden - developer platform disabled, key expired/disabled, or tenant mismatch404Message, conversation, contact, or template not found409Conflicting state (e.g. conversation not sendable)422Validation failed (invalid body, unapproved template, missing variables, unsupported type)429Too many requests - rate limit exceeded500Unexpected server error
curl -X POST "https://api.inaiy.com/api/public/v1/messages" \
-H "x-tenant-id: demo" \
-H "x-api-key: wp_live_xxxxxxxxxxxxxxxxxxxx"{
"type": "text",
"channelId": "662fa1b2c3d4e5f6a7b8c9d0",
"contactId": "663fa1b2c3d4e5f6a7b8c9d0",
"text": "Hello from the Public API!"
}{
"success": true,
"data": {
"id": "6650a1b2c3d4e5f6a7b8c9d0",
"status": "queued",
"conversationId": "664fa1b2c3d4e5f6a7b8c9d0",
"type": "text"
},
"meta": {
"apiVersion": "v1",
"queued": true
},
"requestId": "a1f4c2e0-9b6d-4a1e-8c3f-2d7e5b9a0c11",
"timestamp": "2026-07-12T09:30:00.000Z"
}Get a message by id
/api/public/v1/messages/{id}Fetch a single message by its public id.
Path parameters
idstringrequired24-char message ObjectId
Query parameters
fieldsstringComma-separated list of fields to return (sparse fieldset).
Response 200The message
successbooleandataobject17 child fields
idstringconversationIdstring | nullcontactIdstring | nullchannelIdstring | nulldirectionstring | nullOne of
inboundoutboundnulltypestring | nullbodystring | nullstatusstringOne of
queuedsentdeliveredreadfailedfailureReasonstring | nulltimestampstring (date-time) | nullreplyContextobject | null3 child fields
messageIdstring | nullpreviewstring | nullfromstring | null
mediaobject | null7 child fields
typestring | nullmimeTypestring | nullfilenamestring | nullcaptionstring | nullsizenumber | nulldownloadStatusstring | nullready (file stored, use downloadUrl), pending (inbound file still downloading), failed, or unavailable (e.g. sent by URL).
downloadUrlstring | nullPublic API path to download the file (same auth headers). Null until the file is stored.
replyobject | nullInbound only: the option a customer picked on an interactive button or list message.
4 child fields
typestring | nullOne of
button_replylist_replynullidstring | nullThe id you set on the button / row.
titlestring | nulldescriptionstring | null
reactionobject | nulltemplateobject | null4 child fields
namestring | nulllanguagestring | nullcategorystring | nullparametersany
createdAtstring (date-time)updatedAtstring (date-time)
metaobject1 child field
apiVersionstring
requestIdstring | nulltimestampstring (date-time)
Errors
400Malformed request or missing tenant context401Missing or invalid API key403Forbidden - developer platform disabled, key expired/disabled, or tenant mismatch404Message, conversation, contact, or template not found429Too many requests - rate limit exceeded500Unexpected server error
curl -X GET "https://api.inaiy.com/api/public/v1/messages/6650a1b2c3d4e5f6a7b8c9d0" \
-H "x-tenant-id: demo" \
-H "x-api-key: wp_live_xxxxxxxxxxxxxxxxxxxx"{
"success": true,
"data": {
"id": "6650a1b2c3d4e5f6a7b8c9d0",
"conversationId": "664fa1b2c3d4e5f6a7b8c9d0",
"contactId": "663fa1b2c3d4e5f6a7b8c9d0",
"channelId": "662fa1b2c3d4e5f6a7b8c9d0",
"direction": "outbound",
"type": "TEXT",
"body": "Hello from the Public API!",
"status": "delivered",
"failureReason": null,
"timestamp": "2026-07-12T09:30:00.000Z",
"replyContext": null,
"media": null,
"reaction": null,
"template": null,
"createdAt": "2026-07-12T09:30:00.000Z",
"updatedAt": "2026-07-12T09:31:00.000Z"
},
"meta": {
"apiVersion": "v1"
},
"requestId": "a1f4c2e0-9b6d-4a1e-8c3f-2d7e5b9a0c11",
"timestamp": "2026-07-12T09:30:00.000Z"
}Download message media
/api/public/v1/messages/{id}/mediaStreams the file of an inbound or outbound media message (image, video, audio, document, sticker). Check `media.downloadStatus` on the message first: 409 MEDIA_NOT_READY means the inbound file is still downloading; 404 MEDIA_NOT_FOUND means the message has no stored file (e.g. media sent by URL). Pass inline=1 to get Content-Disposition: inline.
Path parameters
idstringrequired24-char message ObjectId
Query parameters
inlinestringSet to 1/true for inline Content-Disposition.
Response 200Binary file stream with Content-Type set to the media MIME type.
Errors
400Malformed request or missing tenant context401Missing or invalid API key403Forbidden - developer platform disabled, key expired/disabled, or tenant mismatch404Message, conversation, contact, or template not found409Conflicting state (e.g. conversation not sendable)429Too many requests - rate limit exceeded500Unexpected server error
curl -X GET "https://api.inaiy.com/api/public/v1/messages/6650a1b2c3d4e5f6a7b8c9d0/media" \
-H "x-tenant-id: demo" \
-H "x-api-key: wp_live_xxxxxxxxxxxxxxxxxxxx"stringList messages in a conversation
/api/public/v1/conversations/{conversationId}/messagesReturns a paginated, filterable list of messages within a specific conversation.
Path parameters
conversationIdstringrequired24-char conversation ObjectId
Query parameters
pageintegerDefault
1limitintegerDefault
25sortstringtimestampcreatedAtDefault
"timestamp"orderstringascdescDefault
"desc"conversationIdstringcontactIdstringstatusstringqueuedprocessingreceivedsentdeliveredreadfailedtypestringMessage type (case-insensitive), e.g. text, image, template.
directionstringinboundoutboundfromstring (date-time)tostring (date-time)fieldsstring
Response 200Paginated list of messages
successbooleandataobject[]17 child fields
idstringconversationIdstring | nullcontactIdstring | nullchannelIdstring | nulldirectionstring | nullOne of
inboundoutboundnulltypestring | nullbodystring | nullstatusstringOne of
queuedsentdeliveredreadfailedfailureReasonstring | nulltimestampstring (date-time) | nullreplyContextobject | null3 child fields
messageIdstring | nullpreviewstring | nullfromstring | null
mediaobject | null7 child fields
typestring | nullmimeTypestring | nullfilenamestring | nullcaptionstring | nullsizenumber | nulldownloadStatusstring | nullready (file stored, use downloadUrl), pending (inbound file still downloading), failed, or unavailable (e.g. sent by URL).
downloadUrlstring | nullPublic API path to download the file (same auth headers). Null until the file is stored.
replyobject | nullInbound only: the option a customer picked on an interactive button or list message.
4 child fields
typestring | nullOne of
button_replylist_replynullidstring | nullThe id you set on the button / row.
titlestring | nulldescriptionstring | null
reactionobject | nulltemplateobject | null4 child fields
namestring | nulllanguagestring | nullcategorystring | nullparametersany
createdAtstring (date-time)updatedAtstring (date-time)
paginationobject8 child fields
pageintegerlimitintegertotalintegertotalPagesintegerhasNextPagebooleanhasPrevPagebooleannextCursorstring | nullprevCursorstring | null
metaobject1 child field
apiVersionstring
requestIdstring | nulltimestampstring (date-time)
Errors
400Malformed request or missing tenant context401Missing or invalid API key403Forbidden - developer platform disabled, key expired/disabled, or tenant mismatch404Message, conversation, contact, or template not found429Too many requests - rate limit exceeded500Unexpected server error
curl -X GET "https://api.inaiy.com/api/public/v1/conversations/6650a1b2c3d4e5f6a7b8c9d0/messages" \
-H "x-tenant-id: demo" \
-H "x-api-key: wp_live_xxxxxxxxxxxxxxxxxxxx"{
"success": true,
"data": [
{
"id": "6650a1b2c3d4e5f6a7b8c9d0",
"conversationId": "664fa1b2c3d4e5f6a7b8c9d0",
"contactId": "663fa1b2c3d4e5f6a7b8c9d0",
"channelId": "662fa1b2c3d4e5f6a7b8c9d0",
"direction": "outbound",
"type": "TEXT",
"body": "Hello from the Public API!",
"status": "delivered",
"failureReason": null,
"timestamp": "2026-07-12T09:30:00.000Z",
"replyContext": null,
"media": null,
"reaction": null,
"template": null,
"createdAt": "2026-07-12T09:30:00.000Z",
"updatedAt": "2026-07-12T09:31:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 25,
"total": 87,
"totalPages": 4,
"hasNextPage": true,
"hasPrevPage": false,
"nextCursor": null,
"prevCursor": null
},
"meta": {
"apiVersion": "v1",
"sort": {
"field": "timestamp",
"order": "desc"
}
},
"requestId": "a1f4c2e0-9b6d-4a1e-8c3f-2d7e5b9a0c11",
"timestamp": "2026-07-12T09:30:00.000Z"
}