Boardy Migration Guide
This guide covers migrating from the basic /v1/ingest endpoint to the new /v1/ingest/multi endpoint for multi-channel data (emails, calls, tool calls, and chat messages).
What’s Changing
| Aspect | Old (/v1/ingest) | New (/v1/ingest/multi) |
|---|
| Endpoint | /v1/ingest | /v1/ingest/multi |
| Data types | Only chat messages | Emails, calls, tool calls, channels |
| Email threading | Not supported | inReplyTo field |
| Call transcripts | Not supported | Full transcript array |
| Tool calls | Embedded in message | First-class tool_call type |
| Environment | Not supported | environment field for dev/staging/prod |
Authentication
No change - same API key, same header:
Authorization: Bearer moda_xxx
Endpoint
POST https://moda-ingest.your-domain.workers.dev/v1/ingest/multi
Request Body
{
"environment": "production", // Optional: "development", "staging", or "production" (default)
"events": [
// Array of events - can mix types in one request
]
}
Environment
The environment field lets you differentiate data from development, staging, and production environments. This is useful for:
- Filtering analytics by environment
- Keeping dev/test data separate from production in clustering
- Debugging issues in specific environments
| Field | Type | Required | Default | Description |
|---|
environment | string | No | "production" | Deployment environment |
Valid values:
"development" - Local development or dev servers
"staging" - Staging/QA environment
"production" - Production environment (default)
If you don’t specify an environment, all data defaults to "production". This is backwards compatible with existing integrations.
Event Types
Channel Messages (Chat/Slack)
For text conversations between users and assistants.
{
"messageType": "channel",
"id": "msg-123",
"conversationId": "thread-456",
"message": "Hello, I need help with my order",
"role": "user",
"userId": "user-789",
"timestamp": "2025-01-09T12:00:00Z"
}
| Field | Type | Required | Description |
|---|
messageType | string | Yes | Must be "channel" |
id | string | Yes | Unique message ID |
conversationId | string | Yes | Thread/conversation ID |
message | string | Yes | Message content |
role | string | Yes | "user", "assistant", or "system" |
userId | string | No | User identifier |
timestamp | string | No | ISO 8601 timestamp |
For logging tool/function invocations.
{
"messageType": "tool_call",
"id": "tool-123",
"conversationId": "thread-456",
"name": "lookup_order",
"toolCallRequest": {
"order_id": "ORD-999"
},
"toolCallResponse": {
"status": "shipped",
"tracking": "1Z999..."
},
"timestamp": "2025-01-09T12:00:05Z"
}
| Field | Type | Required | Description |
|---|
messageType | string | Yes | Must be "tool_call" |
id | string | Yes | Unique tool call ID |
conversationId | string | Yes | Associated conversation |
name | string | Yes | Tool/function name |
toolCallRequest | object | Yes | Request payload (any JSON) |
toolCallResponse | object | Yes | Response payload (any JSON) |
userId | string | No | User identifier |
timestamp | string | No | ISO 8601 timestamp |
Emails
For email conversations. Uses inReplyTo for threading.
{
"messageType": "email",
"id": "email-123",
"subject": "Re: Support request #456",
"body": "Thanks for reaching out. I've looked into your issue and found that...",
"inReplyTo": "email-thread-001",
"from": "support@boardy.com",
"to": ["customer@example.com"],
"timestamp": "2025-01-09T12:00:00Z"
}
| Field | Type | Required | Description |
|---|
messageType | string | Yes | Must be "email" |
id | string | Yes | Unique email ID |
body | string | Yes | Email body content |
inReplyTo | string | Yes | Thread ID (becomes conversation_id) |
subject | string | No | Email subject line |
from | string | No | Sender email address |
to | string[] | No | Recipient email addresses |
userId | string | No | User identifier |
timestamp | string | No | ISO 8601 timestamp |
The inReplyTo field is used as the conversation_id to group emails into threads.
Use the same inReplyTo value for all emails in a thread.
Call Transcripts
For phone/video call recordings.
{
"messageType": "call",
"id": "call-123",
"conversationId": "call-session-456",
"transcript": [
{ "role": "customer", "content": "Hi, I'm calling about my bill" },
{ "role": "agent", "content": "I'd be happy to help. Can I get your account number?" },
{ "role": "customer", "content": "Sure, it's 12345" },
{ "role": "agent", "content": "Thank you. I see the issue - let me fix that for you." }
],
"duration": 180,
"callType": "phone",
"timestamp": "2025-01-09T12:00:00Z"
}
| 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 |
transcript[].role | string | Yes | Speaker role (e.g., customer, agent) |
transcript[].content | string | Yes | What was said |
duration | number | No | Call duration in seconds |
callType | string | No | "phone", "video", or "voice" |
userId | string | No | User identifier |
timestamp | string | No | ISO 8601 timestamp |
Call transcripts are stored as a single record with the full transcript.
This optimizes for embedding generation and semantic search across entire calls.
Mixed Batch Example
You can send multiple event types in a single request:
{
"environment": "staging",
"events": [
{
"messageType": "channel",
"id": "msg-1",
"conversationId": "conv-123",
"message": "Hi, I need to check on order #456",
"role": "user"
},
{
"messageType": "tool_call",
"id": "tool-1",
"conversationId": "conv-123",
"name": "lookup_order",
"toolCallRequest": { "order_id": "456" },
"toolCallResponse": { "status": "delivered" }
},
{
"messageType": "channel",
"id": "msg-2",
"conversationId": "conv-123",
"message": "Your order #456 was delivered yesterday at 3pm.",
"role": "assistant"
},
{
"messageType": "email",
"id": "email-1",
"inReplyTo": "support-thread-789",
"subject": "Order confirmation",
"body": "Your order has been confirmed...",
"from": "orders@boardy.com",
"to": ["customer@example.com"]
}
]
}
Success
{
"success": true,
"count": 4,
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"details": {
"channel": 2,
"tool_call": 1,
"email": 1,
"call": 0,
"call_transcript_messages": 0
}
}
Error
{
"success": false,
"count": 0,
"message": "Event 2: Channel message missing 'conversationId'",
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"retryable": false
}
Migration Checklist
-
Update endpoint URL
- Old:
/v1/ingest
- New:
/v1/ingest/multi
-
Add
messageType to all events
- Chat messages:
"messageType": "channel"
- Tool calls:
"messageType": "tool_call"
- Emails:
"messageType": "email"
- Calls:
"messageType": "call"
-
Rename fields for channel messages
conversation_id → conversationId (camelCase)
-
Add type-specific fields
- Emails:
inReplyTo, subject, from, to
- Calls:
transcript, duration, callType
- Tool calls:
name, toolCallRequest, toolCallResponse
-
(Optional) Add environment
- Add
"environment": "staging" at the top level to tag non-production data
- Omit for production (defaults to
"production")
-
Test with a small batch first
- Send a few events to verify the format is correct
- Check the response
details breakdown
Limits
| Limit | Value |
|---|
| Max events per request | 1,000 |
| Max message size | 100 KB |
| Max request size | 5 MB |
Questions?
Contact the Moda team for integration support.