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

> Submit a chat request and receive a request ID for polling. Process answers in the background.

## Overview

The asynchronous chat endpoint accepts a message and returns immediately with a `request_id`. Your application then polls the [status endpoint](/api-reference/endpoint/m2m-chat-status) until the answer is ready.

Use this when your integration uses background job patterns, message queues, or webhooks to handle results asynchronously.

## When to use async chat

* Your application already uses job or queue processing
* You want non-blocking request handling
* You expect high concurrency or spike traffic
* You prefer polling over waiting for a 60-second response
* You want to decouple request submission from result handling

For immediate synchronous answers, use [sync chat](/api-reference/endpoint/m2m-chat-sync).

## 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.

## 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.
</ParamField>

<ParamField body="email" type="string">
  The user's email address. Required when creating a new session. Optional for follow-up messages.
</ParamField>

<ParamField body="model" type="string">
  The AI model to use. Defaults to the site's configured model or `gpt-4o-mini`.
</ParamField>

<ParamField body="response_language" type="string">
  Language for the response (e.g., `en`, `tr`, `es`).
</ParamField>

<ParamField body="ui_locale" type="string">
  Locale code for UI context (e.g., `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/async" \
    --header "X-API-Key: <YOUR_API_KEY>" \
    --header "Content-Type: application/json" \
    --data '{
      "session_id": "user-123-web",
      "message": "What shipping methods do you support?",
      "email": "customer@example.com"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://api.uppzy.com/api/v1/m2m/sites/<SITE_ID>/chat/async",
    {
      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 shipping methods do you support?",
        email: "customer@example.com",
      }),
    }
  );

  const data = await response.json();
  console.log(`Request ID: ${data.request_id}`);
  // Now poll for status using data.request_id
  ```

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

  response = requests.post(
      "https://api.uppzy.com/api/v1/m2m/sites/<SITE_ID>/chat/async",
      headers={"X-API-Key": os.environ["UPPZY_API_KEY"]},
      json={
          "session_id": "user-123-web",
          "message": "What shipping methods do you support?",
          "email": "customer@example.com",
      },
  )

  response.raise_for_status()
  data = response.json()
  print(f"Request ID: {data['request_id']}")
  # Store the request_id and poll later
  ```
</CodeGroup>

## Response schema (immediate)

<ResponseField name="request_id" type="string">
  A unique identifier for this chat request. Use it to poll the status endpoint.
</ResponseField>

<ResponseField name="session_id" type="string">
  The session identifier for this conversation.
</ResponseField>

<ResponseField name="status" type="string">
  Always `pending` on the initial response.
</ResponseField>

<ResponseField name="placeholder" type="string">
  An optional placeholder message to show the user while processing.
</ResponseField>

## Response example

<ResponseExample>
  ```json theme={null}
  {
    "request_id": "req_550e8400e29b41d4a716446655440000",
    "session_id": "user-123-web",
    "status": "pending",
    "placeholder": "Let me look that up for you..."
  }
  ```
</ResponseExample>

## Polling the status

After receiving the initial response, poll the [status endpoint](/api-reference/endpoint/m2m-chat-status) to check completion:

```bash theme={null}
GET /m2m/sites/{site_id}/chat/requests/{request_id}
```

Example:

```bash theme={null}
curl --request GET \
  --url "https://api.uppzy.com/api/v1/m2m/sites/<SITE_ID>/chat/requests/<REQUEST_ID>" \
  --header "X-API-Key: <YOUR_API_KEY>"
```

## Complete workflow example

<CodeGroup>
  ```javascript JavaScript (with polling) theme={null}
  async function chatAsync(siteId, message, sessionId, email) {
    // Step 1: Submit the request
    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
    let answer = null;
    let status = "pending";
    let attempts = 0;
    const maxAttempts = 120; // 2 minutes with 1s polling

    while (status === "pending" && attempts < maxAttempts) {
      await new Promise((r) => setTimeout(r, 1000)); // Wait 1 second

      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 statusData = await statusRes.json();
      status = statusData.status;

      if (status === "completed") {
        answer = statusData.answer;
      } else if (status === "failed") {
        throw new Error(`Request failed: ${statusData.error}`);
      }

      attempts++;
    }

    if (status !== "completed") {
      throw new Error("Request timed out after 2 minutes");
    }

    return { requestId, answer };
  }
  ```

  ```python Python (with polling) theme={null}
  import requests
  import time
  import os

  def chat_async(site_id, message, session_id, email):
      api_key = os.environ["UPPZY_API_KEY"]
      
      # Step 1: Submit the request
      submit_resp = requests.post(
          f"https://api.uppzy.com/api/v1/m2m/sites/{site_id}/chat/async",
          headers={"X-API-Key": api_key},
          json={
              "session_id": session_id,
              "message": message,
              "email": email,
          },
          timeout=30,
      )
      submit_resp.raise_for_status()
      submitted = submit_resp.json()
      request_id = submitted["request_id"]

      # Step 2: Poll for completion
      max_attempts = 120
      attempt = 0
      
      while attempt < max_attempts:
          time.sleep(1)  # Wait 1 second between polls
          
          status_resp = requests.get(
              f"https://api.uppzy.com/api/v1/m2m/sites/{site_id}/chat/requests/{request_id}",
              headers={"X-API-Key": api_key},
              timeout=30,
          )
          status_resp.raise_for_status()
          status_data = status_resp.json()
          
          if status_data["status"] == "completed":
              return {
                  "request_id": request_id,
                  "answer": status_data["answer"],
              }
          elif status_data["status"] == "failed":
              raise Exception(f"Request failed: {status_data.get('error')}")
          
          attempt += 1
      
      raise Exception("Request timed out after 2 minutes")
  ```
</CodeGroup>

## Error responses

Same error codes as [sync chat](/api-reference/endpoint/m2m-chat-sync):

* `400`: Invalid request
* `401`: Authentication failed
* `403`: Plan does not include M2M API
* `404`: Site not found
* `429`: Rate limited
* `500`: Server error

## Polling strategy

Recommended polling approach:

1. **Initial poll**: Wait 1-2 seconds before first status check
2. **Polling interval**: Check every 1-2 seconds
3. **Timeout**: Stop after 2-5 minutes (typical completion is 5-30 seconds)
4. **Backoff on failure**: If status endpoint returns 5xx, implement exponential backoff

Example adaptive polling:

```javascript theme={null}
async function pollUntilComplete(siteId, requestId, maxWaitSeconds = 300) {
  const startTime = Date.now();
  const maxWait = maxWaitSeconds * 1000;
  
  let pollIntervalMs = 1000; // Start with 1 second
  
  while (Date.now() - startTime < maxWait) {
    const resp = await fetch(
      `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/requests/${requestId}`,
      {
        headers: { "X-API-Key": process.env.UPPZY_API_KEY },
      }
    );

    if (resp.ok) {
      const data = await resp.json();
      if (data.status === "completed" || data.status === "failed") {
        return data;
      }
      // Reset interval on success
      pollIntervalMs = 1000;
    } else if (resp.status >= 500) {
      // Back off on server errors
      pollIntervalMs = Math.min(pollIntervalMs * 1.5, 10000);
    }

    await new Promise((r) => setTimeout(r, pollIntervalMs));
  }

  throw new Error("Polling timeout");
}
```

## Comparison: async vs sync

| Aspect                  | Async                       | Sync                           |
| ----------------------- | --------------------------- | ------------------------------ |
| **Implementation**      | Submit + poll               | Wait for answer                |
| **Latency to response** | 0.1s (submit)               | 5-60s                          |
| **Scalability**         | Better for high concurrency | Limited by request connections |
| **Complexity**          | More code (polling loop)    | Simpler                        |
| **Best for**            | Background jobs, queues     | Real-time chat UI              |

## Related endpoints

* [M2M Chat (Sync)](/api-reference/endpoint/m2m-chat-sync) — Blocking alternative
* [M2M Chat Status](/api-reference/endpoint/m2m-chat-status) — Poll request status
* [M2M Chat Feedback](/api-reference/endpoint/m2m-chat-feedback) — Submit quality feedback
* [M2M Sessions](/api-reference/endpoint/m2m-sessions) — List conversations
