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

# M2M Chat Endpoints Guide

> Complete reference for all M2M chat endpoints: sync, async, status polling, feedback, and session management.

This guide maps the five M2M chat endpoints to real integration patterns. Use it to understand which endpoint to call when.

## Quick reference

| Endpoint                                                        | Method                                          | Purpose                                  | Response Time | Use When                      |
| --------------------------------------------------------------- | ----------------------------------------------- | ---------------------------------------- | ------------- | ----------------------------- |
| [Chat (Sync)](/api-reference/endpoint/m2m-chat-sync)            | POST `/m2m/sites/{id}/chat`                     | Send message and get answer immediately  | 5-60s         | Caller needs immediate answer |
| [Chat (Async)](/api-reference/endpoint/m2m-chat-async)          | POST `/m2m/sites/{id}/chat/async`               | Submit message for background processing | 100ms         | Caller should not block       |
| [Chat Status](/api-reference/endpoint/m2m-chat-status)          | GET `/m2m/sites/{id}/chat/requests/{requestId}` | Check status of async request            | 10ms          | Polling async completion      |
| [Chat Feedback](/api-reference/endpoint/m2m-chat-feedback)      | POST `/m2m/sites/{id}/chat/feedback`            | Rate the response quality                | 100ms         | Improving answer quality      |
| [Session List](/api-reference/endpoint/m2m-sessions)            | GET `/m2m/sites/{id}/sessions`                  | List user conversations                  | 100ms         | Tracking sessions             |
| [Session Close](/api-reference/endpoint/m2m-chat-session-close) | POST `/m2m/sites/{id}/chat/session/close`       | Mark session as closed                   | 100ms         | Ending conversation           |

## Pattern 1: Synchronous chat (blocking)

Use when the user is waiting for the answer.

```
User sends message
       ↓
POST /m2m/sites/{id}/chat
       ↓
[Assistant processes for 5-60 seconds]
       ↓
Return answer + confidence immediately
```

**Typical latency**: 5-30 seconds. Simple answers faster, complex queries slower.

**Example**: Customer support chatbot in a web form.

```javascript theme={null}
async function answerQuestion(siteId, userQuestion, userEmail) {
  const response = await fetch(
    `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat`,
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.UPPZY_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        session_id: `user-${userEmail}`,
        email: userEmail,
        message: userQuestion,
      }),
    }
  );

  const data = await response.json();
  return data.answer;
}
```

## Pattern 2: Asynchronous chat (non-blocking)

Use when you have a job queue or background processing system.

```
User sends message
       ↓
POST /m2m/sites/{id}/chat/async
       ↓
Return immediately with request_id (100ms)
       ↓
[Processing continues in background]
       ↓
App polls GET /m2m/sites/{id}/chat/requests/{requestId}
       ↓
When status changes to 'completed', deliver answer
```

**Typical latency**: 100ms submit + 5-30 seconds processing + polling.

**Example**: Background job processor, message queue consumer, webhook receiver.

```javascript theme={null}
async function processAsyncChat(siteId, sessionId, message, email) {
  // Step 1: Submit the request (returns immediately)
  const submitRes = await fetch(
    `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/async`,
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.UPPZY_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ session_id: sessionId, message, email }),
    }
  );

  const submitted = await submitRes.json();
  const requestId = submitted.request_id;

  // Step 2: Poll for completion (every 1 second, up to 2 minutes)
  let answer = null;
  for (let i = 0; i < 120; i++) {
    const statusRes = await fetch(
      `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/requests/${requestId}`,
      {
        headers: { "X-API-Key": process.env.UPPZY_API_KEY },
      }
    );

    const status = await statusRes.json();
    if (status.status === "completed") {
      answer = status.answer;
      break;
    }
    if (status.status === "failed") {
      throw new Error(`Chat failed: ${status.error}`);
    }

    // Wait 1 second before next poll
    await new Promise((r) => setTimeout(r, 1000));
  }

  if (!answer) throw new Error("Chat polling timed out");
  return answer;
}
```

