> ## 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 (Sync)

> Send a message to the AI assistant and receive the answer in a single request.

## Overview

The synchronous chat endpoint sends a message to the AI assistant and waits for a complete response. Use this endpoint when your integration needs an immediate answer and can tolerate request latency up to 60 seconds.

## When to use sync chat

* The caller (application or user) is waiting for the answer
* Request volume is moderate (M2M endpoints are rate limited to 5 req/sec per client IP)
* Your integration can handle response latency of 5-60 seconds
* You want the simplest integration pattern

For high-volume or background workloads, consider [async chat](/api-reference/endpoint/m2m-chat-async).

## Authentication

This endpoint requires an API key in the `X-API-Key` header:

```http theme={null}
X-API-Key: <YOUR_API_KEY>
```

The API key must belong to a tenant that owns the specified site. If the key is expired, revoked, or from a different tenant, the request fails with `401` or `403`.

## Request body

<ParamField body="message" type="string" required>
  The user's question or statement. Required.
</ParamField>

<ParamField body="session_id" type="string">
  A stable identifier for the conversation. If omitted, a new session is created. For follow-up questions, include the same `session_id` to maintain context.

  Format: typically a UUID or stable string unique to the user/channel.

  Example: `user-123-web`, `conversation-abc-def`, or a UUID.
</ParamField>

<ParamField body="email" type="string">
  The user's email address. Required when creating a new session (no `session_id` provided). Optional for follow-up messages in an existing session.

  The email is used to associate chat history with a user account.
</ParamField>

<ParamField body="model" type="string">
  The AI model to use for this request. If omitted, defaults to the site's configured default model or `gpt-4o-mini`.

  Common values: `gpt-4o-mini`, `gpt-4-turbo`, `claude-3-5-sonnet-20241022`.

  Check [available models](/api-reference/endpoint/tenant-api-key-list) for your tenant.
</ParamField>

<ParamField body="response_language" type="string">
  Language for the assistant's response. If omitted, the assistant chooses based on the input message.

  Example values: `en`, `tr`, `es`, `fr`, `de`.
</ParamField>

<ParamField body="ui_locale" type="string">
  Locale code for the UI context (used for formatting, not response language).

  Example: `en-US`, `tr-TR`.
</ParamField>

## Request example

<CodeGroup>
  ```bash curl theme={null}
  curl --request POST \
    --url "https://api.uppzy.com/api/v1/m2m/sites/<SITE_ID>/chat" \
    --header "X-API-Key: <YOUR_API_KEY>" \
    --header "Content-Type: application/json" \
    --data '{
      "session_id": "user-123-web",
      "message": "What is your return policy?",
      "email": "customer@example.com",
      "response_language": "en"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://api.uppzy.com/api/v1/m2m/sites/<SITE_ID>/chat",
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.UPPZY_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        session_id: "user-123-web",
        message: "What is your return policy?",
        email: "customer@example.com",
        response_language: "en",
      }),
    }
  );

  if (!response.ok) {
    throw new Error(`Chat failed: ${response.status}`);
  }

  const data = await response.json();
  console.log(data.answer);
  ```

  ```python Python theme={null}
  import requests
  import os

  response = requests.post(
      "https://api.uppzy.com/api/v1/m2m/sites/<SITE_ID>/chat",
      headers={"X-API-Key": os.environ["UPPZY_API_KEY"]},
      json={
          "session_id": "user-123-web",
          "message": "What is your return policy?",
          "email": "customer@example.com",
          "response_language": "en",
      },
      timeout=60,
  )

  response.raise_for_status()
  data = response.json()
  print(data["answer"])
  ```
</CodeGroup>

## Response schema

<ResponseField name="session_id" type="string">
  The session identifier for this conversation. Use this in follow-up requests to maintain context.
</ResponseField>

<ResponseField name="request_id" type="string">
  A unique identifier for this chat request. Use it to reference this specific Q\&A pair when submitting feedback or checking logs.
</ResponseField>

<ResponseField name="answer" type="string">
  The assistant's response to the user's message.
</ResponseField>

<ResponseField name="confidence_score" type="integer">
  A score from 0-100 indicating confidence in the answer (higher is more confident). Omitted if not available.
</ResponseField>

<ResponseField name="confidence_level" type="string">
  Enum: `low`, `medium`, `high`. Summarizes the confidence score. Omitted if not available.
