Bi-directional Agent Communication#
This guide provides everything needed to integrate with the Eden API for bi-directional communication with AI agents from a frontend application. The Eden API uses a session-based communication model where you create sessions with specific agents and then exchange messages within those sessions.
Overview#
The Eden API enables real-time, bi-directional communication between your application and Eden's AI agents. This allows for:
- Interactive Conversations: Back-and-forth dialogue with AI agents
- Streaming Responses: Real-time message delivery as agents generate content
- Multi-Agent Sessions: Communicate with multiple agents simultaneously
- Persistent Context: Maintain conversation history and context across interactions
Authentication#
All API requests require authentication using an API key passed in the request headers:
Important
Never expose API keys in frontend code. Use environment variables or a backend proxy.
Core Integration Flow#
1. Create a Session#
Before sending messages, you must create a session with one or more agents.
Endpoint: POST https://api.eden.art/v2/sessions/create
{
"agent_ids": ["agent-id-1", "agent-id-2"],
"scenario": "Optional description of what you want to accomplish",
"title": "Human-readable session title",
"budget": {
"manna_budget": 1000, // Range: 100-50000 (computational resources)
"token_budget": 10000, // Range: 1000-1000000 (text generation)
"turn_budget": 100 // Range: 1-1000 (conversation turns)
},
"autonomy_settings": {
"auto_reply": true, // Agents respond automatically
"reply_interval": 5, // Seconds between responses (0-3600)
"actor_selection_method": "random" // How agents are selected to respond
}
}
Response:
2. Send Messages to Agents#
Once you have a session ID, you can send messages to the agents.
Endpoint: POST https://api.eden.art/v2/sessions
{
"session_id": "your-session-id",
"content": "Your message to the agent(s)",
"attachments": ["https://example.com/file1.png"], // Optional file URLs
"stream": true, // Enable real-time streaming responses
"thinking": true, // Show agent's reasoning process
"agent_ids": ["specific-agent-id"] // Optional: target specific agents only
}
3. Handle Responses#
Responses vary based on whether you enable streaming:
- Non-streaming: Complete response once the agent finishes processing
- Streaming (
stream: true
): Real-time chunks as the agent generates content
Implementation Examples#
Basic Session Management#
class EdenAPIClient {
constructor(apiKey, baseURL = 'https://api.eden.art') {
this.apiKey = apiKey;
this.baseURL = baseURL;
this.headers = {
'X-Api-Key': apiKey,
'Content-Type': 'application/json'
};
}
async createSession(agentIds, options = {}) {
const payload = {
agent_ids: agentIds,
title: options.title || "Chat Session",
scenario: options.scenario || "",
budget: {
manna_budget: options.mannaBudget || 1000,
token_budget: options.tokenBudget || 10000,
turn_budget: options.turnBudget || 100
},
autonomy_settings: {
auto_reply: options.autoReply !== false,
reply_interval: options.replyInterval || 2,
actor_selection_method: options.actorSelection || "random"
}
};
try {
const response = await fetch(`${this.baseURL}/v2/sessions/create`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return data.session_id;
} catch (error) {
console.error('Failed to create session:', error);
throw error;
}
}
async sendMessage(sessionId, message, options = {}) {
const payload = {
session_id: sessionId,
content: message,
stream: options.stream !== false,
thinking: options.thinking !== false,
attachments: options.attachments || [],
agent_ids: options.targetAgents || []
};
try {
const response = await fetch(`${this.baseURL}/v2/sessions`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response;
} catch (error) {
console.error('Failed to send message:', error);
throw error;
}
}
}
Handling Streaming Responses#
async function handleStreamingResponse(response, onChunk, onComplete, onError) {
try {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) {
if (buffer) {
onChunk(buffer);
}
onComplete();
break;
}
buffer += decoder.decode(value, { stream: true });
// Process complete chunks (assuming newline-delimited)
const lines = buffer.split('\n');
buffer = lines.pop(); // Keep incomplete line in buffer
for (const line of lines) {
if (line.trim()) {
try {
// Try to parse as JSON if it's structured data
const data = JSON.parse(line);
onChunk(data);
} catch {
// Handle as plain text
onChunk(line);
}
}
}
}
} catch (error) {
onError(error);
}
}
// Usage example
async function sendStreamingMessage(client, sessionId, message) {
const response = await client.sendMessage(sessionId, message, { stream: true });
await handleStreamingResponse(
response,
(chunk) => {
// Update UI with each chunk
console.log('Received chunk:', chunk);
updateChatDisplay(chunk);
},
() => {
// Handle completion
console.log('Response complete');
markMessageComplete();
},
(error) => {
// Handle errors
console.error('Streaming error:', error);
showErrorMessage(error);
}
);
}
React Hook Example#
import { useState, useCallback } from 'react';
export function useEdenAPI(apiKey) {
const [client] = useState(() => new EdenAPIClient(apiKey));
const [sessions, setSessions] = useState(new Map());
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const createSession = useCallback(async (agentIds, options) => {
setLoading(true);
setError(null);
try {
const sessionId = await client.createSession(agentIds, options);
setSessions(prev => new Map(prev.set(sessionId, {
id: sessionId,
messages: [],
agents: agentIds,
createdAt: new Date()
})));
return sessionId;
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
}, [client]);
const sendMessage = useCallback(async (sessionId, message, options = {}) => {
setLoading(true);
setError(null);
// Add user message to local state
setSessions(prev => {
const session = prev.get(sessionId);
if (session) {
session.messages.push({
id: Date.now(),
content: message,
sender: 'user',
timestamp: new Date()
});
}
return new Map(prev);
});
try {
const response = await client.sendMessage(sessionId, message, options);
if (options.stream !== false) {
// Handle streaming
let responseContent = '';
const messageId = Date.now() + 1;
await handleStreamingResponse(
response,
(chunk) => {
responseContent += typeof chunk === 'string' ? chunk : JSON.stringify(chunk);
setSessions(prev => {
const session = prev.get(sessionId);
if (session) {
const existingMessage = session.messages.find(m => m.id === messageId);
if (existingMessage) {
existingMessage.content = responseContent;
} else {
session.messages.push({
id: messageId,
content: responseContent,
sender: 'agent',
timestamp: new Date(),
streaming: true
});
}
}
return new Map(prev);
});
},
() => {
setSessions(prev => {
const session = prev.get(sessionId);
if (session) {
const message = session.messages.find(m => m.id === messageId);
if (message) {
message.streaming = false;
}
}
return new Map(prev);
});
},
(err) => setError(err.message)
);
} else {
// Handle non-streaming response
const data = await response.json();
setSessions(prev => {
const session = prev.get(sessionId);
if (session) {
session.messages.push({
id: Date.now() + 1,
content: data.content || 'No response content',
sender: 'agent',
timestamp: new Date()
});
}
return new Map(prev);
});
}
} catch (err) {
setError(err.message);
throw err;
} finally {
setLoading(false);
}
}, [client]);
return {
createSession,
sendMessage,
sessions: Array.from(sessions.values()),
loading,
error,
clearError: () => setError(null)
};
}
Session Management#
Get Session Details#
async getSession(sessionId) {
const response = await fetch(`${this.baseURL}/v2/sessions/${sessionId}`, {
headers: this.headers
});
return response.json();
}
List All Sessions#
async listSessions() {
const response = await fetch(`${this.baseURL}/v2/sessions`, {
headers: this.headers
});
return response.json();
}
Delete Session#
async deleteSession(sessionId) {
await fetch(`${this.baseURL}/v2/sessions/${sessionId}`, {
method: 'DELETE',
headers: this.headers
});
}
Agent Management#
List Available Agents#
async listAgents() {
const response = await fetch(`${this.baseURL}/v2/agents`, {
headers: this.headers
});
return response.json();
}
Get Agent Details#
async getAgent(agentId) {
const response = await fetch(`${this.baseURL}/v2/agents/${agentId}`, {
headers: this.headers
});
return response.json();
}
Error Handling#
Always implement proper error handling:
async function handleEdenAPICall(apiCall) {
try {
return await apiCall();
} catch (error) {
if (error.response) {
// API returned an error response
const errorData = await error.response.json();
console.error('API Error:', errorData.message);
switch (error.response.status) {
case 401:
throw new Error('Invalid API key. Please check your authentication.');
case 400:
throw new Error(`Bad request: ${errorData.message}`);
case 404:
throw new Error('Resource not found. Check your session or agent IDs.');
case 429:
throw new Error('Rate limit exceeded. Please wait and try again.');
default:
throw new Error(`API error: ${errorData.message || 'Unknown error'}`);
}
} else {
// Network or other error
console.error('Network Error:', error);
throw new Error('Failed to connect to Eden API. Please check your connection.');
}
}
}
Complete React Component Example#
import React, { useState, useEffect, useRef } from 'react';
import { useEdenAPI } from './useEdenAPI';
export function EdenChat({ apiKey, agentIds }) {
const { createSession, sendMessage, sessions, loading, error } = useEdenAPI(apiKey);
const [currentSession, setCurrentSession] = useState(null);
const [messageInput, setMessageInput] = useState('');
const messagesEndRef = useRef(null);
useEffect(() => {
// Create initial session
const initSession = async () => {
try {
const sessionId = await createSession(agentIds, {
title: 'New Chat',
autoReply: true,
replyInterval: 1
});
setCurrentSession(sessionId);
} catch (err) {
console.error('Failed to create session:', err);
}
};
initSession();
}, [createSession, agentIds]);
useEffect(() => {
// Auto-scroll to bottom
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [sessions]);
const handleSendMessage = async (e) => {
e.preventDefault();
if (!messageInput.trim() || !currentSession || loading) return;
const message = messageInput.trim();
setMessageInput('');
try {
await sendMessage(currentSession, message, { stream: true });
} catch (err) {
console.error('Failed to send message:', err);
}
};
const currentSessionData = sessions.find(s => s.id === currentSession);
return (
<div className="eden-chat">
<div className="messages">
{currentSessionData?.messages.map(msg => (
<div key={msg.id} className={`message ${msg.sender}`}>
<div className="content">
{msg.content}
{msg.streaming && <span className="typing-indicator">...</span>}
</div>
<div className="timestamp">
{msg.timestamp.toLocaleTimeString()}
</div>
</div>
))}
{error && (
<div className="error-message">
Error: {error}
</div>
)}
<div ref={messagesEndRef} />
</div>
<form onSubmit={handleSendMessage} className="input-form">
<input
type="text"
value={messageInput}
onChange={(e) => setMessageInput(e.target.value)}
placeholder="Type your message..."
disabled={loading || !currentSession}
/>
<button type="submit" disabled={loading || !currentSession || !messageInput.trim()}>
{loading ? 'Sending...' : 'Send'}
</button>
</form>
</div>
);
}
Best Practices#
1. Budget Management#
Monitor your session budgets to prevent unexpected terminations: - Manna Budget: Computational resources for agent processing - Token Budget: Text generation limits - Turn Budget: Maximum conversation exchanges
2. Session Lifecycle#
- Create sessions when starting new conversations
- Reuse sessions for continued conversations
- Clean up sessions when conversations end
- Implement session persistence for user experience
3. Performance Optimization#
- Use streaming for better perceived performance
- Implement message caching for session history
- Consider pagination for long conversation histories
- Handle network failures gracefully
4. Security Considerations#
- Never expose API keys in client-side code
- Use environment variables or backend proxies
- Implement proper input validation
- Sanitize user inputs before sending to API
5. User Experience#
- Show loading states during API calls
- Display streaming responses in real-time
- Implement retry mechanisms for failed requests
- Provide clear error messages to users
Troubleshooting#
Common Issues#
- Authentication Errors (401)
- Verify API key is correct
- Check headers are properly set
-
Ensure API key has necessary permissions
-
Session Creation Failures
- Verify agent IDs exist
- Check budget values are within allowed ranges
-
Ensure required fields are provided
-
Streaming Issues
- Verify browser supports ReadableStream
- Check for network connectivity issues
-
Implement fallback to non-streaming mode
-
Rate Limiting
- Implement exponential backoff
- Add request queuing
- Monitor usage patterns
Next Steps#
- Explore the API Overview for more details
- Check out other integrations for platform-specific guides
- Visit the full API documentation
This guide provides comprehensive coverage for implementing bi-directional communication with Eden's AI agents. Start with the basic examples and gradually add more advanced features as needed.