Overview
The multi-type ingestion endpoint allows you to send various types of conversation data to Moda, not just LLM chat messages. This is useful for:
- Chat/messaging platforms (Slack, Discord, etc.)
- Customer support tools with tool/function calls
- Email thread analytics
- Call center transcripts and voice analytics
All data types flow through the standard Moda analytics pipeline, including embeddings, evaluations, and clustering.
Endpoint
POST https://moda-ingest.modas.workers.dev/v1/ingest/multi
Authentication
Include your Moda API key in the Authorization header:
-H "Authorization: Bearer YOUR_MODA_API_KEY"
Message types
Channel messages
Text messages from chat platforms or messaging systems.
{
"messageType": "channel",
"id": "msg-123",
"conversationId": "thread-456",
"message": "Hello, how can I help you today?",
"role": "assistant",
"userId": "user-789",
"timestamp": "2025-01-04T12:00:00Z"
}
| Field | Type | Required | Description |
|---|
messageType | string | Yes | Must be "channel" |
id | string | Yes | Unique message ID |
conversationId | string | Yes | Thread/channel ID for grouping messages |
message | string | Yes | Message content |
role | string | Yes | One of: user, assistant, system |
userId | string | No | User identifier |
timestamp | string | No | ISO 8601 timestamp (defaults to now) |
metadata | object | No | Custom metadata |
Function/tool invocations with request and response data.
{
"messageType": "tool_call",
"id": "tool-123",
"conversationId": "thread-456",
"name": "search_knowledge_base",
"toolCallRequest": {
"query": "refund policy",
"limit": 5
},
"toolCallResponse": {
"results": [
{"title": "Refund Policy", "content": "..."}
]
}
}
| Field | Type | Required | Description |
|---|
messageType | string | Yes | Must be "tool_call" |
id | string | Yes | Unique tool call ID |
conversationId | string | Yes | Conversation ID for grouping |
name | string | Yes | Tool/function name |
toolCallRequest | any | Yes | Request payload (can be any JSON) |
toolCallResponse | any | Yes | Response payload (can be any JSON) |
userId | string | No | User identifier |
timestamp | string | No | ISO 8601 timestamp |
Emails
Email messages with threading support via inReplyTo.
{
"messageType": "email",
"id": "email-123",
"subject": "Re: Order #12345 - Shipping question",
"body": "Thank you for reaching out. Your order is scheduled to arrive...",
"inReplyTo": "email-thread-001",
"from": "support@example.com",
"to": ["customer@example.com"]
}
| Field | Type | Required | Description |
|---|
messageType | string | Yes | Must be "email" |
id | string | Yes | Unique email ID |
inReplyTo | string | Yes | Thread ID (becomes conversation_id for threading) |
body | string | Yes | Email body content |
subject | string | No | Email subject line |
from | string | No | Sender email address |
to | array | No | Recipient email addresses |
userId | string | No | User identifier |
timestamp | string | No | ISO 8601 timestamp |
The inReplyTo field is used as the conversation_id in Moda, allowing you to thread emails together. Use the same inReplyTo value for all emails in a thread.
Call transcripts
Phone or video call transcripts with speaker turns.
{
"messageType": "call",
"id": "call-123",
"conversationId": "call-session-456",
"transcript": [
{"role": "user", "content": "Hi, I'm having trouble with my account."},
{"role": "assistant", "content": "I'd be happy to help. Can you tell me more about the issue?"},
{"role": "user", "content": "I can't log in after resetting my password."}
],
"duration": 180,
"callType": "phone"
}
| Field | Type | Required | Description |
|---|
messageType | string | Yes | Must be "call" |
id | string | Yes | Unique call ID |
conversationId | string | Yes | Call session ID |
transcript | array | Yes | Array of transcript entries (see below) |
duration | number | No | Call duration in seconds |
callType | string | No | One of: phone, video, voice |
userId | string | No | User identifier |
timestamp | string | No | ISO 8601 timestamp |
Transcript entry format:
| Field | Type | Required | Description |
|---|
role | string | Yes | Speaker role (e.g., user, assistant, customer, agent) |
content | string | Yes | What was said |
timestamp | string | No | When this was said |
Call transcripts are stored as a single record containing the full transcript.
The complete transcript is included in the message field for embedding/search,
and the structured data is preserved in content_blocks for detailed analysis.
Send an array of events in the request body. You can mix different message types in a single request.
{
"events": [
{
"messageType": "channel",
"id": "msg-1",
"conversationId": "conv-123",
"message": "Hello!",
"role": "user"
},
{
"messageType": "tool_call",
"id": "tool-1",
"conversationId": "conv-123",
"name": "lookup_order",
"toolCallRequest": {"order_id": "12345"},
"toolCallResponse": {"status": "shipped"}
}
]
}
Example
curl https://moda-ingest.modas.workers.dev/v1/ingest/multi \
-H "Authorization: Bearer YOUR_MODA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"messageType": "channel",
"id": "msg-001",
"conversationId": "support-thread-123",
"message": "I need help with my subscription",
"role": "user",
"userId": "customer-456"
},
{
"messageType": "tool_call",
"id": "tool-001",
"conversationId": "support-thread-123",
"name": "get_subscription",
"toolCallRequest": {"user_id": "customer-456"},
"toolCallResponse": {"plan": "pro", "status": "active"}
},
{
"messageType": "channel",
"id": "msg-002",
"conversationId": "support-thread-123",
"message": "I can see you have an active Pro subscription. How can I help?",
"role": "assistant"
}
]
}'
Response
Success response
{
"success": true,
"count": 4,
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"details": {
"channel": 2,
"tool_call": 1,
"email": 0,
"call": 1,
"call_transcript_messages": 0
}
}
| Field | Type | Description |
|---|
success | boolean | Whether the request succeeded |
count | number | Total records created |
requestId | string | Unique request ID for debugging |
details | object | Breakdown by message type |
details.call_transcript_messages | number | Always 0 (kept for backwards compatibility) |
Error response
{
"success": false,
"count": 0,
"message": "Event 2: Channel message missing 'conversationId'",
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"retryable": false
}
Batch limits
| Limit | Value |
|---|
| Max events per request | 1,000 |
| Max message size | 100 KB |
| Max request size | 5 MB |
Error handling
| Status | Meaning | Retryable |
|---|
| 200 | Success | - |
| 400 | Invalid request format or validation error | No |
| 401 | Invalid or missing API key | No |
| 413 | Request too large | No |
| 503 | Service temporarily unavailable | Yes |
Each event is validated individually. The error message will indicate which event failed and why (e.g., “Event 2: Email missing ‘inReplyTo’”).