راهنمای توسعهدهنده · API تیکتها
پشتیبانی، در صفحههای سایت خودتان
با API تیکتها، صفحه «پشتیبانی» حساب کاربری سایت شما همان صفحه خودتان میماند و پشت آن میز پشتیبانی پیکسولچت کار میکند: مشتری تیکت باز میکند، پاسخ پشتیبان را میخواند، دوباره مینویسد، گفتوگو را میبندد و امتیاز میدهد؛ پشتیبانها همه را در پنل پیکسولچت میبینند.
احراز هویت
این API را فقط سرور سایت شما صدا میزند، نه مرورگر مشتری. کلید API همان کلیدی است که برای
فرستادن مشتریان ساختهاید («تنظیمات کسبوکار ← اتصال به سایت ← کلید API») و با هر درخواست در
سرآیند Authorization میآید:
GET /api/v1/contacts/u_123/conversations HTTP/1.1
Host: pixevel.chat
Authorization: Bearer sk_0123456789abcdef0123456789abcdef01234567
کلید، خودِ کسبوکار است: در هیچ نشانی شناسه کسبوکار نمیآید و هیچ درخواستی به کسبوکار دیگری نمیرسد. کلید را در کد صفحهها نگذارید؛ مرورگر مشتری فقط با سرور شما حرف میزند و سرور شما با ما.
مشتری کیست و چه چیزی را میبیند
مشتری را با شناسه خودتان برای او نام میبرید، همان externalId که با API مشتریان میفرستید
(در نشانی، کدگذاریشده). سرور شما پیش از هر درخواست باید مطمئن باشد کاربر واردشده همین مشتری است.
هر مشتری فقط این گفتوگوها را میبیند:
- گفتوگوهایی که خودش با ورود امضاشده در ابزارک شروع کرده است؛
- گفتوگوهایی که با همین API برایش باز شدهاند (
channel: "api")؛ - گفتوگوهایی که پشتیبان در پنل به او وصل کرده است: تیکتی که برای او ثبت شده، یا گفتوگویی که
پشتیبان «وصل کردن به مشتری» را برایش زده است (
openedByStaffبرای تیکتهای ثبت پشتیبانtrueاست).
و هرگز اینها را نمیبیند:
- گفتوگویی که فقط با شماره یا ایمیلی که بازدیدکنندهای در ابزارک نوشته به او وصل شده است؛ هر کسی ممکن است شماره مشتری شما را بنویسد؛
- گفتوگویی که بازدیدکنندهای بدون ورود در ابزارک شروع کرده، حتی اگر پشتیبان آن را به او وصل کند: آن گفتوگو هنوز مال مرورگری است که شروعش کرد، و اگر اینجا هم نشان داده میشد، آن مرورگر نوشتههای حساب مشتری را میخواند و نوشتههایش در حساب مشتری به نام خود او دیده میشد. اگر باید در حساب مشتری دیده شود، پشتیبان برایش تیکت تازهای باز کند؛
- گفتوگوهای تلگرام، بله و ایمیل، حتی اگر پشتیبان به او وصلشان کرده باشد؛ پاسخ آنها همانجا فرستاده میشود.
گفتوگوی مشتری دیگر یا کسبوکار دیگر همیشه 404 NOT_FOUND است، هرگز 403. اگر پشتیبان پیوند را
بردارد یا به مشتری دیگری بدهد، یا مشتری را حذف کنید، گفتوگو همان لحظه از دید او بیرون میرود؛
مشتریای که دوباره با همان شناسه بفرستید، گفتوگوهای قبلی را پس نمیگیرد. پیوندهایی که پشتیبان
پیش از این API ساخته به مشتری نشان داده نمیشوند؛ برای نشان دادنشان یک بار دیگر وصلشان کنید.
مسیرها
| روش | مسیر | کار |
|---|---|---|
GET | /api/v1/categories | دستههای فعال، به ترتیب پنل |
GET | /api/v1/contacts/{externalId}/conversations | فهرست گفتوگوهای مشتری |
POST | /api/v1/contacts/{externalId}/conversations | باز کردن گفتوگوی تازه (201) |
GET | /api/v1/contacts/{externalId}/conversations/{number} | یک گفتوگو با پیامهایش؛ خوانده میشود |
PATCH | /api/v1/contacts/{externalId}/conversations/{number} | بستن (resolved) یا بازکردن (open) |
POST | /api/v1/contacts/{externalId}/conversations/{number}/messages | پیام مشتری (201) |
POST | /api/v1/contacts/{externalId}/conversations/{number}/rating | امتیاز (201) |
POST | /api/v1/uploads | گرفتن جای بارگذاری یک فایل (201) |
GET | /api/v1/contacts/{externalId}/conversations/{number}/attachments/{id} | خود فایل یک پیام |
پاسخها مثل باقی API در پوشش { "ok": true, "data": … } میآیند و خطاها
{ "ok": false, "error": { "code", "message" } }. پیام خطاها انگلیسی است و نام فیلد را میگوید،
مثل score: must be an integer from 1 to 5. number همان شمارهای است که مشتری میبیند (#42 یعنی
42).
گفتوگو و پیام
{
"id": "0b7e6c2a-1f3d-4e5a-9b8c-000000000042",
"number": 42,
"kind": "ticket",
"status": "pending",
"channel": "api",
"openedByStaff": false,
"subject": "سفارش ۱۰۴۲ نرسیده",
"category": { "id": "5d1c0a2e-0000-4000-8000-0000000000c7", "name": "پیگیری سفارش" },
"orderExternalId": "ord-1042",
"lastMessage": "پیگیری میکنیم.",
"lastAuthor": "agent",
"messageCount": 2,
"unreadCount": 1,
"awaitingCustomer": true,
"canRate": false,
"rating": null,
"lastActivityAt": "2026-10-02T10:05:00Z",
"resolvedAt": null,
"createdAt": "2026-10-02T10:00:00Z"
}
messageCountپیامهای عمومی است؛ یادداشتهای داخلی پشتیبان هیچجا نمیآیند.unreadCountپاسخهای پشتیبان (یا دستیار) پس از آخرین باری است که مشتری گفتوگو را با این API خوانده است.awaitingCustomerیعنی پشتیبان پاسخ داده و منتظر مشتری است (pending).canRateیعنی گفتوگو حل یا بسته شده و این حلشدن هنوز امتیاز نگرفته است.
پیامها { id, author, body, attachments, createdAt, editedAt } هستند (هر فایل
{ id, name, contentType, kind, sizeBytes, width, height, durationMs })؛ author یکی از customer،
agent، assistant و system است. editedAt زمان آخرین ویرایش پشتیبان است (null اگر ویرایش
نشده)؛ پیامی که پشتیبان حذف کرده اصلاً در فهرست نمیآید. شناسه پشتیبان، فرد مسئول، اولویت و شماره و ایمیل مشتری هرگز در
پاسخ نیست.
باز کردن گفتوگو
POST /api/v1/contacts/u_123/conversations
Idempotency-Key: 7c2f1e0a-create
{
"kind": "ticket",
"subject": "سفارش ۱۰۴۲ نرسیده",
"body": "سفارش ۱۰۴۲ من هنوز نرسیده.",
"categoryId": "5d1c0a2e-0000-4000-8000-0000000000c7",
"orderExternalId": "ord-1042",
"contact": { "name": "مریم رضایی", "phone": "09121234567" }
}
kindیکی ازticketوliveاست؛ برای تیکتsubjectلازم است (حداکثر 200 نویسه).bodyحداکثر 5000 نویسه است و کوتاه نمیشود، رد میشود.- هر درخواست یک گفتوگوی تازه باز میکند؛ ادامه دادن گفتوگوی باز با پیام است.
- گفتوگو مثل گفتوگوی ابزارک به پشتیبانِ مسئول دسته سپرده میشود، پشتیبانها باخبر میشوند و
وبهوک
conversation.createdفرستاده میشود. orderExternalIdشناسه سفارش در سیستم شماست (حداکثر 120 نویسه) و همانطور که فرستادهاید نگه داشته و برگردانده میشود.- اگر مشتریای با این
externalIdنفرستاده باشید، از رویcontactساخته میشود (با همان قاعدههای API مشتریان، و جزو سقف مشتریان شما). مشتری موجود هرگز با تیکت تغییر نمیکند:contactباز هم بررسی میشود (نادرستش در هر حال رد میشود)، اما به کار نمیرود. - اگر کسبوکار غیرفعال باشد یا اشتراکش معلق شده باشد،
403 DESK_UNAVAILABLEبرمیگردد و هیچ چیزی، حتی مشتری، ساخته نمیشود.
پاسخ (201) خودِ گفتوگو و پیامهایش است: { conversation, messages }.
خواندن و تازهها
GET …/conversations/{number} گفتوگو را با همه پیامهای عمومیاش، از قدیمی به تازه، برمیگرداند و
آنها را خوانده میکند؛ unreadCount صفر میشود. پاسخ ساختن و پاسخ دادن هم همین کار را میکند.
خواندن، زمان تغییر گفتوگو را جابهجا نمیکند و وبهوکی نمیفرستد.
جریان زنده نیست: برای دیدن پیامهای تازه، با شناسه آخرین پیامی که دارید بپرسید:
GET /api/v1/contacts/u_123/conversations/42?after=7d1e2f30-0000-4000-8000-0000000000d1
پاسخ مشتری
POST …/messages با { "body": "…" } پیام مشتری را ثبت میکند. گفتوگو دوباره منتظر پشتیبان
میشود و اگر حل شده بود، باز میشود و پشتیبانش باخبر میشود. پاسخ
{ conversation, message, messages } است و message همان پیامی است که این درخواست نوشت.
گفتوگوی بسته پیام نمیگیرد: 409 CONVERSATION_CLOSED (یک گفتوگوی تازه باز کنید).
فایلها
هر پیام تا 6 فایل دارد: تصویر، صدا، ویدیو، PDF، فایل فشرده یا متن. سه قدم:
POST /api/v1/uploadsبا{ "name": "screen.png", "contentType": "image/png", "sizeBytes": 48213 }جای بارگذاری میدهد:{ uploadId, uploadUrl, expiresAt, maxBytes }.- خود فایل را با
PUTبهuploadUrlبفرستید — بدون کلید؛ نشانی، خودش مجوز است و 5 دقیقه اعتبار دارد. سقف هر فایل 25 مگابایت و هر ویدیو 100 مگابایت است. uploadIdرا درattachmentsپیام بفرستید (POST …/conversationsیاPOST …/messages)؛ با فایل،bodyمیتواند خالی باشد. هر بارگذاری یک بار و تا یک روز فرستادنی است.
نوع فایل از خود بایتها خوانده میشود، نه از نام یا contentType. فایل یک پیام را با
GET …/conversations/{number}/attachments/{id} و همان کلید بگیرید و برای مشتری بفرستید؛ نشانی را
به مرورگر ندهید. فایل یادداشت داخلی، گفتوگوی مشتری دیگر یا کسبوکار دیگر 404 است. فضای فایلها
به پلن بسته است (PLAN_LIMIT با limit: storage).
بستن، بازکردن و امتیاز
PATCH …/conversations/{number} با { "status": "resolved" } گفتوگوی باز یا منتظر را حلشده
میکند و با { "status": "open" } گفتوگوی حلشده را دوباره باز میکند (پشتیبان باخبر میشود). روی
گفتوگویی که از قبل همان وضعیت را دارد کاری نمیکند؛ گفتوگوی بسته باز نمیشود
(409 CONVERSATION_CLOSED).
POST …/rating با { "score": 5, "comment": "عالی" } امتیاز را ثبت میکند: score عددی صحیح از
1 تا 5 و comment حداکثر 1000 نویسه.
- هر حلشدن یک امتیاز دارد. اگر گفتوگو دوباره باز شود (با پیام مشتری، پاسخ پشتیبان یا
PATCH) و دوباره حل شود، دوباره میشود امتیاز داد. - پیش از حل شدن:
409 NOT_RESOLVED. امتیاز دوم برای همان حلشدن:409 ALREADY_RATED. - هر امتیاز وبهوک
conversation.ratedرا میفرستد (وبهوکها).
بستن و امتیاز دادن وقتی اشتراک معلق است هم کار میکند؛ فقط گفتوگو و پیام تازه پذیرفته نمیشود.
فهرست و صفحهبندی
GET …/conversations گفتوگوها را از تازهترین فعالیت میآورد: { items, nextCursor }. برای
صفحه بعد، nextCursor را بهعنوان cursor بفرستید؛ در صفحه آخر null است. پارامترها:
| پارامتر | معنا |
|---|---|
kind | ticket یا live |
status | فهرستی با ویرگول از open، pending، resolved و closed |
cursor | nextCursor صفحه قبل |
limit | 1 تا 100؛ پیشفرض 20 |
برای مشتریای که هنوز نفرستادهاید، فهرست خالی است. گفتوگویی که در میانه ورق زدن فعالیت تازه بگیرد ممکن است به صفحه دیگری برود.
تکرار بیخطر با Idempotency-Key
به هر POST میتوانید سرآیند Idempotency-Key بدهید (1 تا 255
نویسه ASCII دیدنی). اگر پاسخ در راه گم شد، همان درخواست را با همان کلید دوباره بفرستید:
- کلید برای هر کسبوکار جداست و 24 ساعت معتبر است.
- همان کلید با همان درخواست (روش، مسیر و بایتهای بدنه) یک بار انجام میشود. تکرارش با
201و سرآیندIdempotent-Replayed: trueپاسخ میگیرد. - پاسخ تکرار از روی شناسههایی که بار اول ثبت شد دوباره ساخته میشود، نه بایتهای ذخیرهشده:
گفتوگو را همانطور که اکنون هست نشان میدهد، پاسخِ تکرار پیام همان پیام ثبتشده را در
messageدارد، و اگر پشتیبان در این میان پیوند گفتوگو را برداشته باشد،404میگیرد. - همان کلید با درخواست دیگر (بدنه، مسیر یا گفتوگوی دیگر)
422 IDEMPOTENCY_KEY_REUSEDاست و کاری انجام نمیشود. - درخواستی که رد شود یا شکست بخورد کلیدی ثبت نمیکند؛ همان کلید را دوباره به کار ببرید.
- سرآیند خالی (یا فقط فاصله)
400 VALIDATIONاست، نه «بدون کلید»؛ اگر کلید ندارید، سرآیند را نفرستید.
محدودیتها
- این مسیرها: 1200 درخواست در دقیقه برای هر کلید.
- مسیرهای مشتریان (
/api/v1/meو/api/v1/contacts…بدون گفتوگو): 600 درخواست در دقیقه برای هر کلید، جدا از اینها؛ همگامسازی مشتریان و صفحههای پشتیبانی جای هم را تنگ نمیکنند. - بدنه حداکثر 1 مگابایت.
- فایل: 6 در هر پیام؛ 25 مگابایت (ویدیو 100)؛ فایل بارگذاریشدهای که یک روز فرستاده نشود، پاک میشود.
خطاها
| کد | وضعیت | چه وقت |
|---|---|---|
UNAUTHORIZED | 401 | کلید نیست، اشتباه است یا عوض شده |
VALIDATION | 400 | فیلد یا پارامتر نادرست؛ پیام، نام فیلد را میگوید |
NOT_FOUND | 404 | گفتوگو نیست یا مال این مشتری نیست؛ یا categoryId: no such category |
DESK_UNAVAILABLE | 403 | کسبوکار غیرفعال یا معلق است؛ گفتوگو و پیام تازه پذیرفته نمیشود |
CONVERSATION_CLOSED | 409 | گفتوگوی بسته؛ یکی تازه باز کنید |
NOT_RESOLVED | 409 | امتیاز پیش از حل شدن |
ALREADY_RATED | 409 | این حلشدن امتیاز گرفته است |
CONTACT_LIMIT | 409 | ساختن مشتری تازه از سقف مشتریان میگذرد |
IDEMPOTENCY_KEY_REUSED | 422 | همان کلید با درخواست دیگر |
PAYLOAD_TOO_LARGE | 413 | بدنه بزرگتر از یک مگابایت |
RATE_LIMITED | 429 | بیش از سهم دقیقه؛ پس از Retry-After ثانیه |
ATTACHMENT_TOO_LARGE | 413 | فایل از سقف بزرگتر است |
ATTACHMENT_TYPE | 415 | نوع واقعی فایل پذیرفته نمیشود |
PLAN_LIMIT | 403 | فضای فایلهای پلن پر شده (limit: storage) |
ATTACHMENTS_UNAVAILABLE | 503 | فایل فعلاً پذیرفته نمیشود؛ پیام بدون فایل کار میکند |
از پشتیبانی فروشگاه به این API
اگر صفحههای پشتیبانی شما فیلدهای زیر را دارند، اینها جایشان را میگیرند:
| فیلد فعلی | از این API |
|---|---|
id / number | number |
subjectFa | subject |
status | status با حروف بزرگ |
orderId | orderExternalId |
openedByStaff | openedByStaff |
messageCount | messageCount |
lastMessageAt | lastActivityAt |
unreadForUser | unreadCount > 0 |
category | category.name |
channel | channel |
messages[].isStaff | author یکی از agent و assistant |
messages[].bodyFa | body |
| امتیاز رضایت | POST …/rating |
message در پاسخ ثبت پیام | message |
productId | ندارد؛ اگر لازم است، نام کالا را در موضوع یا متن پیام بیاورید |
pagination.total / page | ندارد؛ صفحهبندی با cursor و nextCursor است و شمار کل گفتوگوها برنمیگردد |
این API به هر externalId که سرور شما بفرستد اعتماد میکند. پیش از هر درخواست مطمئن شوید کاربر
واردشده در سایت همان مشتری است؛ شناسه را هرگز از مرورگر نپذیرید.