> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs-parschat.parstechai.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-parschat.parstechai.com/_mcp/server.

# پاسخ به مشتریان

> از پنل خودتان با متن، فایل و پیام صوتی به مشتریان نهایی پاسخ دهید.

> **Warning**
>
> **نسخه پیش‌انتشار `v1`، منتشرشده در ۲۰۲۶-۰۹-۱۴.** این صفحه شکل API را تعهد می‌کند، نه تاریخ
> آن را. دقیقاً همین چیزی را که این‌جا مستند شده می‌سازیم. شکل API تا **۲۰۲۶-۱۰-۱۴** هنوز ممکن است
> تغییر کند؛ پس از آن، تغییرات از [نسخه‌بندی و پایداری](/documentation/reference/versioning-stability) پیروی می‌کنند.
>
> این بنر صفحه‌به‌صفحه و هم‌زمان با فعال‌شدن هر مسیر برداشته می‌شود. تا وقتی این‌جاست، بر اساس
> قرارداد پیاده‌سازی کنید و فرض کنید این مسیر هنوز قابل فراخوانی نیست.

اپراتورهای شما از پنل **شما** به مشتریان نهایی پاسخ می‌دهند، نه از پنل ما. یک گفتگو به وب‌هوک شما
می‌رسد، کارشناس شما پاسخی می‌نویسد و شما آن را از طریق این API برمی‌گردانید. پیام از همان کانالی
که گفتگو به آن تعلق دارد به مشتری می‌رسد؛ پس یک فراخوانی واحد هم اینستاگرام را پوشش می‌دهد و هم
تلگرام را.

## ارسال پاسخ متنی

هر رویداد یک `conversation_id` دارد. برای پاسخ‌دادن همین کافی است.

**`cURL`**

```bash cURL
curl -X POST https://api-chat.parstechai.com/v1/conversations/cnv_4dR8nW/messages \
  -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Yes, we deliver on Fridays. Shall I reserve one for you?",
    "external_id": "op_reply_5521"
  }'
```

**`Python`**

```python Python
message = requests.post(
    f"{BASE}/conversations/{conversation_id}/messages",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "text": "Yes, we deliver on Fridays. Shall I reserve one for you?",
        "external_id": "op_reply_5521",
    },
    timeout=10,
).json()
```

**`Node.js`**

```javascript Node.js
const res = await fetch(
  `${BASE}/conversations/${conversationId}/messages`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      text: "Yes, we deliver on Fridays. Shall I reserve one for you?",
      external_id: "op_reply_5521",
    }),
  },
);
const message = await res.json();
```

**`Response`**

```json Response
{
  "conversation_id": "cnv_4dR8nW",
  "message_id": "msg_9wY7zA",
  "external_id": "op_reply_5521",
  "role": "operator",
  "type": "text",
  "text": "Yes, we deliver on Fridays. Shall I reserve one for you?",
  "attachments": [],
  "status": "sent",
  "created_at": "2026-09-20T11:33:05Z"
}
```

> **Info**
>
> `external_id` کلید idempotency شماست. اگر همان مقدار را دو بار بفرستید، به‌جای پیام تکراری همان
> پیام اصلی را پس می‌گیرید؛ پس تلاش دوباره پس از timeout بی‌خطر است.

`status` با مقدار `queued` یعنی کانال به محدودیت نرخ درخواست خورده است و پیام را وقتی ظرفیت آزاد شد
تحویل می‌دهیم. چیزی دور ریخته نمی‌شود.

## ارسال فایل یا پیام صوتی

دو مرحله دارد: فایل را بارگذاری کنید، سپس پیامی بفرستید که به آن ارجاع می‌دهد.

#### فایل را بارگذاری کنید

`POST /v1/attachments` را به‌صورت `multipart/form-data` فراخوانی کنید. یک `id` پس می‌گیرید.

#### پیام را بفرستید

همان id را در `attachment_ids` بفرستید. وقتی فایل می‌فرستید، متن اختیاری است. اگر چند فایل
بفرستید، متن فقط یک بار و به‌عنوان کپشن فایل اول ارسال می‌شود.

