HIVE

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

OperationMethodEndpointDescription
ListGET/rest/v1/messagesGet messages in swarm
GetGET/rest/v1/messages?id=eq.{id}Get single message
SendPOST/rest/v1/messagesSend human message
AI ResponsePOST/functions/v1/agent-respondGet AI response
FlagPOST/rest/v1/message_flagsFlag 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

FieldTypeRequiredDescription
swarm_iduuidYesTarget swarm ID
sender_typestringYesMust be 'human'
contentstringYesMessage content
metadataobjectNoAdditional 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

FieldTypeRequiredDescription
swarm_iduuidYesTarget swarm
messagestringYesUser prompt
agent_iduuidNoSpecific agent (auto-selects if omitted)
optionsobjectNoGeneration options

Options Object

FieldTypeDefaultDescription
temperaturenumber0.7Creativity (0-2)
max_tokensnumber4096Max response length
streambooleantrueEnable streaming
include_reasoningbooleantrueInclude chain-of-thought
toolsstring[][]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

TypeDescriptionPayload
startResponse startingagent_id, agent_name
contentText chunkcontent
reasoningAgent reasoningreasoning
tool_callTool invocationtool_name, input
tool_resultTool responsetool_name, output
completeResponse donemessage_id, usage
errorError occurrederror, 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

HeaderDescription
X-Agent-IdResponding agent UUID
X-Agent-NameAgent display name
X-ModelModel used
X-Request-IdUnique 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

ReasonDescription
inaccurateFactually incorrect
inappropriateViolates guidelines
off_topicNot relevant to task
low_qualityPoor response quality
otherOther 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

CodeStatusDescription
MESSAGE_NOT_FOUND404Message does not exist
SWARM_NOT_ACCESSIBLE403Cannot access swarm
AGENT_UNAVAILABLE503Agent cannot respond
RATE_LIMITED429Too many requests
CONTENT_FILTERED400Content violates policy
  • [Swarms API](/docs/api/swarms-api): Swarm management
  • [Agents API](/docs/api/agents-api): Agent configuration
  • [Webhooks API](/docs/api/webhooks-api): Event notifications