> ## 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 Request Status

> Poll the status and result of an async chat request.

## Overview

This endpoint returns the current status of an async chat request. Use it to poll until the answer is ready.

The endpoint is read-only and safe to call frequently. Typical completion time is 5-30 seconds; most requests complete within the first 10 seconds.

## Authentication

Requires API key in the `X-API-Key` header:

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

## Path parameters

<ParamField path="id" type="string" required>
  The site ID.
</ParamField>

<ParamField path="requestId" type="string" required>
  The request ID from the async chat submission response.
</ParamField>

## Request example

<CodeGroup>
  ```bash curl 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>"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/requests/${requestId}`,
    {
      headers: {
        "X-API-Key": process.env.UPPZY_API_KEY,
      },
    }
  );

  const data = await response.json();
  console.log(`Status: ${data.status}`);
  ```

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

  response = requests.get(
      f"https://api.uppzy.com/api/v1/m2m/sites/{site_id}/chat/requests/{request_id}",
      headers={"X-API-Key": os.environ["UPPZY_API_KEY"]},
  )

  response.raise_for_status()
  data = response.json()
  print(f"Status: {data['status']}")
  ```
</CodeGroup>

## Response schema

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

<ResponseField name="request_id" type="string">
  The request ID.
</ResponseField>

<ResponseField name="status" type="string">
  One of: `pending`, `completed`, `failed`.

  * `pending`: The request is still processing.
  * `completed`: The answer is ready (see `answer` field).
  * `failed`: The request failed (see `error` field).
</ResponseField>

<ResponseField name="answer" type="string">
  The assistant's response. Only present when `status` is `completed`.
</ResponseField>

<ResponseField name="error" type="string">
  Error description if the request failed. Only present when `status` is `failed`.

  Examples: `"failed to call rag service"`, `"site not found"`.
</ResponseField>

## Response examples

### Pending

<ResponseExample>
  ```json theme={null}
  {
    "session_id": "user-123-web",
    "request_id": "req_550e8400e29b41d4a716446655440000",
    "status": "pending"
  }
  ```
</ResponseExample>

### Completed

<ResponseExample>
  ```json theme={null}
  {
    "session_id": "user-123-web",
    "request_id": "req_550e8400e29b41d4a716446655440000",
    "status": "completed",
    "answer": "We offer standard shipping (5-7 business days) and express shipping (2-3 business days). International shipping is available to most countries."
  }
  ```
</ResponseExample>

### Failed

<ResponseExample>
  ```json theme={null}
  {
    "session_id": "user-123-web",
    "request_id": "req_550e8400e29b41d4a716446655440000",
    "status": "failed",
    "error": "failed to call rag service"
  }
  ```
</ResponseExample>

## Error responses

### 400 Bad Request

Invalid path parameters (e.g., malformed request ID).

```json theme={null}
{
  "error": "site id and request id are required"
}
```

### 401 Unauthorized

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

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

### 403 Forbidden

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

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

### 404 Not Found

The site or request does not exist.

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

Common causes:

* Wrong site ID
* Wrong request ID
* Request has expired or been deleted
* Request belongs to a different site

### 500 Internal Server Error

An unexpected error occurred.

## Implementation tips

### Polling loop pattern

```javascript theme={null}
async function waitForChatCompletion(siteId, requestId, timeoutSeconds = 300) {
  const startTime = Date.now();
  const timeoutMs = timeoutSeconds * 1000;

  while (Date.now() - startTime < timeoutMs) {
    const response = await fetch(
      `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/requests/${requestId}`,
      {
        headers: {
          "X-API-Key": process.env.UPPZY_API_KEY,
        },
      }
    );

    if (!response.ok) {
      // Handle auth or server errors
      throw new Error(`Status check failed: ${response.status}`);
    }

    const data = await response.json();

    if (data.status === "completed") {
      return data.answer;
    }

    if (data.status === "failed") {
      throw new Error(`Chat failed: ${data.error}`);
    }

    // Still pending, wait before next poll
    await new Promise((r) => setTimeout(r, 1000));
  }

  throw new Error("Chat request timed out");
}
```

### Optimal polling interval

* **First poll**: Wait 1-2 seconds (processing takes time)
* **Subsequent polls**: 1-2 second intervals
* **Max attempts**: 120-300 (2-5 minutes)

Most requests complete in 5-30 seconds. Waiting longer helps avoid thundering herd and reduces API load.

### Handling partial failures

If the status endpoint returns 5xx errors, implement exponential backoff:

```javascript theme={null}
async function pollWithBackoff(siteId, requestId) {
  let backoffMs = 1000;
  const maxBackoff = 10000;

  while (true) {
    const response = await fetch(
      `https://api.uppzy.com/api/v1/m2m/sites/${siteId}/chat/requests/${requestId}`,
      {
        headers: { "X-API-Key": process.env.UPPZY_API_KEY },
      }
    );

    if (response.ok) {
      const data = await response.json();
      if (data.status !== "pending") {
        return data;
      }
      backoffMs = 1000; // Reset backoff on success
    } else if (response.status >= 500) {
      // Server error, back off
      backoffMs = Math.min(backoffMs * 1.5, maxBackoff);
    } else if (response.status === 404) {
      // Request not found, stop polling
      throw new Error("Request expired or not found");
    } else {
      // Other client errors
      throw new Error(`Poll failed: ${response.status}`);
    }

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

### Tracking in a database

Store the request ID and check status later:

```sql theme={null}
-- Example: store request info for later polling
INSERT INTO pending_chat_requests (
  request_id,
  site_id,
  user_id,
  submitted_at,
  status
) VALUES (
  'req_...',
  'site_...',
  'user_...',
  NOW(),
  'pending'
);

-- Later: update when complete
UPDATE pending_chat_requests
SET status = 'completed', answer = '...', completed_at = NOW()
WHERE request_id = 'req_...';
```

## Related endpoints

* [M2M Chat (Async)](/api-reference/endpoint/m2m-chat-async) — Submit a request
* [M2M Chat (Sync)](/api-reference/endpoint/m2m-chat-sync) — Immediate answer
* [M2M Chat Feedback](/api-reference/endpoint/m2m-chat-feedback) — Rate the answer