این کار به‌جای یک مرحله دو مرحله است تا اگر بارگذاری یک فایل بزرگ وسط انتقال شکست خورد، بتوان آن را
به‌تنهایی دوباره انجام داد، بدون اینکه پیام دوباره فرستاده شود.

> **Note**
>
> **`conversation_id` هنگام بارگذاری الزامی است.** هر پیوست به کانالی تعلق دارد که قرار است از آن
> ارسال شود؛ پس پیش از ذخیره فایل باید مقصد را بدانیم. هر بارگذاری فقط برای یک گفتگوست؛ برای
> فرستادن یک فایل به دو گفتگو، آن را دو بار بارگذاری کنید.

### ۱. بارگذاری

**`cURL`**

```bash cURL
curl -X POST https://api-chat.parstechai.com/v1/attachments \
  -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx" \
  -F "conversation_id=cnv_4dR8nW" \
  -F "file=@product-photo.jpg"
```

**`Python`**

```python Python
with open("product-photo.jpg", "rb") as fh:
    attachment = requests.post(
        f"{BASE}/attachments",
        headers={"Authorization": f"Bearer {API_KEY}"},
        data={"conversation_id": "cnv_4dR8nW"},
        files={"file": fh},
        timeout=60,
    ).json()
```

**`Response`**

```json Response
{
  "id": "att_5kR2nP",
  "identifier": "9f2c41b8e7d4",
  "name": "product-photo.jpg",
  "url": "https://cdn.parstechai.com/a/9f2c41b8e7d4.jpg",
  "type": "image"
}
```

### ۲. ارسال

```bash
curl -X POST https://api-chat.parstechai.com/v1/conversations/cnv_4dR8nW/messages \
  -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "attachment_ids": ["att_5kR2nP"], "external_id": "op_photo_77" }'
```

**`Response`**

```json Response
{
  "conversation_id": "cnv_4dR8nW",
  "message_id": "msg_1xZ8bC",
  "external_id": "op_photo_77",
  "role": "operator",
  "type": "image",
  "text": "",
  "attachments": [],
  "status": "sent",
  "created_at": "2026-09-20T11:35:41Z"
}
```

> **Note**
>
> **`attachments` در پاسخ ارسال خالی برمی‌گردد**، حتی وقتی پیام پیوست داشته باشد. به‌جای انتظار
> برای بازتاب آن در این‌جا، از `id`ای که از بارگذاری گرفته‌اید استفاده کنید. فایل روی پیام هست؛
> رویداد وب‌هوک `message` آن را با `attachments` پرشده تحویل می‌دهد.

اندازه فایل یکی از **ابعاد بسته** است و هنگام بارگذاری برای هر فایل جداگانه بررسی می‌شود:

| نوع                 |       پایه |        حرفه‌ای | نوع‌های پذیرفته‌شده                                                                            |
| ------------------- | ---------: | -------------: | ---------------------------------------------------------------------------------------------- |
| **تصویر**           |  ۲ مگابایت |  **۴ مگابایت** | `image/jpeg` · `image/png`                                                                     |
| **ویدیو**           | ۱۰ مگابایت | **۲۰ مگابایت** | `video/mp4` · `video/quicktime` (.mov) · `video/webm` · `video/ogg` · `video/x-msvideo` (.avi) |
| **صدا / پیام صوتی** |  ۵ مگابایت | **۱۰ مگابایت** | `audio/aac` · `audio/mp4` · `audio/x-m4a` · `audio/wav`                                        |
| **سند**             |  ۵ مگابایت | **۱۰ مگابایت** | `application/pdf`                                                                              |

نگارش‌های جایگزین رایج هم پذیرفته می‌شوند (`image/jpg`، `video/avi`، `audio/m4a`، `audio/wave`، `audio/x-wav`، `audio/vnd.wave`). این دقیقاً همان چیزی است که اینستاگرام تحویل می‌دهد: **GIF، WebP و فرمت‌های دیگر هنگام بارگذاری رد می‌شوند**، نه اینکه پذیرفته شوند و بعد هنگام ارسال شکست بخورند.