## Pattern 3: Collect feedback (quality monitoring)

Use after each completed response to track answer quality.

```
Response shown to user
       ↓
User rates: "good" or "bad"
       ↓
POST /m2m/sites/{id}/chat/feedback
       ↓
Feedback stored in dashboard
```

**Example**: Chat widget with thumbs up/down buttons.

```javascript theme={null}
async function submitFeedback(siteId, sessionId, requestId, isSatisfied) {
  const feedback = isSatisfied ? "good" : "bad";

  const response = await fetch(
    `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/feedback`,
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.UPPZY_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        session_id: sessionId,
        request_id: requestId,
        feedback,
      }),
    }
  );

  return response.json();
}
```

## Pattern 4: Retrieve conversation history

Use to look up past sessions for a user.

```
App needs to find user's conversations
       ↓
GET /m2m/sites/{id}/sessions?email=user@example.com
       ↓
Returns: [{ id, created_at, message_count, ... }]
       ↓
App can display conversation list or resume
```

**Example**: Multi-session support ticket system.

```javascript theme={null}
async function getUserSessions(siteId, userEmail) {
  const response = await fetch(
    `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/sessions?email=${encodeURIComponent(userEmail)}`,
    {
      headers: { "X-API-Key": process.env.UPPZY_API_KEY },
    }
  );

  const data = await response.json();

  // Find most recent active session
  const activeSession = data.sessions
    .filter((s) => s.closed_at === null)
    .sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at))[0];

  return { allSessions: data.sessions, activeSession };
}
```

## Pattern 5: Close sessions explicitly

Use when a conversation ends (user resolution, timeout, etc.).

```
Conversation ends (user resolved issue, or timeout)
       ↓
POST /m2m/sites/{id}/chat/session/close
       ↓
Session marked as closed in dashboard
       ↓
If user returns, create new session for fresh context
```

**Example**: Support ticket closure, conversation timeout.

```javascript theme={null}
async function closeConversation(siteId, sessionId) {
  const response = await fetch(
    `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/session/close`,
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.UPPZY_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ session_id: sessionId }),
    }
  );

  return response.json();
}
```

## Session strategy

Before using any chat endpoint, define your `session_id` strategy:

### Option A: Per-user sessions

Best for chat applications where one user = one long conversation.

```javascript theme={null}
const sessionId = `user-${userId}`;
```

Characteristics:

* One session per user account
* Conversation history grows over time
* Follow-up questions maintain context
* Close explicitly when user logs out

### Option B: Per-thread sessions

Best for ticket or thread-based systems.

```javascript theme={null}
const sessionId = `ticket-${ticketId}`;
```

Characteristics:

* One session per support ticket
* Clear conversation boundaries
* Easy to archive
* Close when ticket resolved

### Option C: Per-channel sessions

Best for multi-channel integrations (email, slack, etc.).

```javascript theme={null}
const sessionId = `slack-user-${userId}-${channelId}`;
```

Characteristics:

* One session per channel + user combo
* Separate context per channel
* Can close after inactivity
* Useful for omnichannel setups

## Request/response flow

### Sync chat flow

```
REQUEST:
  POST /m2m/sites/{siteId}/chat
  {
    "session_id": "user-123",
    "message": "How do I return an item?",
    "email": "user@example.com"
  }

RESPONSE (after 5-60 seconds):
  {
    "session_id": "user-123",
    "request_id": "req_xyz...",
    "answer": "Returns are accepted within 30 days...",
    "confidence_score": 92,
    "confidence_level": "high"
  }
```

### Async chat flow

```
REQUEST 1:
  POST /m2m/sites/{siteId}/chat/async
  { "session_id": "...", "message": "..." }

RESPONSE 1 (100ms):
  {
    "request_id": "req_xyz...",
    "status": "pending"
  }

REQUEST 2 (after waiting):
  GET /m2m/sites/{siteId}/chat/requests/req_xyz...

RESPONSE 2 (when complete):
  {
    "request_id": "req_xyz...",
    "status": "completed",
    "answer": "Returns are accepted within 30 days..."
  }
```

