> ## Documentation Index
> Fetch the complete documentation index at: https://ago.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Send Message API

> Send messages to AI agents and manage conversations

This API endpoint allows you to send messages to AI agents and create new conversations or continue existing ones. It
supports real-time streaming responses, file attachments, and background agent processing.

## API Endpoint

```
POST /api/business_messages/sync/handle-message-conversation
```

**Authentication:** Requires authenticated user
**Content-Type:** `application/json` or `multipart/form-data` (when files are included)
**Response Format:** Server-Sent Events (SSE) stream

## Overview

The send message API allows you to:

* Start new conversations with AI agents
* Continue existing conversation threads
* Upload file attachments (PDF, images, documents)
* Receive real-time streaming responses
* Use background agents for long-running tasks
* Specify agent or use permission-based agent selection

## Request Parameters

### Query Parameters

| Parameter       | Type   | Required | Description                                                 |
| --------------- | ------ | -------- | ----------------------------------------------------------- |
| `agent_id`      | string | No       | Specific agent UUID to use (takes priority over permission) |
| `permission_id` | string | No       | Permission ID to select default agent for that permission   |

<Note>
  If neither `agent_id` nor `permission_id` is provided, the system will use the user's default agent. Even if an `agent_id` is provided, the system will verify that the user has permission to use that agent.
</Note>

### Request Body (JSON)

The simplest request body is:

```json theme={null}
{
  "content": "Your message content here"
}
```

When you want to reply to an existing conversation thread, include the `thread` object:

```json theme={null}
{
  "content": "Your message content here",
  "thread": {
    "id": "existing-thread-uuid"
  }
}
```

#### Body Fields

| Field       | Type      | Required | Description                                                                                           |
| ----------- | --------- | -------- | ----------------------------------------------------------------------------------------------------- |
| `content`   | string    | Yes      | The message text content                                                                              |
| `thread`    | object    | No       | Thread object with `id` field for continuing conversation                                             |
| `isStaff`   | boolean   | No       | Whether this is a staff message (default: false). When true, role is set to "Staff", otherwise "user" |
| `languages` | string\[] | No       | User's preferred languages (default: \["en"])                                                         |

<Info>
  IsStaff sets the message role to "Staff". We use it when a human staff member is sending the message, otherwise the role is "user". Languages are used to return document titles and other metadata in the user's preferred language.
</Info>

### Request Body (FormData with Files)

When uploading files, use `multipart/form-data` format:

* **message**: JSON string containing the message data (same structure as JSON body above)
* **files**: One or more file uploads (max 5 files, 10MB each)

<Warning>
  #### File Upload Constraints

  | Constraint    | Value                                                             |
  | ------------- | ----------------------------------------------------------------- |
  | Max files     | 5 files per request                                               |
  | Max file size | 10MB per file                                                     |
  | Allowed types | PDF, images (PNG, JPG, JPEG), documents (DOCX, TXT), spreadsheets |
  | Organization  | File attachments must be enabled in organization config           |
</Warning>

## Response Format

The API returns a Server-Sent Events (SSE) stream with multiple event types:

### 1. Agent and Thread Information

First event sent when starting a conversation:

```json theme={null}
{
  "agent": {
    "id": "agent-uuid",
    "name": "Customer Support AI",
    "icon": "🤖",
    "customIcon": "icon-uuid",
    "displayName": "Support Agent",
    "is_background_agent": false
  },
  "thread": {
    "id": "thread-uuid"
  },
  "message_id": "message-uuid",
  "status": "IN_PROGRESS",
  "redirect-to-thread-page": true
}
```

### 2. Knowledge Sources

Sources used by the agent to answer the question:

```json theme={null}
{
  "agent": {
    ...
  },
  "knowledge_sources": [
    {
      "id": "doc-uuid",
      "title": "User Guide",
      "source_name": "Documentation",
      "position": 1,
      "url": "https://docs.example.com/guide"
    }
  ],
  "thread": {
    "id": "thread-uuid"
  },
  "timestamp": "2024-12-06T14:30:00Z",
  "message_id": "message-uuid",
  "status": "IN_PROGRESS"
}
```

### 3. Streaming Content

Incremental content chunks as the agent generates the response:

```json theme={null}
{
  "content": "partial response text...",
  "thread": {
    "id": "thread-uuid"
  }
}
```

### 4. Completion Status

Final event when the response is complete:

```json theme={null}
{
  "thread": {
    "id": "thread-uuid"
  },
  "message_id": "message-uuid",
  "status": "DONE"
}
```

### 5. Thread Title Generation

Automatic title generation for new threads:

```json theme={null}
{
  "thread": {
    "id": "thread-uuid",
    "title": "How to reset password"
  }
}
```

## Response Status Values

| Status        | Description                               |
| ------------- | ----------------------------------------- |
| `IN_PROGRESS` | Agent is processing the message           |
| `DONE`        | Message processing completed successfully |
| `ERROR`       | An error occurred during processing       |

## Background Agents

When using a background agent (for long-running tasks):