هر بسته‌ای که دسترسی API دارد هر چهار نوع را ارسال می‌کند؛ تفاوت سطح‌ها در اندازه است، نه در
اینکه چه نوع فایلی را می‌توانید بفرستید. اگر قرارداد شما سقف‌های دیگری تعیین کرده باشد، همان‌ها
اعمال می‌شوند.

نوع فایلی خارج از این فهرست، یا فایلی بزرگ‌تر از سقف بسته شما، پیش از ذخیره با
`422 validation_failed` رد می‌شود؛ برای سقف اندازه، `details` شامل `kind`، `cap_mb` و
`size_bytes` است. نوعی از فایل که بسته شما شامل آن نیست، خطای `403 attachment_not_in_plan` می‌دهد.
بارگذاری‌های ارسال‌نشده پس از ۲۴ ساعت دور ریخته می‌شوند.

محدودیت‌های کامل و ارتباط آن‌ها با محدودیت‌های خود اینستاگرام در
[محدودیت نرخ درخواست](/documentation/guides/rate-limits#files-voice-and-documents) آمده است.

> **Info**
>
> پیام صوتی فقط یک پیوست با `type: voice` است. فایل صوتی را به همان روش بارگذاری کنید؛ ما نوع را از
> نوع رسانه تشخیص می‌دهیم، یا می‌توانید `type=voice` را صریحاً بفرستید.

## نمایش نشانگر «در حال نوشتن»

وقتی کارشناس شما شروع به نوشتن می‌کند این را فراخوانی کنید تا مشتری همان نشانه‌ای را ببیند که در هر
برنامه گفتگوی دیگری می‌بیند.

```bash
curl -X POST https://api-chat.parstechai.com/v1/conversations/cnv_4dR8nW/typing \
  -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "is_typing": true }'
```

نشانگر پس از چند ثانیه خودبه‌خود منقضی می‌شود. تا وقتی کارشناس به نوشتن ادامه می‌دهد، به‌جای
فرستادن درخواست توقف، آن را دوباره بفرستید.

## ساختن صف اپراتور

گفتگوها را بر اساس وضعیت فیلتر کنید تا آن‌هایی را که منتظر یک انسان‌اند پیدا کنید.

```bash
curl "https://api-chat.parstechai.com/v1/conversations?status=operator_attention" \
  -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx"
```

**`Response`**

```json Response
{
  "data": [
    {
      "id": "cnv_4dR8nW",
      "robot_id": "rbt_8fK2mQ",
      "channel_id": "chn_3pQ7xL",
      "status": "operator_attention",
      "client": { "id": "iguser_88213", "username": "sara.k" },
      "last_message_at": "2026-09-20T11:20:44Z",
      "unread_count": 2,
      "created_at": "2026-09-20T11:08:59Z"
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "total_count": 1, "has_more": false }
}
```

`unread_count` تعداد پیام‌های مشتری از آخرین پاسخ یک اپراتور، هوش مصنوعی یا یک کارشناس است؛ یعنی
آنچه هنوز منتظر پاسخ است.

لازم نیست این را مدام بپرسید (poll کنید). رویداد وب‌هوک `chat` همان لحظه‌ای که گفتگو وارد
`operator_attention` می‌شود ارسال می‌شود؛ پس از رویداد برای به‌روزکردن صف و از این اندپوینت برای
بازسازی آن پس از راه‌اندازی دوباره استفاده کنید.

## بستن گفتگو

```bash
curl -X POST https://api-chat.parstechai.com/v1/conversations/cnv_4dR8nW/close \
  -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx"
```

> **Warning**
>
> بستن گفتگو مرزی برای زمینه (context) است، نه حذف. متن کامل گفتگو باقی می‌ماند و قابل خواندن است.
> تغییر این است که گفتگوی بعدی با آن مشتری بدون تاریخچه قبلی به‌عنوان زمینه هوش مصنوعی آغاز
> می‌شود. ببینید: [`context_reset`](/documentation/reference/webhook-event-reference).