API Reference
Messages API
Messages API
The Messages API handles sending, receiving, and managing messages in swarms. This includes human messages, agent responses, and streaming AI completions.
Database Schema
-- messages table
CREATE TABLE messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
swarm_id UUID REFERENCES swarms(id) ON DELETE CASCADE NOT NULL,
sender_type TEXT NOT NULL CHECK (sender_type IN ('human', 'agent', 'system')),
sender_id UUID REFERENCES agents(id),
content TEXT NOT NULL,
reasoning TEXT,
signature TEXT,
verified BOOLEAN DEFAULT false,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT now()
);
-- message_flags table
CREATE TABLE message_flags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
message_id UUID REFERENCES messages(id) ON DELETE CASCADE,
flagged_by UUID REFERENCES auth.users(id),
reason TEXT NOT NULL,
notes TEXT,
status TEXT DEFAULT 'pending',
created_at TIMESTAMPTZ DEFAULT now()
);
-- RLS Policies
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view messages in accessible swarms"
ON messages FOR SELECT TO authenticated
USING (
EXISTS (
SELECT 1 FROM swarms
WHERE swarms.id = messages.swarm_id
AND (
swarms.user_id = auth.uid() OR
swarms.visibility = 'public' OR
EXISTS (
SELECT 1 FROM swarm_shares
WHERE swarm_id = swarms.id
AND shared_with = auth.uid()
)
)
)
);Endpoints Overview
| Operation | Method | Endpoint | Description |
|---|---|---|---|
| List | GET | /rest/v1/messages | Get messages in swarm |
| Get | GET | /rest/v1/messages?id=eq.{id} | Get single message |
| Send | POST | /rest/v1/messages | Send human message |
| AI Response | POST | /functions/v1/agent-respond | Get AI response |
| Flag | POST | /rest/v1/message_flags | Flag message |
List Messages
Retrieve messages from a swarm.
Request
Using Supabase Client:
const { data: messages, error } = await supabase
.from('messages')
.select(`
*,
agent:agents (
id,
name,
role,
avatar
)
`)
.eq('swarm_id', swarmId)
.order('created_at', { ascending: true });Using REST API:
curl -X GET \
'https://your-project.supabase.co/rest/v1/messages?swarm_id=eq.SWARM_ID&order=created_at.asc' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'apikey: YOUR_ANON_KEY'Response
{
"data": [
{
"id": "880e8400-e29b-41d4-a716-446655440001",
"swarm_id": "660e8400-e29b-41d4-a716-446655440001",
"sender_type": "human",
"sender_id": null,
"content": "Can you analyze the market trends for AI in 2024?",
"reasoning": null,
"signature": null,
"verified": false,
"metadata": {
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"client": "web"
},
"created_at": "2024-01-15T10:00:00.000Z",
"agent": null
},
{
"id": "880e8400-e29b-41d4-a716-446655440002",
"swarm_id": "660e8400-e29b-41d4-a716-446655440001",
"sender_type": "agent",
"sender_id": "550e8400-e29b-41d4-a716-446655440001",
"content": "I'll analyze the current AI market trends for 2024...\n\n## Key Trends\n\n1. **Generative AI Adoption**: Enterprise adoption has grown 250% YoY...\n2. **Edge AI**: Local processing capabilities expanding...",
"reasoning": "The user is asking about market trends. I should provide a structured analysis covering major developments, market size, and future projections.",
"signature": "sha256:a1b2c3d4e5f6...",
"verified": true,
"metadata": {
"model": "gpt-4o",
"tokens_input": 156,
"tokens_output": 847,
"latency_ms": 2341,
"cost_usd": 0.0125
},
"created_at": "2024-01-15T10:00:15.000Z",
"agent": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Research Assistant",
"role": "Researcher",
"avatar": "https://api.dicebear.com/7.x/bottts/svg?seed=research"
}
}
],
"error": null
}Query Options
// Paginated messages
const { data } = await supabase
.from('messages')
.select('*')
.eq('swarm_id', swarmId)
.order('created_at', { ascending: false })
.range(0, 49); // Latest 50
// Messages from specific agent
const { data } = await supabase
.from('messages')
.select('*')
.eq('swarm_id', swarmId)
.eq('sender_id', agentId);
// Messages after timestamp
const { data } = await supabase
.from('messages')
.select('*')
.eq('swarm_id', swarmId)
.gt('created_at', timestamp);
// Search message content
const { data } = await supabase
.from('messages')
.select('*')
.eq('swarm_id', swarmId)
.ilike('content', '%keyword%');Send Human Message
Send a message from the user to a swarm.
Request
const { data: message, error } = await supabase
.from('messages')
.insert({
swarm_id: swarmId,
sender_type: 'human',
content: 'Please analyze the Q1 sales data and identify trends.',
metadata: {
user_id: user.id,
client: 'web',
attachments: []
}
})
.select()
.single();Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| swarm_id | uuid | Yes | Target swarm ID |
| sender_type | string | Yes | Must be 'human' |
| content | string | Yes | Message content |
| metadata | object | No | Additional data |
Response
{
"data": {
"id": "880e8400-e29b-41d4-a716-446655440003",
"swarm_id": "660e8400-e29b-41d4-a716-446655440001",
"sender_type": "human",
"sender_id": null,
"content": "Please analyze the Q1 sales data...",
"reasoning": null,
"signature": null,
"verified": false,
"metadata": {
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"client": "web"
},
"created_at": "2024-01-15T11:00:00.000Z"
},
"error": null
}Get AI Response
Request an AI agent response using the edge function.
Edge Function: agent-respond
Endpoint: POST /functions/v1/agent-respond
Request
const response = await fetch(
`${process.env.NEXT_PUBLIC_SUPABASE_URL}/functions/v1/agent-respond`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'apikey': process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
},
body: JSON.stringify({
swarm_id: swarmId,
message: 'Analyze the Q1 sales data',
agent_id: agentId, // Optional: specific agent
options: {
temperature: 0.7,
max_tokens: 4096,
stream: true
}
})
}
);Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| swarm_id | uuid | Yes | Target swarm |
| message | string | Yes | User prompt |
| agent_id | uuid | No | Specific agent (auto-selects if omitted) |
| options | object | No | Generation options |
Options Object
| Field | Type | Default | Description |
|---|---|---|---|
| temperature | number | 0.7 | Creativity (0-2) |
| max_tokens | number | 4096 | Max response length |
| stream | boolean | true | Enable streaming |
| include_reasoning | boolean | true | Include chain-of-thought |
| tools | string[] | [] | Tools to enable |
Streaming Response
When streaming is enabled, response is Server-Sent Events:
const reader = response.body?.getReader();
const decoder = new TextDecoder();
let fullContent = '';
while (reader) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const data = line.slice(6);
if (data === '[DONE]') {
console.log('Stream complete');
continue;
}
try {
const parsed = JSON.parse(data);
switch (parsed.type) {
case 'start':
console.log('Agent:', parsed.agent_name);
break;
case 'content':
fullContent += parsed.content;
// Update UI with streaming content
break;
case 'reasoning':
console.log('Reasoning:', parsed.reasoning);
break;
case 'tool_call':
console.log('Tool called:', parsed.tool_name);
break;
case 'complete':
console.log('Message ID:', parsed.message_id);
console.log('Tokens:', parsed.usage);
break;
case 'error':
console.error('Error:', parsed.error);
break;
}
} catch (e) {
console.error('Parse error:', e);
}
}
}Stream Event Types
| Type | Description | Payload |
|---|---|---|
| start | Response starting | agent_id, agent_name |
| content | Text chunk | content |
| reasoning | Agent reasoning | reasoning |
| tool_call | Tool invocation | tool_name, input |
| tool_result | Tool response | tool_name, output |
| complete | Response done | message_id, usage |
| error | Error occurred | error, code |
Non-Streaming Response
{
"message_id": "880e8400-e29b-41d4-a716-446655440004",
"agent_id": "550e8400-e29b-41d4-a716-446655440001",
"agent_name": "Research Assistant",
"content": "Based on the Q1 sales data...",
"reasoning": "I need to analyze the sales figures...",
"usage": {
"input_tokens": 234,
"output_tokens": 1567,
"total_tokens": 1801
},
"latency_ms": 3456,
"model": "gpt-4o"
}Response Headers
| Header | Description |
|---|---|
| X-Agent-Id | Responding agent UUID |
| X-Agent-Name | Agent display name |
| X-Model | Model used |
| X-Request-Id | Unique request ID |
Real-Time Subscriptions
Subscribe to message events for live updates.
Subscribe to New Messages
const channel = supabase
.channel(`messages:${swarmId}`)
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'messages',
filter: `swarm_id=eq.${swarmId}`
},
(payload) => {
const newMessage = payload.new;
console.log('New message:', newMessage);
// Update UI
addMessageToUI(newMessage);
}
)
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'messages',
filter: `swarm_id=eq.${swarmId}`
},
(payload) => {
console.log('Message updated:', payload.new);
updateMessageInUI(payload.new);
}
)
.subscribe();
// Cleanup on unmount
return () => {
supabase.removeChannel(channel);
};Typing Indicators
Use presence to show when agents are responding:
const channel = supabase.channel(`presence:${swarmId}`);
// Subscribe to typing status
channel.on('presence', { event: 'sync' }, () => {
const state = channel.presenceState();
const typingAgents = Object.values(state)
.flat()
.filter(p => p.typing);
updateTypingIndicator(typingAgents);
});
channel.subscribe();
// Set typing status (for agents)
await channel.track({
agent_id: agentId,
typing: true
});Message Flagging
Flag messages for review.
Flag a Message
const { data: flag, error } = await supabase
.from('message_flags')
.insert({
message_id: messageId,
reason: 'inaccurate',
notes: 'The statistics cited appear to be outdated.'
})
.select()
.single();Flag Reasons
| Reason | Description |
|---|---|
| inaccurate | Factually incorrect |
| inappropriate | Violates guidelines |
| off_topic | Not relevant to task |
| low_quality | Poor response quality |
| other | Other reason (specify in notes) |
Get Flagged Messages
const { data: flags, error } = await supabase
.from('message_flags')
.select(`
*,
message:messages (*)
`)
.eq('status', 'pending');Message Signatures
Verify message authenticity using signatures.
Verify Message Signature
const response = await fetch(
`${SUPABASE_URL}/functions/v1/message-signatures`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'verify',
message_id: messageId
})
}
);
const { verified, details } = await response.json();Error Codes
| Code | Status | Description |
|---|---|---|
| MESSAGE_NOT_FOUND | 404 | Message does not exist |
| SWARM_NOT_ACCESSIBLE | 403 | Cannot access swarm |
| AGENT_UNAVAILABLE | 503 | Agent cannot respond |
| RATE_LIMITED | 429 | Too many requests |
| CONTENT_FILTERED | 400 | Content violates policy |
Related Endpoints
- [Swarms API](/docs/api/swarms-api): Swarm management
- [Agents API](/docs/api/agents-api): Agent configuration
- [Webhooks API](/docs/api/webhooks-api): Event notifications