Messages API
Send messages to your AI agents programmatically via channels.
Overview
Use your channel to send messages programmatically. The channel identifier routes messages to the configured default agent automatically.
Required Fields
| Field | Description |
|---|---|
content | The message text |
fromChannelIdentifier | Your channel's identifier (routes to the default agent) |
Optional Fields
| Field | Description |
|---|---|
sessionId | Group messages in a conversation (auto-generated if omitted) |
sessionName | Display name for the chat session (also used as CRM card title) |
externalUserId | Your customer's identifier for tracking |
attachments | Array of file attachments |
isEcho | Set to true to sync messages from external systems without AI processing |
agentId | Target agent ID — triggers session transfer if different from current |
kanbanId | Target kanban ID — triggers session transfer if different from current |
Send Message
POST /messagesRequest Parameters
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes* | The message text (*or attachments required) |
fromChannelIdentifier | string | Yes | Your channel's identifier |
channelType | string | No | Channel type (see table below). Defaults to api |
sessionId | string | No | Conversation ID (auto-generated if omitted) |
sessionName | string | No | Display name for the session |
externalUserId | string | No | Your customer's identifier |
attachments | array | No | File attachments (see File Attachments) |
isEcho | bool | No | Set true to sync without AI processing |
agentId | string | No | Target agent ID — triggers a session transfer if different from current |
kanbanId | string | No | Target kanban ID — triggers a session transfer if different from current |
Channel Types
The channelType field determines how the fromChannelIdentifier is routed. You must pass the correct type matching your channel, otherwise routing will fail.
| Value | Description |
|---|---|
api | API channel (default) |
whatsapp_byot | WhatsApp Custom App (Bring Your Own Token) |
whatsapp_no | WhatsApp Non-Official (QR Code) |
whatsapp | WhatsApp Cloud API (OAuth) |
web | Web chat widget |
telegram | Telegram bot |
sms | SMS channel |
Channel Type is Required for Routing
If fromChannelIdentifier is provided, the channelType must match the type of the channel you created. For example, a WhatsApp QR Code channel requires channelType: "whatsapp_no", and a WhatsApp Custom App channel requires channelType: "whatsapp_byot". Omitting channelType defaults to "api" and will fail to find non-API channels.
Basic Request
curl -X POST https://api.maiacompany.io/messages \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "Hello, I need help!",
"fromChannelIdentifier": "YOUR_CHANNEL_IDENTIFIER",
"externalUserId": "customer-123",
"sessionId": "YOUR_SESSION_ID",
"sessionName": "Customer Support Chat"
}'Response
Returns 202 Accepted. The AI processes the message asynchronously and delivers the response via webhook.
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"isNewSession": true,
"userMessage": {
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"role": "user",
"content": "Hello, I need help!",
"timestamp": 1704067200000
},
"message": "Message received. AI response will be delivered via webhook."
}File Attachments
Step 1: Request Upload URL
Request a presigned upload URL from S3.
curl -X POST https://api.maiacompany.io/messages/upload \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fileName": "photo.jpg",
"contentType": "image/jpeg",
"size": 1024000
}'Response
{
"uploadUrl": "https://s3.amazonaws.com/...",
"fileKey": "attachments/acc123/2024/12/uuid-photo.jpg",
"expiresAt": 1703001234567
}Step 2: Upload File to S3
Upload the file using the presigned URL from Step 1.
curl -X PUT "UPLOAD_URL_FROM_RESPONSE" \
-H "Content-Type: image/jpeg" \
--data-binary @photo.jpgStep 3: Get Download URL (Optional)
Generate a presigned download URL for a previously uploaded file. This is useful when you need a direct URL to the file — for example, when sending WhatsApp Template messages with media headers.
curl "https://api.maiacompany.io/attachments/attachments/acc123/2024/12/uuid-photo.jpg" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response
{
"downloadUrl": "https://s3.amazonaws.com/...",
"expiresAt": 1703005200000,
"contentType": "image/jpeg",
"size": 1024000
}| Field | Type | Description |
|---|---|---|
downloadUrl | string | Presigned S3 URL for downloading the file (1h expiry) |
expiresAt | number | URL expiration timestamp (milliseconds) |
contentType | string | MIME type of the file |
size | number | File size in bytes |
TIP
The download URL expires after 1 hour. Request a new one if needed.
Step 4: Send Message with Attachment
Include the fileKey in your message.
curl -X POST https://api.maiacompany.io/messages \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "Here is the document you requested",
"fromChannelIdentifier": "YOUR_CHANNEL_IDENTIFIER",
"externalUserId": "customer-123",
"sessionId": "YOUR_SESSION_ID",
"attachments": [
{
"fileKey": "attachments/acc123/2024/12/uuid-photo.jpg",
"fileName": "photo.jpg",
"contentType": "image/jpeg",
"size": 1024000
}
]
}'Send Voice Message
Voice messages are sent as audio attachments. Audio files are automatically converted to OGG Opus for WhatsApp.
curl -X POST https://api.maiacompany.io/messages \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "",
"fromChannelIdentifier": "YOUR_CHANNEL_IDENTIFIER",
"externalUserId": "customer-123",
"sessionId": "YOUR_SESSION_ID",
"attachments": [
{
"fileKey": "attachments/acc123/2024/12/uuid-voice.ogg",
"fileName": "voice-message.ogg",
"contentType": "audio/ogg",
"size": 256000
}
]
}'Supported File Types
| Category | Formats | Max Size |
|---|---|---|
| Images | JPEG, PNG, GIF, WebP | 10 MB |
| Audio | MP3, WAV, OGG, M4A, WebM, AAC, AMR | 25 MB |
| Video | MP4, WebM, MOV | 50 MB |
| Documents | PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT, CSV | 25 MB |
Audio Conversion
Audio files are automatically converted to OGG Opus format for WhatsApp compatibility.
Limits
- Maximum 10 attachments per message
- Total size limit: 100 MB per message
Echo Messages
Use isEcho: true to sync messages from your external system into MAIA without triggering AI processing. Echo messages appear in the conversation as if sent by the customer/human, keeping the chat history complete.
When to Use Echo
- Syncing customer messages received on external platforms (WhatsApp, etc.)
- Maintaining complete conversation history in MAIA
- Messages that should appear as customer messages but not trigger AI responses
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes* | The message text (*or attachments required) |
fromChannelIdentifier | string | Yes | Your channel's identifier |
externalUserId | string | Yes | Your customer's identifier |
isEcho | bool | Yes | Must be true for echo messages |
channelType | string | No | Channel type (see Channel Types). Defaults to api |
sessionId | string | No | Conversation ID (auto-generated if omitted) |
attachments | array | No | File attachments |
Echo Request
curl -X POST https://api.maiacompany.io/messages \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "Customer message from WhatsApp",
"fromChannelIdentifier": "YOUR_CHANNEL_IDENTIFIER",
"externalUserId": "customer-123",
"sessionId": "YOUR_SESSION_ID",
"isEcho": true
}'Response
Returns 200 OK (not 202 Accepted) since no async processing occurs.
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"isNewSession": false,
"userMessage": {
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"role": "user",
"content": "Customer message from WhatsApp",
"timestamp": 1704067200000
},
"message": "Message synced successfully."
}Required Fields for Echo
Echo messages require both fromChannelIdentifier and externalUserId for proper channel routing and CRM linking.
Message Status & Reactions
Sync message delivery status and reactions from your external system back to MAIA.
Integration Flow
Link Provider Message ID
After receiving our webhook, link your external message ID with our messageId.
curl -X POST "https://api.maiacompany.io/sessions/{sessionId}/messages/{messageId}/link" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"providerMsgId": "whatsapp_msg_123",
"setStatusToSent": true
}'Update Message Status
Update delivery status using the providerMsgId.
curl -X POST "https://api.maiacompany.io/sessions/{sessionId}/messages/status" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"providerMsgId": "whatsapp_msg_123",
"status": "delivered"
}'Status values: sent | delivered | read | failed
Sync Reactions (from external)
Sync reactions from your external system when customers react to messages.
curl -X POST "https://api.maiacompany.io/sessions/{sessionId}/messages/reactions" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"providerMsgId": "whatsapp_msg_123",
"action": "upsert",
"emoji": "👍"
}'Action values:
| Action | Description |
|---|---|
add | Add a reaction |
remove | Remove a reaction |
upsert | Clear all reactions, then add emoji (if provided). Omit emoji to clear all. |
Session Management
The sessionId groups messages into a conversation. Use the same sessionId for follow-up messages from the same customer.
- You can generate any unique string (e.g., UUID, customer ID + timestamp)
- If omitted, a new session will be created automatically
TIP
Use sessionName to set a display name for the chat session. This is also used as the CRM card title.
Session Transfer via Message
You can trigger an implicit session transfer by including agentId and/or kanbanId in your POST /messages request. When the provided values differ from the session's current agent or kanban, the session is automatically transferred before the message is processed.
How It Works
- Send a message with
agentIdand/orkanbanIdin the request body - If the session already exists and the provided agent/kanban differs from the current one, a transfer is triggered
- A system message is recorded in the chat history documenting the transfer
- If the kanban changes, a new CRM record is created on the target kanban
Transfer Request
curl -X POST https://api.maiacompany.io/messages \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "Route this to the sales team",
"fromChannelIdentifier": "YOUR_CHANNEL_IDENTIFIER",
"externalUserId": "customer-123",
"sessionId": "EXISTING_SESSION_ID",
"agentId": "TARGET_AGENT_ID",
"kanbanId": "TARGET_KANBAN_ID"
}'Transfer Scenarios
| Params Provided | Behavior |
|---|---|
agentId only | Agent is set explicitly; kanbanId is resolved from the channel's agent-kanban config |
kanbanId only | Kanban is set explicitly; agentId is resolved from the channel's default agent |
Both agentId + kanbanId | Both are set explicitly — full control over the transfer target |
| Neither | No transfer — message is routed to the session's current agent |
TIP
Transfer only triggers on existing sessions. For new sessions (first message), the agentId and kanbanId simply set the initial assignment.
Channel Configuration Required
The target agent must be in the channel's allowedAgentIds. For automatic kanban resolution, configure agentKanbanConfigs on the channel.
AI Pause / Unpause
Control whether the AI responds to messages in a session. When paused, incoming messages are stored but the AI won't generate responses until resumed.
Pause or Resume AI
PATCH /sessions/{sessionId}/ai-pausedcurl -X PATCH "https://api.maiacompany.io/sessions/{sessionId}/ai-paused" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"aiPaused": true
}'Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
aiPaused | boolean | Yes | true to pause AI, false to resume AI |
Response:
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"aiPaused": true,
"message": "AI responses paused for this session"
}Webhook Notification
When the AI is paused or resumed, an ai_pause webhook event is sent. See CRM Webhooks for details.
Use Cases
- Human takeover: Pause AI when a human agent needs to handle the conversation
- After-hours support: Pause AI during business hours for human agents
- Escalation handling: Pause AI when complex issues require human intervention
Error Responses
| Status | Description |
|---|---|
400 | Bad request - invalid parameters |
401 | Unauthorized - invalid or missing token |
404 | Resource not found |
429 | Rate limit exceeded |
500 | Server error |
Example Error
{
"error": "Bad Request",
"message": "content or attachments required"
}