### Feedback flow

```
REQUEST:
  POST /m2m/sites/{siteId}/chat/feedback
  {
    "session_id": "user-123",
    "request_id": "req_xyz...",
    "feedback": "good"
  }

RESPONSE:
  {
    "status": "ok",
    "request_id": "req_xyz...",
    "feedback": "good",
    "feedback_submitted_at": "2024-06-13T14:32:45Z"
  }
```

## Common patterns

### Pattern: "Sync with fallback"

Try sync chat first, fall back to a template answer if it times out.

```javascript theme={null}
async function askWithFallback(siteId, question, userEmail) {
  try {
    // Try sync first
    const response = await fetch(
      `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat`,
      {
        method: "POST",
        headers: {
          "X-API-Key": process.env.UPPZY_API_KEY,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          session_id: `user-${userEmail}`,
          email: userEmail,
          message: question,
        }),
        signal: AbortSignal.timeout(15000), // 15 second timeout
      }
    );

    if (response.ok) {
      const data = await response.json();
      return data.answer;
    }
  } catch (err) {
    // Timeout or error occurred
  }

  // Fallback
  return "Thank you for your question. Our team will respond shortly.";
}
```

### Pattern: "Async with webhook"

Submit async chat and notify via webhook when complete.

```javascript theme={null}
// Submit async request
const submitRes = await fetch(
  `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/async`,
  { method: "POST", ... }
);
const { request_id } = await submitRes.json();

// Store request_id in database
await db.insert("pending_chats", {
  request_id,
  user_id: userId,
  webhook_url: "https://myapp.com/api/chat-complete",
  created_at: new Date(),
});

// Background job polls and sends webhook
setInterval(async () => {
  const pendingChats = await db.query(
    "SELECT * FROM pending_chats WHERE completed_at IS NULL"
  );

  for (const chat of pendingChats) {
    const statusRes = await fetch(
      `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/requests/${chat.request_id}`,
      { headers: { "X-API-Key": API_KEY } }
    );

    const status = await statusRes.json();
    if (status.status === "completed") {
      // Send webhook
      await fetch(chat.webhook_url, {
        method: "POST",
        body: JSON.stringify({
          request_id: chat.request_id,
          answer: status.answer,
        }),
      });

      // Mark as completed
      await db.update("pending_chats", { completed_at: new Date() });
    }
  }
}, 2000); // Poll every 2 seconds
```

## Error handling checklist

✓ Treat `400` as a payload error (fix request)\
✓ Treat `401` as auth failure (check key, rotation)\
✓ Treat `403` as access denied (check plan/permissions)\
✓ Treat `404` as resource not found (check IDs)\
✓ Retry `429` and `5xx` with exponential backoff\
✓ Don't retry `4xx` (except `429`) without fixing the request\
✓ Async polling has an overall timeout\
✓ User-facing errors are safe and don't expose API details

See [Error Handling Catalog](/developer/error-handling-catalog) for complete guidance.

## Production readiness

Before deploying chat integrations:

✓ All HTTP calls have timeouts\
✓ Retries are bounded and selective\
✓ Session IDs are stable across requests\
✓ Feedback collection is wired correctly\
✓ Async polling respects timeouts\
✓ Monitoring and alerting are configured\
✓ Logs do not contain API keys\
✓ User-facing errors are safe\
✓ Staging smoke test passed

## Related resources

* [M2M API Overview](/developer/m2m-api) — High-level architecture
* [API Authentication](/developer/api-authentication) — Key management
* [M2M Integration Guidelines](/developer/m2m-best-practices) — Production rules
* [Error Handling Catalog](/developer/error-handling-catalog) — How to handle failures
* [M2M Code Samples](/developer/m2m-code-samples) — Working examples
