Message Management - /external/message
Manage individual messages within chat sessions. These endpoints allow you to add new messages to existing conversations and create new chat sessions with initial messages.
Add New Message
Endpoint
POST /external/message/new-message
Authentication
Authorization: Bearer YOUR_AGENT_ACCESS_TOKEN
Request Body
{
"chat_id": "chat_abc123",
"sender": "user",
"content": "I need help with my recent order",
"metadata": {
"source": "website",
"timestamp": "2025-06-16T10:30:00Z",
"message_type": "text"
}
}
Parameters
Parameter | Type | Required | Description |
---|---|---|---|
chat_id | string | Yes | ID of the chat session |
sender | string | Yes | Message sender: "user" or "assistant" |
content | string | Yes | The message content |
metadata | object | No | Additional message metadata |
Response
{
"message_id": "msg_456",
"chat_id": "chat_abc123",
"sender": "user",
"content": "I need help with my recent order",
"timestamp": "2025-06-16T10:30:00Z",
"processed": true,
"agent_response": {
"message_id": "msg_457",
"content": "I'd be happy to help you with your order. Can you provide your order number?",
"timestamp": "2025-06-16T10:30:15Z"
}
}
Create Chat with Initial Message
Endpoint
POST /external/message/new-chat
Authentication
Authorization: Bearer YOUR_AGENT_ACCESS_TOKEN
Request Body
{
"client_id": "user_789",
"ai_enabled": true,
"metadata": {
"source": "website",
"initial_url": "https://example.com/contact"
}
}
Parameters
Parameter | Type | Required | Description |
---|---|---|---|
client_id | string | Yes | Unique identifier for the client |
ai_enabled | boolean | No | Enable AI responses (default: true) |
metadata | object | No | Additional chat metadata |
Response
{
"chat_id": "chat_abc123",
"agent_id": "agent_456",
"client_id": "user_789",
"created_at": "2025-06-16T10:30:00Z",
"ai_enabled": true,
"lead_collected": false,
"status": "active"
}
Usage Examples
JavaScript - Message Management
class OptimlyMessageManager {
constructor(accessToken, baseUrl = 'https://api.optimly.io') {
this.accessToken = accessToken;
this.baseUrl = baseUrl;
this.headers = {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
};
}
async addMessage(chatId, content, sender = 'user', metadata = {}) {
const response = await fetch(`${this.baseUrl}/external/message/new-message`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify({
chat_id: chatId,
sender,
content,
metadata: {
source: 'javascript_sdk',
timestamp: new Date().toISOString(),
...metadata
}
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to add message');
}
return await response.json();
}
async createChatWithMessage(clientId, initialMessage = null, metadata = {}) {
// First create the chat
const chatResponse = await fetch(`${this.baseUrl}/external/message/new-chat`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify({
client_id: clientId,
ai_enabled: true,
metadata
})
});
if (!chatResponse.ok) {
const error = await chatResponse.json();
throw new Error(error.detail || 'Failed to create chat');
}
const chat = await chatResponse.json();
// If initial message provided, add it to the chat
if (initialMessage) {
const messageResult = await this.addMessage(
chat.chat_id,
initialMessage,
'user',
{ initial_message: true }
);
return {
chat,
initialMessage: messageResult
};
}
return { chat };
}
async sendUserMessage(chatId, content, metadata = {}) {
return await this.addMessage(chatId, content, 'user', metadata);
}
async sendAgentMessage(chatId, content, metadata = {}) {
return await this.addMessage(chatId, content, 'assistant', metadata);
}
}
// Usage example
const messageManager = new OptimlyMessageManager('your_access_token');
// Create new chat with initial message
const result = await messageManager.createChatWithMessage(
'user_123',
'Hello, I need help with my account',
{
source: 'website',
page_url: window.location.href
}
);
console.log('Created chat:', result.chat.chat_id);
console.log('Agent response:', result.initialMessage.agent_response.content);
// Add follow-up message
const followUp = await messageManager.sendUserMessage(
result.chat.chat_id,
'I forgot my password',
{ message_type: 'follow_up' }
);
console.log('Follow-up response:', followUp.agent_response.content);
Python - Message Management
import requests
from typing import Dict, Optional, Any
from datetime import datetime
class OptimlyMessageManager:
def __init__(self, access_token: str, base_url: str = 'https://api.optimly.io'):
self.access_token = access_token
self.base_url = base_url
self.headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
def add_message(
self,
chat_id: str,
content: str,
sender: str = 'user',
metadata: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Add a new message to an existing chat."""
if metadata is None:
metadata = {}
payload = {
'chat_id': chat_id,
'sender': sender,
'content': content,
'metadata': {
'source': 'python_sdk',
'timestamp': datetime.now().isoformat(),
**metadata
}
}
response = requests.post(
f'{self.base_url}/external/message/new-message',
json=payload,
headers=self.headers
)
response.raise_for_status()
return response.json()
def create_chat_with_message(
self,
client_id: str,
initial_message: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Create a new chat and optionally add an initial message."""
if metadata is None:
metadata = {}
# Create chat
chat_payload = {
'client_id': client_id,
'ai_enabled': True,
'metadata': metadata
}
chat_response = requests.post(
f'{self.base_url}/external/message/new-chat',
json=chat_payload,
headers=self.headers
)
chat_response.raise_for_status()
chat = chat_response.json()
result = {'chat': chat}
# Add initial message if provided
if initial_message:
message_result = self.add_message(
chat['chat_id'],
initial_message,
'user',
{'initial_message': True}
)
result['initial_message'] = message_result
return result
def send_user_message(
self,
chat_id: str,
content: str,
metadata: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Send a user message to the chat."""
return self.add_message(chat_id, content, 'user', metadata)
def send_agent_message(
self,
chat_id: str,
content: str,
metadata: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Send an agent message to the chat."""
return self.add_message(chat_id, content, 'assistant', metadata)
# Usage example
message_manager = OptimlyMessageManager('your_access_token')
# Create chat with initial message
result = message_manager.create_chat_with_message(
client_id='user_123',
initial_message='I need help with billing',
metadata={
'source': 'customer_portal',
'user_tier': 'premium'
}
)
print(f"Created chat: {result['chat']['chat_id']}")
if 'initial_message' in result:
print(f"Agent response: {result['initial_message']['agent_response']['content']}")
# Send follow-up message
follow_up = message_manager.send_user_message(
result['chat']['chat_id'],
'I was charged twice for the same item',
{'message_type': 'complaint', 'urgency': 'high'}
)
print(f"Follow-up response: {follow_up['agent_response']['content']}")
React Hook for Message Management
import { useState, useCallback } from 'react';
export function useOptimlyMessages(accessToken) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const baseUrl = 'https://api.optimly.io';
const headers = {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
};
const addMessage = useCallback(async (chatId, content, sender = 'user', metadata = {}) => {
setLoading(true);
setError(null);
try {
const response = await fetch(`${baseUrl}/external/message/new-message`, {
method: 'POST',
headers,
body: JSON.stringify({
chat_id: chatId,
sender,
content,
metadata: {
source: 'react_app',
timestamp: new Date().toISOString(),
...metadata
}
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || 'Failed to add message');
}
const result = await response.json();
return result;
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
}, [accessToken]);
const createChatWithMessage = useCallback(async (clientId, initialMessage = null, metadata = {}) => {
setLoading(true);
setError(null);
try {
// Create chat
const chatResponse = await fetch(`${baseUrl}/external/message/new-chat`, {
method: 'POST',
headers,
body: JSON.stringify({
client_id: clientId,
ai_enabled: true,
metadata
})
});
if (!chatResponse.ok) {
const errorData = await chatResponse.json();
throw new Error(errorData.detail || 'Failed to create chat');
}
const chat = await chatResponse.json();
let result = { chat };
// Add initial message if provided
if (initialMessage) {
const messageResult = await addMessage(
chat.chat_id,
initialMessage,
'user',
{ initial_message: true }
);
result.initialMessage = messageResult;
}
return result;
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
}, [accessToken, addMessage]);
const sendUserMessage = useCallback(async (chatId, content, metadata = {}) => {
return await addMessage(chatId, content, 'user', metadata);
}, [addMessage]);
const sendAgentMessage = useCallback(async (chatId, content, metadata = {}) => {
return await addMessage(chatId, content, 'assistant', metadata);
}, [addMessage]);
return {
loading,
error,
addMessage,
createChatWithMessage,
sendUserMessage,
sendAgentMessage
};
}
// Usage in component
function MessageInterface({ accessToken, clientId }) {
const {
loading,
error,
createChatWithMessage,
sendUserMessage
} = useOptimlyMessages(accessToken);
const [currentChat, setCurrentChat] = useState(null);
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
const startNewChat = async () => {
try {
const result = await createChatWithMessage(
clientId,
'Hello, I need assistance',
{
source: 'react_component',
page_url: window.location.href
}
);
setCurrentChat(result.chat);
if (result.initialMessage) {
setMessages([
{
id: result.initialMessage.message_id,
sender: 'user',
content: 'Hello, I need assistance',
timestamp: result.initialMessage.timestamp
},
{
id: result.initialMessage.agent_response.message_id,
sender: 'assistant',
content: result.initialMessage.agent_response.content,
timestamp: result.initialMessage.agent_response.timestamp
}
]);
}
} catch (error) {
console.error('Failed to create chat:', error);
}
};
const handleSendMessage = async () => {
if (!newMessage.trim() || !currentChat) return;
try {
const result = await sendUserMessage(
currentChat.chat_id,
newMessage,
{ message_type: 'user_query' }
);
// Add user message
setMessages(prev => [...prev, {
id: result.message_id,
sender: 'user',
content: newMessage,
timestamp: result.timestamp
}]);
// Add agent response
if (result.agent_response) {
setMessages(prev => [...prev, {
id: result.agent_response.message_id,
sender: 'assistant',
content: result.agent_response.content,
timestamp: result.agent_response.timestamp
}]);
}
setNewMessage('');
} catch (error) {
console.error('Failed to send message:', error);
}
};
if (error) {
return <div className="error">Error: {error}</div>;
}
return (
<div className="message-interface">
{!currentChat ? (
<div className="start-chat">
<button onClick={startNewChat} disabled={loading}>
{loading ? 'Starting Chat...' : 'Start New Chat'}
</button>
</div>
) : (
<div className="chat-active">
<div className="messages">
{messages.map(message => (
<div key={message.id} className={`message ${message.sender}`}>
<div className="content">{message.content}</div>
<div className="timestamp">
{new Date(message.timestamp).toLocaleTimeString()}
</div>
</div>
))}
</div>
<div className="input-area">
<input
type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()}
placeholder="Type your message..."
disabled={loading}
/>
<button onClick={handleSendMessage} disabled={loading || !newMessage.trim()}>
{loading ? 'Sending...' : 'Send'}
</button>
</div>
</div>
)}
</div>
);
}
Message Processing Flow
When you add a user message via the API, here's what happens:
- Message Storage: The message is stored in the chat history
- AI Processing: If AI is enabled, the agent processes the message
- Tool Detection: The agent checks if any tools should be executed
- Response Generation: The agent generates a response
- Tool Execution: Any triggered tools are executed automatically
- Response Return: The complete response including any tool results is returned
Message Metadata
Use metadata to enrich your messages with additional context:
const metadata = {
// Source tracking
source: 'website',
page_url: window.location.href,
user_agent: navigator.userAgent,
// User context
user_tier: 'premium',
session_duration: '00:05:30',
previous_interactions: 3,
// Message context
message_type: 'question',
urgency: 'medium',
category: 'technical_support',
// Custom tracking
campaign_id: 'summer_2025',
ab_test_variant: 'variant_b'
};
Error Handling
Common Error Scenarios
async function safeAddMessage(chatId, content, metadata = {}) {
try {
const result = await messageManager.addMessage(chatId, content, 'user', metadata);
return result;
} catch (error) {
if (error.message.includes('Chat not found')) {
// Chat doesn't exist, create a new one
console.log('Chat not found, creating new chat...');
const newChatResult = await messageManager.createChatWithMessage(
getUserId(),
content,
metadata
);
return newChatResult.initialMessage;
} else if (error.message.includes('Invalid access token')) {
// Handle authentication error
console.error('Authentication failed, redirecting to login...');
redirectToLogin();
} else if (error.message.includes('Rate limit exceeded')) {
// Handle rate limiting
console.log('Rate limit exceeded, retrying in 60 seconds...');
setTimeout(() => safeAddMessage(chatId, content, metadata), 60000);
} else {
// Handle other errors
console.error('Failed to add message:', error.message);
showErrorMessage('Failed to send message. Please try again.');
}
throw error;
}
}
Best Practices
1. Message Sequencing
Ensure messages are sent in the correct order:
// Use async/await to maintain order
async function sendSequentialMessages(chatId, messages) {
const results = [];
for (const message of messages) {
const result = await messageManager.addMessage(chatId, message.content, message.sender);
results.push(result);
// Optional: wait between messages to avoid rate limits
await new Promise(resolve => setTimeout(resolve, 100));
}
return results;
}
2. Context Preservation
Include relevant context in message metadata:
// Track conversation context
let conversationContext = {
topic: null,
user_intent: null,
last_tool_used: null
};
async function sendContextualMessage(chatId, content) {
const metadata = {
conversation_topic: conversationContext.topic,
user_intent: conversationContext.user_intent,
last_tool_used: conversationContext.last_tool_used,
message_index: getMessageCount(chatId)
};
const result = await messageManager.addMessage(chatId, content, 'user', metadata);
// Update context based on response
if (result.agent_response) {
conversationContext.topic = detectTopic(result.agent_response.content);
conversationContext.last_tool_used = result.tools_executed?.[0]?.tool;
}
return result;
}
3. Batch Operations
For multiple operations, use transactions when possible:
async function createChatWithMultipleMessages(clientId, messages, metadata = {}) {
try {
// Create chat first
const chatResult = await messageManager.createChatWithMessage(
clientId,
null, // No initial message
metadata
);
const chatId = chatResult.chat.chat_id;
const results = [];
// Add messages sequentially
for (const message of messages) {
const result = await messageManager.addMessage(
chatId,
message.content,
message.sender || 'user',
{ batch_index: results.length, ...message.metadata }
);
results.push(result);
}
return {
chat: chatResult.chat,
messages: results
};
} catch (error) {
console.error('Batch operation failed:', error);
throw error;
}
}
Rate Limits
- 100 messages per minute per access token
- 1000 messages per hour per access token
- 50 new chats per hour per access token
Integration Examples
Customer Support Ticket System
// Create chat when support ticket is created
async function createSupportChat(ticketId, customerId, initialMessage) {
const result = await messageManager.createChatWithMessage(
customerId,
initialMessage,
{
source: 'support_system',
ticket_id: ticketId,
priority: 'high',
department: 'customer_support'
}
);
// Store chat ID with ticket
await updateTicket(ticketId, { chat_id: result.chat.chat_id });
return result;
}
// Add support agent messages
async function addSupportMessage(chatId, agentId, message) {
return await messageManager.addMessage(
chatId,
message,
'assistant',
{
agent_id: agentId,
message_type: 'support_response',
timestamp: new Date().toISOString()
}
);
}
E-commerce Order Inquiries
// Create chat for order inquiry
async function createOrderInquiryChat(customerId, orderId) {
const orderDetails = await getOrderDetails(orderId);
const result = await messageManager.createChatWithMessage(
customerId,
`I have a question about my order #${orderId}`,
{
source: 'ecommerce',
order_id: orderId,
order_status: orderDetails.status,
order_value: orderDetails.total
}
);
return result;
}
Next Steps
- Agent Interaction - Use messages with the enhanced agent endpoint
- Chat Management - Learn about chat lifecycle management
- Tools Integration - Enable automatic tool execution
- Authentication - Secure your message endpoints