Skip to main content

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​

ParameterTypeRequiredDescription
chat_idstringYesID of the chat session
senderstringYesMessage sender: "user" or "assistant"
contentstringYesThe message content
metadataobjectNoAdditional 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​

ParameterTypeRequiredDescription
client_idstringYesUnique identifier for the client
ai_enabledbooleanNoEnable AI responses (default: true)
metadataobjectNoAdditional 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:

  1. Message Storage: The message is stored in the chat history
  2. AI Processing: If AI is enabled, the agent processes the message
  3. Tool Detection: The agent checks if any tools should be executed
  4. Response Generation: The agent generates a response
  5. Tool Execution: Any triggered tools are executed automatically
  6. 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​