1. The initial response confirms the background task has started
2. Use the progress stream endpoint to monitor status
3. The agent will process the request asynchronously
4. Progress updates are stored in the message's `background_agent_progress` field

**Progress Stream Endpoint:**

```
GET /api/business_messages/sync/message/{message_id}/progress-stream
```

See the [Background Agent Progress](#monitoring-background-agents) section for details.

## Usage Examples

### Basic Request - Start New Conversation

```bash theme={null}
curl -X POST "https://your_tenant.api.example.com/api/business_messages/sync/handle-message-conversation" \
     -H "x-api-key: YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "content": "How do I reset my password?",
       "languages": ["en"]
     }'
```

### Continue Existing Conversation

```bash theme={null}
curl -X POST "https://your_tenant.api.example.com/api/business_messages/sync/handle-message-conversation" \
     -H "x-api-key: YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "content": "Can you provide more details?",
       "thread": {
         "id": "existing-thread-uuid"
       },
       "languages": ["en"]
     }'
```

### Send Message to Specific Agent

```bash theme={null}
curl -X POST "https://your_tenant.api.example.com/api/business_messages/sync/handle-message-conversation?agent_id=agent-uuid" \
     -H "x-api-key: YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "content": "I need technical support",
       "languages": ["en"]
     }'
```

### Send Message with File Attachments

```bash theme={null}
curl -X POST "https://your_tenant.api.example.com/api/business_messages/sync/handle-message-conversation" \
     -H "x-api-key: YOUR_API_KEY" \
     -F 'message={"content":"Please analyze this document","languages":["en"]}' \
     -F "files=@document.pdf" \
     -F "files=@screenshot.png"
```

### Use Permission-Based Agent Selection

```bash theme={null}
curl -X POST "https://your_tenant.api.example.com/api/business_messages/sync/handle-message-conversation?permission_id=premium" \
     -H "x-api-key: YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "content": "I have a premium support question",
       "languages": ["en"]
     }'
```

### JavaScript/TypeScript Example with SSE

```typescript theme={null}
async function sendMessage(content: string, threadId?: string, files?: File[]) {
    const endpoint = threadId
        ? `/api/business_messages/sync/handle-message-conversation`
        : `/api/business_messages/sync/handle-message-conversation?agent_id=${agentId}`;

    let body;
    let headers: any = {};

    if (files && files.length > 0) {
        // Use FormData for file uploads
        const formData = new FormData();
        formData.append('message', JSON.stringify({
            content,
            thread: threadId ? {id: threadId} : undefined,
            languages: navigator.languages
        }));

        files.forEach(file => formData.append('files', file));
        body = formData;
    } else {
        // Use JSON for text-only messages
        headers['Content-Type'] = 'application/json';
        body = JSON.stringify({
            content,
            thread: threadId ? {id: threadId} : undefined,
            languages: navigator.languages
        });
    }

    const response = await fetch(endpoint, {
        method: 'POST',
        headers,
        body
    });

    // Process SSE stream
    const reader = response.body?.getReader();
    const decoder = new TextDecoder();

    while (true) {
        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: ')) {
                const data = JSON.parse(line.slice(6));

                if (data.content) {
                    // Handle streaming content
                    console.log('Content chunk:', data.content);
                } else if (data.knowledge_sources) {
                    // Handle knowledge sources
                    console.log('Sources:', data.knowledge_sources);
                } else if (data.status === 'DONE') {
                    // Handle completion
                    console.log('Message complete');
                }
            }
        }
    }
}
```

## Monitoring Background Agents

For background agents, use the progress stream endpoint to monitor execution:

```bash theme={null}
curl -X GET "https://your_tenant.api.example.com/api/business_messages/sync/message/{message_id}/progress-stream" \
     -H "x-api-key: YOUR_API_KEY"
```

### Progress Stream Events

```json theme={null}
{
  "type": "progress",
  "message": "Retrieving relevant documents...",
  "timestamp": "2024-12-06T14:30:15Z"
}
```

```json theme={null}
{
  "type": "result",
  "success": true,
  "result": "Task completed successfully with these findings...",
  "timestamp": "2024-12-06T14:35:00Z"
}
```

## Fallback Polling System

If streaming fails or is not supported (some mobile environments, proxies), use the message status endpoint for polling.

### When to Use Polling

* SSE connection fails or times out
* Behind proxy that doesn't support streaming
* Mobile SDK with limited SSE support
* Testing/debugging scenarios

### Message Status Endpoint

```
GET /api/business_messages/sync/message-status/{message_id}
```

```bash theme={null}
curl -X GET "https://your_tenant.api.example.com/api/business_messages/sync/message-status/message-uuid" \
     -H "x-api-key: YOUR_API_KEY"
```

**Response:**

```json theme={null}
{
  "message_id": "message-uuid",
  "status": "IN_PROGRESS",
  "content": "Current response text...",
  "full_content": null,
  "progress": 0.65,
  "knowledge_sources": [],
  "timestamp": "2024-12-06T14:30:00Z"
}
```

**Completed Response:**

```json theme={null}
{
  "message_id": "message-uuid",
  "status": "DONE",
  "content": null,
  "full_content": "Complete response text with all content...",
  "knowledge_sources": [
    {
      "id": "doc-uuid",
      "title": "User Guide",
      "url": "https://docs.example.com/guide"
    }
  ],
  "timestamp": "2024-12-06T14:30:45Z"
}
```

### Polling Implementation

Poll every 1-2 seconds until `status` is `DONE` or `ERROR`:

```typescript theme={null}
async function pollForResponse(messageId: string): Promise<MessageResponse> {
  const maxAttempts = 60; // 2 minutes max
  const pollInterval = 2000; // 2 seconds

  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const response = await fetch(
      `/api/business_messages/sync/message-status/${messageId}`,
      { headers: { 'x-api-key': API_KEY } }
    );

    const data = await response.json();

    // Update UI with progress
    if (data.content) {
      updatePartialContent(data.content);
    }

    if (data.status === 'DONE') {
      return {
        content: data.full_content,
        sources: data.knowledge_sources
      };
    }

    if (data.status === 'ERROR') {
      throw new Error('Message processing failed');
    }

    // Wait before next poll
    await new Promise(resolve => setTimeout(resolve, pollInterval));
  }

  throw new Error('Polling timeout');
}

// Usage with streaming fallback
async function sendMessageWithFallback(content: string) {
  const response = await fetch('/api/business_messages/sync/handle-message-conversation', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ content })
  });

  // Extract message_id from initial response
  const reader = response.body?.getReader();
  const firstChunk = await reader?.read();
  const initialData = JSON.parse(
    new TextDecoder().decode(firstChunk?.value).split('data: ')[1]
  );

  if (!response.ok || !reader) {
    // Fallback to polling
    return pollForResponse(initialData.message_id);
  }

  // Continue with SSE streaming...
}
```

### Polling Best Practices

| Practice         | Recommendation                                        |
| ---------------- | ----------------------------------------------------- |
| Poll interval    | 1-2 seconds (balance between responsiveness and load) |
| Max duration     | 2-3 minutes (timeout for background agents)           |
| Progress display | Update UI with partial `content` while polling        |
| Error handling   | Retry on network errors, stop on 4xx errors           |

## Error Responses

### 400 Bad Request - Invalid Request Body

```json theme={null}
{
  "error": "Invalid request body: Missing required field 'content'"
}
```

### 400 Bad Request - Invalid UUID Format

```json theme={null}
{
  "error": "Invalid thread ID format."
}
```

### 403 Forbidden - File Attachments Disabled

```json theme={null}
{
  "error": "File attachments are not enabled for this organization"
}
```

### 400 Bad Request - File Too Large

```json theme={null}
{
  "error": "File document.pdf is too large (max: 10MB)"
}
```

### 400 Bad Request - Too Many Files

```json theme={null}
{
  "error": "Cannot upload more than 5 files at once"
}
```

### 400 Bad Request - Invalid File Type

```json theme={null}
{
  "error": "File type application/exe is not allowed for file program.exe"
}
```

### 404 Not Found - Agent Not Found

```json theme={null}
{
  "error": "Agent not found or user does not have permission"
}
```

## Integration Notes

<Note>
  ### Agent Selection Priority

  The system selects an agent in this order:

  1. **agent\_id** parameter (if provided) - highest priority
  2. **permission\_id** parameter (selects default agent for that permission)
  3. User's default agent (fallback)
</Note>

### Message Processing Flow

1. User message is saved to the database
2. Agent is selected based on parameters
3. File attachments are validated and stored (if present)
4. For background agents: task is queued for async processing
5. For standard agents:
   * Relevant documents are retrieved from knowledge base
   * Documents are reranked if enabled
   * Additional context is added to the prompt
   * Response is streamed back via SSE
6. Assistant message is saved with sources and status
7. Thread title is generated for new conversations

### Tool Integration

Agents can use tools during message processing. Tool calls and responses are stored in the conversation history and
included in subsequent context.

## Best Practices when implementing the Send Message API in your client

<Tip>
  ### Client Implementation

  * **Always handle streaming**: Implement proper SSE parsing for real-time responses
  * **Implement fallback polling**: Use the message status endpoint if streaming fails
  * **Show progress indicators**: Update UI in real-time as content streams
  * **Handle reconnection**: Retry stream connections on network errors
  * **Display sources**: Show knowledge sources to build user trust
</Tip>

<Tip>
  ### File Uploads

  * **Validate client-side**: Check file size and type before upload to save bandwidth
  * **Show upload progress**: Provide feedback during file upload
  * **Handle errors gracefully**: Display clear error messages for file issues
  * **Preview files**: Let users review files before sending
</Tip>

<Tip>
  ### Performance

  * **Limit file sizes**: Keep files under 10MB for optimal processing
  * **Use appropriate agents**: Select specialized agents for specific tasks
  * **Monitor background tasks**: Check progress regularly for long-running operations
</Tip>

<Warning>
  ### Security

  * **Sanitize content**: Clean user input before processing
  * **Check file types**: Only allow safe file types for upload
  * **Rate limiting**: Implement rate limits to prevent abuse
</Warning>