</ResponseField>

<ResponseField name="confidence_reason" type="string">
  Human-readable explanation of the confidence level (e.g., "Based on multiple supporting documents"). Omitted if not available.
</ResponseField>

## Response example

<ResponseExample>
  ```json theme={null}
  {
    "session_id": "user-123-web",
    "request_id": "req_550e8400e29b41d4a716446655440000",
    "answer": "Returns are accepted within 30 days of purchase with proof of purchase. Items must be in original condition.",
    "confidence_score": 92,
    "confidence_level": "high",
    "confidence_reason": "Based on multiple supporting documents in the knowledge base."
  }
  ```
</ResponseExample>

## Error responses

### 400 Bad Request

Missing or invalid fields in the request body.

```json theme={null}
{
  "error": "invalid payload"
}
```

Common causes:

* Missing `message` field
* Invalid JSON structure
* Missing `email` for a new session (no `session_id` provided)

### 401 Unauthorized

API key is missing, invalid, expired, or revoked.

```json theme={null}
{
  "error": "invalid_api_key"
}
```

Common causes:

* Malformed or missing `X-API-Key` header
* Key has been revoked
* Key has expired
* Key belongs to a different tenant

### 403 Forbidden

The API key's plan does not include M2M API access.

```json theme={null}
{
  "error": "api_access_not_available_on_plan",
  "message": "API Access is available only for Level 2 and Level 3 plans."
}
```

### 404 Not Found

The site does not exist or does not belong to the authenticated tenant.

```json theme={null}
{
  "error": "site not found"
}
```

Common causes:

* Wrong site ID in the path
* Site has been deleted
* Site belongs to a different tenant

### 429 Too Many Requests

Rate limit exceeded. M2M endpoints allow up to 5 requests per second (rate limiting is applied per client IP and request path). When this limit is exceeded, requests are rejected until the window resets — wait at least one second before retrying.

```json theme={null}
{
  "error": "too many requests. please try again later"
}
```

Implement exponential backoff (e.g., 1s, 2s, 4s, 8s).

### 500 Internal Server Error

An unexpected error occurred on the Uppzy side.

```json theme={null}
{
  "error": "failed to call rag service"
}
```

Rare, but can happen if:

* The RAG service is temporarily unavailable
* The LLM provider is unreachable
* An internal service dependency failed

Implement bounded retries with backoff.

## Implementation tips

### Session strategy

Choose a stable `session_id` that groups related messages:

* **Per user**: `user-123` keeps all conversations for that user together
* **Per channel**: `slack-channel-abc` for messaging integrations
* **Per thread**: `support-ticket-456` for support systems

Consistency matters: the same user should use the same session ID across requests.

### Handling confidence

Check `confidence_level` or `confidence_score` to decide whether to:

* Show the answer directly to users
* Flag low-confidence answers with "This answer may be incomplete"
* Route high-uncertainty cases to a human agent

### Timeout handling

The endpoint has a 60-second ceiling. In practice:

* Simple answers return in 1-5 seconds
* Complex queries or cold LLM starts may take 10-30 seconds
* Embeddings generation or very large RAG sets can exceed 30 seconds

If your HTTP client times out before the endpoint completes, the request may still process on the server. Use the `request_id` to poll the [async status endpoint](/api-reference/endpoint/m2m-chat-status) if needed.

### Retry strategy

Implement exponential backoff for transient failures:

```javascript theme={null}
async function chatWithRetry(siteId, request, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      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(request),
        }
      );

      if (response.ok) {
        return await response.json();
      }

      // Don't retry 4xx errors except 429
      if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        throw new Error(`Unrecoverable error: ${response.status}`);
      }

      // Retry 429 and 5xx
      if (attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
        await new Promise((r) => setTimeout(r, delay));
      }
    } catch (err) {
      if (attempt === maxRetries - 1) throw err;
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise((r) => setTimeout(r, delay));
    }
  }
}
```

## Related endpoints

* [M2M Chat (Async)](/api-reference/endpoint/m2m-chat-async) — Non-blocking alternative
* [M2M Chat Status](/api-reference/endpoint/m2m-chat-status) — Check async request status
* [M2M Chat Feedback](/api-reference/endpoint/m2m-chat-feedback) — Submit quality feedback
* [M2M Sessions](/api-reference/endpoint/m2m-sessions) — List user conversations
