راهنمای توسعهدهنده · مرجع API
مرجع API پیکسولچت
همه چیز این صفحه از سند OpenAPI عمومی ساخته میشود؛ همان سند را برای ساختن کلاینت یا وارد کردن در ابزارهایی مثل Postman بردارید: openapi.json (نسخه 0.51.0).
- اتصال ویجت به سایتدکمه پشتیبانی، API مرورگر و رویدادهای ویجت.
- راهنمای API تیکتهاساختن صفحه پشتیبانی سایت خودتان، قدم به قدم.
- راهنمای وبهوکهارویدادها، امضا و بررسی آن به چند زبان، تلاش دوباره.
احراز هویت
هر درخواست کلید API کسبوکار را در سرآیند Authorization: Bearer sk_… میفرستد. کلید را مسئول یا مدیر کسبوکار در پنل از «تنظیمات کسبوکار ← اتصال به سایت ← کلید API» میسازد و فقط همان لحظه یکبار نشان داده میشود. کلید خودِ کسبوکار است: در هیچ نشانی شناسه کسبوکار نمیآید. این API را فقط از سرور صدا بزنید، هرگز از مرورگر مشتری. کلید نادرست، چرخاندهشده یا کلید کسبوکار حذفشده پاسخ 401 UNAUTHORIZED میگیرد.
curl https://pixevel.chat/api/v1/me \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"پاسخها و خطاها
هر پاسخ JSON در یک پوشش میآید: موفق با ok: true و داده در data؛ ناموفق با ok: false و error.code (ثابت، برای کد شما) و error.message (انگلیسی، برای توسعهدهنده؛ ممکن است عوض شود). جریان رویدادها و دریافت پیوستها پوشش ندارند.
{ "ok": true, "data": { … } }
{ "ok": false, "error": { "code": "VALIDATION", "message": "orders[2].amount: …" } }| کد خطا | وضعیت HTTP |
|---|---|
VALIDATION | 400 |
UNAUTHORIZED | 401 |
DESK_UNAVAILABLE | 403 |
PLAN_LIMIT | 403 |
NOT_FOUND | 404 |
CONTACT_LIMIT | 409 |
CONVERSATION_CLOSED | 409 |
NOT_RESOLVED | 409 |
ATTACHMENT_TOO_LARGE | 413 |
PAYLOAD_TOO_LARGE | 413 |
IDEMPOTENCY_KEY_REUSED | 422 |
RATE_LIMITED | 429 |
ATTACHMENTS_UNAVAILABLE | 503 |
STREAM_UNAVAILABLE | 503 |
سقفها
هر کلید دو شمارنده جدا دارد: مسیرهای مشتریان و انتقال تاریخچه حداکثر 600 درخواست در دقیقه، و مسیرهای گفتوگو (تیکتها، پیامها، امتیاز، بارگذاری، دستهها، جریان رویدادها) حداکثر 1200 درخواست در دقیقه؛ پس یک همگامسازی بزرگ مشتریان صفحههای پشتیبانی شما را کند نمیکند. بیشتر از آن پاسخ 429 RATE_LIMITED با سرآیند Retry-After است. بدنه هر درخواست حداکثر 1 مگابایت است (413 PAYLOAD_TOO_LARGE).
تکرار امن
درخواستهایی که چیزی میسازند (گفتوگوی تازه، پیام، امتیاز) سرآیند اختیاری Idempotency-Key میگیرند: همان کلید و همان درخواست تا 24 ساعت همان پاسخ ثبتشده را با Idempotent-Replayed: true برمیگرداند؛ همان کلید با درخواستی دیگر 422 IDEMPOTENCY_KEY_REUSED است. مسیرهای PUT خودشان تکرارپذیرند: همان شناسه، همان نتیجه.
صفحهبندی
فهرستهای بلند با limit و cursor صفحه میشوند: هر صفحه nextCursor دارد و صفحه بعد را با فرستادن همان مقدار در cursor میگیرید؛ نبودن آن یعنی صفحه آخر.
کلید و کسبوکار
Which business an API key belongs to.
get/api/v1/me
Which business the API key is for, and how many customers it has. `lastUsedAt` moves at most once a minute.
سقف: 600 درخواست در دقیقه برای هر کلید (شمارنده مشتریان)
پاسخها
200IngestMeشکل
dataworkspaceobjectrequiredفیلدها
idstringrequirednamestringrequiredsiteUrlstringrequiredaccountIdstringrequiredThe business's billing account (#190): the company's id, else the owner's user id. With id it addresses the dashboard: /dashboard/{accountId}/business/{id}/….
contactsintegerrequired
401UNAUTHORIZED (wrong, rotated, or a deleted business's key)429RATE_LIMITED (more than 600 requests a minute on this API key's ingest budget)
نمونه با curl
curl -X GET "https://pixevel.chat/api/v1/me" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/me", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);مشتریان و سفارشها
Push your customers and their recent orders so the support desk sees who it is talking to.
get/api/v1/staff/{userId}/activity
Staff activity aggregates scoped by the business API key
سقف: 600 درخواست در دقیقه برای هر کلید (شمارنده مشتریان)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
userId* | مسیر | string (uuid) | |
from* | کوئری | string (date) | |
to* | کوئری | string (date) |
پاسخها
200Staff activityشکل
dataworkspaceIdstringrequireduserIdstringrequiredfromstring (date)requiredtostring (date)requiredrepliesintegerrequiredconversationsintegerrequiredresolvedintegerrequiredopenAssignedintegerrequiredansweredCallsintegerrequiredtalkSecondsintegerrequired
400VALIDATION (UUID or date window)401UNAUTHORIZED (business API key)429RATE_LIMITED (more than 600 requests a minute on this API key's ingest budget)
نمونه با curl
curl -X GET "https://pixevel.chat/api/v1/staff/userId/activity" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/staff/userId/activity", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);post/api/v1/contacts/batch
Up to 100 customers in one call, upserted like PUT. One result per row, in the order sent: a bad row (VALIDATION, CONTACT_LIMIT) fails only itself. A body that is not `{ contacts: [...] }`, holds more than 100 rows, or is over 1 MB fails as a whole.
سقف: 600 درخواست در دقیقه برای هر کلید (شمارنده مشتریان)
بدنه درخواست (JSON)
contactsContactInput & object[]requiredEach row is a ContactInput plus its `externalId`. Each is judged on its own.
پاسخها
200BatchResponseشکل
dataresultsobject[]requiredOne result per row sent, in the order sent. A bad row does not fail the others.
فیلدها
externalIdstringrequiredEmpty when the row carried no readable one.
okbooleanrequirederrorobjectفیلدها
codestringrequiredmessagestringrequired
400VALIDATION (the body as a whole)401UNAUTHORIZED413PAYLOAD_TOO_LARGE429RATE_LIMITED (more than 600 requests a minute on this API key's ingest budget)
نمونه با curl
curl -X POST "https://pixevel.chat/api/v1/contacts/batch" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"contacts":[{"externalId":"string"}]}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/batch", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"contacts": [
{
"externalId": "string"
}
]
}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);put/api/v1/contacts/{externalId}
Create or fully replace one customer (missing = empty). `orders` upserts those orders and never deletes others. Idempotent on externalId. Afterwards the business's unlinked conversations with this customer's phone (else email) are linked to them, at most 500.
سقف: 600 درخواست در دقیقه برای هر کلید (شمارنده مشتریان)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
بدنه درخواست (JSON)
namestring | nullphonestring | nullAn Iranian mobile in any usual spelling; stored as 09…. One that is sent but is not a mobile number is VALIDATION.
emailstring | nullLowercased on the way in.
urlstring | nullAbsolute https URL.
telegramIdstring | nullThe customer's Telegram user id as the business knows it (its own Telegram login). A Telegram chat with one of the business's bots is linked to the ONE contact with it, whichever came first (#158). Never shown back.
flagsarray | nullFull replace. Each flag is a key, a short label, and a tone. A reason is VALIDATION.
attributesobject | nullFull replace. At most 30 facts; each value is at most 200 characters.
ordersarray | nullUpserts these orders; never deletes the ones it leaves out.
پاسخها
200ContactResponseشکل
data400VALIDATION (the message names the field, like orders[2].amount)401UNAUTHORIZED409CONTACT_LIMIT (customers it already has can still be updated)413PAYLOAD_TOO_LARGE429RATE_LIMITED (more than 600 requests a minute on this API key's ingest budget)
نمونه با curl
curl -X PUT "https://pixevel.chat/api/v1/contacts/u_123" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);delete/api/v1/contacts/{externalId}
Remove one customer and their orders; their conversations are unlinked, never deleted — and one they started signed in leaves the widget for good (no visitor token reaches it again). The only way a customer leaves pixevel.chat.
سقف: 600 درخواست در دقیقه برای هر کلید (شمارنده مشتریان)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
پاسخها
200Deletedشکل
datadeletedbooleanrequiredfalse when there was nothing to delete (a delete is idempotent).
401UNAUTHORIZED429RATE_LIMITED (more than 600 requests a minute on this API key's ingest budget)
نمونه با curl
curl -X DELETE "https://pixevel.chat/api/v1/contacts/u_123" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);put/api/v1/contacts/{externalId}/orders/{orderExternalId}
Create or fully replace one order of a customer. The customer keeps their 20 newest orders by placedAt (older ones are dropped in the same transaction). Idempotent.
سقف: 600 درخواست در دقیقه برای هر کلید (شمارنده مشتریان)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
orderExternalId* | مسیر | string | The business's own id for the order (up to 120 characters), percent-encoded. |
بدنه درخواست (JSON)
numberstring | nullDefaults to the order's externalId (required when that is longer than 60).
titlestring | nullstatusstring | nullamountstringrequiredA decimal string (the service also reads a bare JSON number by its digits; the public door and SDK take a string).
currencystringrequiredurlstring | nullAbsolute https URL.
placedAtstring (date-time)requiredISO 8601 with a time zone.
پاسخها
200OrderResponseشکل
data400VALIDATION401UNAUTHORIZED404NOT_FOUND (the customer has not been pushed)413PAYLOAD_TOO_LARGE429RATE_LIMITED (more than 600 requests a minute on this API key's ingest budget)
نمونه با curl
curl -X PUT "https://pixevel.chat/api/v1/contacts/u_123/orders/o_456" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount":"string","currency":"string","placedAt":"2026-01-01T00:00:00Z"}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/orders/o_456", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"amount": "string",
"currency": "string",
"placedAt": "2026-01-01T00:00:00Z"
}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);delete/api/v1/contacts/{externalId}/orders/{orderExternalId}
Remove one order of a customer.
سقف: 600 درخواست در دقیقه برای هر کلید (شمارنده مشتریان)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
orderExternalId* | مسیر | string | The business's own id for the order (up to 120 characters), percent-encoded. |
پاسخها
200Deletedشکل
datadeletedbooleanrequiredfalse when there was nothing to delete (a delete is idempotent).
401UNAUTHORIZED429RATE_LIMITED (more than 600 requests a minute on this API key's ingest budget)
نمونه با curl
curl -X DELETE "https://pixevel.chat/api/v1/contacts/u_123/orders/o_456" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/orders/o_456", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);تیکتها و گفتوگوها
Build your own support pages: list, read, open, answer, resolve and rate a customer's conversations.
get/api/v1/orders/{orderExternalId}/conversations
Conversations about one order, newest activity first (API key).
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
orderExternalId* | مسیر | string | The business's own id for the order (up to 120 characters), percent-encoded. |
پاسخها
200OrderConversationListشکل
data400VALIDATION401UNAUTHORIZED429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)
نمونه با curl
curl -X GET "https://pixevel.chat/api/v1/orders/o_456/conversations" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/orders/o_456/conversations", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);get/api/v1/categories
The business's live categories, for the customer's form (tickets API).
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پاسخها
200TicketsCategoryListشکل
dataThe live ones, in the dashboard's order.
401UNAUTHORIZED429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)
نمونه با curl
curl -X GET "https://pixevel.chat/api/v1/categories" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/categories", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);get/api/v1/contacts/{externalId}/conversations
The contact's conversations the tickets API shows them — signed in (the widget's, or this API's) or staff-linked and held by no widget visitor's token — the most recently active first. An unknown contact has an empty page.
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
kind | کوئری | "ticket" | "live" | |
status | کوئری | string | Comma-separated: open, pending, resolved, closed. |
cursor | کوئری | string | A page's nextCursor. |
limit | کوئری | integer |
پاسخها
200TicketsConversationPageشکل
datanextCursorstring | nullrequiredOpaque; null on the last page.
400VALIDATION (names the query parameter)401UNAUTHORIZED429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)
نمونه با curl
curl -X GET "https://pixevel.chat/api/v1/contacts/u_123/conversations" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/conversations", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);post/api/v1/contacts/{externalId}/conversations
Open a NEW conversation for the contact (live or ticket), signed in, channel api: auto-assigned by category, staff alerted, conversation.created emitted. An unknown externalId gets a contact made from `contact`. Answers its thread, marked read.
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
Idempotency-Key | سرآیند | string | Optional, 1–255 visible ASCII characters (else VALIDATION `Idempotency-Key: …`; the public door refuses one sent blank, which reaches this service as none). Per business, for 24 hours: the same key with the same request (method, path and body bytes) is done once — a repeat answers 201 rebuilt from what the first one made (the thread as it is now; a reply's own message; a rating) with `Idempotent-Replayed: true`, and NOT_FOUND if staff unlinked the conversation since. The same key with another request is IDEMPOTENCY_KEY_REUSED (422). A refused or failed request keeps no key. |
بدنه درخواست (JSON)
kind"ticket" | "live"requiredsubjectstringRequired for a ticket.
bodystringcategoryIdstring | nullorderExternalIdstring | nullattachmentsstring[]Upload ids from POST /api/v1/uploads, not sent yet; with any, body may be empty.
contactobjectUsed only when the business has no contact with this externalId: the contact is made from it (counted against the contact ceiling). Judged like a push on every request — a malformed one is VALIDATION even for an existing contact, which is never changed by it.
فیلدها
namestring | nullphonestring | nullemailstring | null
پاسخها
201TicketsThreadشکل
dataPublic messages, oldest first.
400VALIDATION (names the field: contact.phone, attachments: unknown upload id, categoryId: the category is archived, …)401UNAUTHORIZED403DESK_UNAVAILABLE (the business is off or suspended)404NOT_FOUND (categoryId: no such category)409CONTACT_LIMIT413PAYLOAD_TOO_LARGE422IDEMPOTENCY_KEY_REUSED429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)
نمونه با curl
curl -X POST "https://pixevel.chat/api/v1/contacts/u_123/conversations" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"kind":"ticket"}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/conversations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"kind": "ticket"
}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);get/api/v1/contacts/{externalId}/conversations/{number}
One conversation with its public messages (after `after`, a message id, for a poll), marked read up to the newest returned.
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
number* | مسیر | string | The conversation's per-business number (#42 is `42`). Anything but a positive integer, or a conversation the API does not show this contact, is NOT_FOUND. |
after | کوئری | string | A message id; an unknown one returns every message. |
پاسخها
200TicketsThreadشکل
dataPublic messages, oldest first.
400VALIDATION (`after: …` — not text, or longer than an id)401UNAUTHORIZED404NOT_FOUND (another contact's or business's, hidden, or none)429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)
نمونه با curl
curl -X GET "https://pixevel.chat/api/v1/contacts/u_123/conversations/number" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/conversations/number", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);patch/api/v1/contacts/{externalId}/conversations/{number}
The customer resolves (`resolved`: open/pending → resolved; resolved or closed: no-op) or reopens (`open`: resolved → open, staff alerted; open/pending: no-op; closed: CONVERSATION_CLOSED). Allowed while suspended.
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
number* | مسیر | string | The conversation's per-business number (#42 is `42`). Anything but a positive integer, or a conversation the API does not show this contact, is NOT_FOUND. |
بدنه درخواست (JSON)
status"resolved" | "open"required
پاسخها
200TicketsConversationResultشکل
data400VALIDATION401UNAUTHORIZED404NOT_FOUND409CONVERSATION_CLOSED429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)
نمونه با curl
curl -X PATCH "https://pixevel.chat/api/v1/contacts/u_123/conversations/number" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"resolved"}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/conversations/number", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"status": "resolved"
}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);post/api/v1/contacts/{externalId}/conversations/{number}/messages
The customer's message: the conversation needs staff again, a resolved one reopens. `message` is the one this request wrote.
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
number* | مسیر | string | The conversation's per-business number (#42 is `42`). Anything but a positive integer, or a conversation the API does not show this contact, is NOT_FOUND. |
Idempotency-Key | سرآیند | string | Optional, 1–255 visible ASCII characters (else VALIDATION `Idempotency-Key: …`; the public door refuses one sent blank, which reaches this service as none). Per business, for 24 hours: the same key with the same request (method, path and body bytes) is done once — a repeat answers 201 rebuilt from what the first one made (the thread as it is now; a reply's own message; a rating) with `Idempotent-Replayed: true`, and NOT_FOUND if staff unlinked the conversation since. The same key with another request is IDEMPOTENCY_KEY_REUSED (422). A refused or failed request keeps no key. |
بدنه درخواست (JSON)
bodystringRequired unless attachments are sent.
attachmentsstring[]Upload ids from POST /api/v1/uploads, not sent yet.
پاسخها
201TicketsReplyResultشکل
data400VALIDATION401UNAUTHORIZED403DESK_UNAVAILABLE404NOT_FOUND409CONVERSATION_CLOSED (start a new one)413PAYLOAD_TOO_LARGE422IDEMPOTENCY_KEY_REUSED429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)
نمونه با curl
curl -X POST "https://pixevel.chat/api/v1/contacts/u_123/conversations/number/messages" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/conversations/number/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);get/api/v1/contacts/{externalId}/stream
Live event stream of the conversations the tickets API shows this contact (never a note); an unknown contact NOT_FOUND
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
Last-Event-ID | سرآیند | string | The id of the last stream event the client got (EventSource sends it on a reconnect). When it can be resumed — this process's, and still within stream.replay — every matching event after it is replayed and `ready` says resumed true; otherwise resumed false: refetch. |
پاسخها
200text/event-stream: `retry: 3000`, `event: ready` ({resumed}), then `id: <boot>-<seq>` / `event: conversation.updated | message.created | message.updated` / `data: <json>` frames (CustomerStreamConversation, CustomerStreamMessage). message.created includes `message` (TicketsMessage); message.updated does not. `message.delta` (StreamDelta) and `message.aborted` (StreamAborted) have no id, are never replayed, and never carry a note. A `: ping` comment every stream.heartbeat. It ends on shutdown, after stream.max_age, or when it falls 64 events behind — reconnect with Last-Event-ID.text/event-stream
401UNAUTHORIZED404NOT_FOUND429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)503STREAM_UNAVAILABLE — shutting down or too many open streams; keep polling
نمونه با curl
curl -X GET "https://pixevel.chat/api/v1/contacts/u_123/stream" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/stream", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
// Not JSON: read response.body as an event stream / file.get/api/v1/contacts/{externalId}/conversations/{number}/attachments/{aid}
A file of a public message in this conversation of this contact; anything else NOT_FOUND
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
number* | مسیر | string | The conversation's per-business number (#42 is `42`). Anything but a positive integer, or a conversation the API does not show this contact, is NOT_FOUND. |
aid* | مسیر | string | The attachment id. |
download | کوئری | string | 1 = always an attachment disposition. |
پاسخها
200The bytes, Content-Type as sniffed, X-Content-Type-Options nosniff, a sandbox Content-Security-Policy, Cache-Control private no-store, Content-Disposition attachment (filename and RFC 5987 filename*) unless the type may show inline (JPEG, PNG, GIF, WebP, BMP, audio, MP4, WebM, PDF) and download=1 was not asked.*/*
401UNAUTHORIZED404NOT_FOUND429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)503ATTACHMENTS_UNAVAILABLE
نمونه با curl
curl -X GET "https://pixevel.chat/api/v1/contacts/u_123/conversations/number/attachments/aid" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY"نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/conversations/number/attachments/aid", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
},
});
// Not JSON: read response.body as an event stream / file.post/api/v1/uploads
An upload for a customer's message through the tickets API; PUT the file to uploadUrl, then name uploadId in attachments
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
بدنه درخواست (JSON)
namestringKept (cleaned, at most 200 characters) as the file's display and download name.
contentTypestringsizeBytesintegerdurationMsintegerOptional client-measured recording length in milliseconds.
پاسخها
201Uploadشکل
datauploadIdstringrequireduploadUrlstringrequiredexpiresAtstring (date-time)requiredmaxBytesintegerrequiredvisitorTokenstringWidget only: present when this call minted the visitor's token.
400VALIDATION401UNAUTHORIZED403PLAN_LIMIT (limit storage: the plan's file storage is used)413ATTACHMENT_TOO_LARGE429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)503ATTACHMENTS_UNAVAILABLE (media is not configured or did not answer)
نمونه با curl
curl -X POST "https://pixevel.chat/api/v1/uploads" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/uploads", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);post/api/v1/contacts/{externalId}/conversations/{number}/rating
Rate the conversation's current resolution — once per resolution (reopened and resolved again, it may be rated again). Emits conversation.rated. Allowed while suspended.
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
پارامترها
| نام | جا | نوع | توضیح |
|---|---|---|---|
externalId* | مسیر | string | The business's own id for the customer: any text up to 120 characters, percent-encoded in the URL (spaces, slashes and Persian letters are fine). |
number* | مسیر | string | The conversation's per-business number (#42 is `42`). Anything but a positive integer, or a conversation the API does not show this contact, is NOT_FOUND. |
Idempotency-Key | سرآیند | string | Optional, 1–255 visible ASCII characters (else VALIDATION `Idempotency-Key: …`; the public door refuses one sent blank, which reaches this service as none). Per business, for 24 hours: the same key with the same request (method, path and body bytes) is done once — a repeat answers 201 rebuilt from what the first one made (the thread as it is now; a reply's own message; a rating) with `Idempotent-Replayed: true`, and NOT_FOUND if staff unlinked the conversation since. The same key with another request is IDEMPOTENCY_KEY_REUSED (422). A refused or failed request keeps no key. |
بدنه درخواست (JSON)
scoreintegerrequiredcommentstring | null
پاسخها
201TicketsRatingResultشکل
data400VALIDATION (score: must be an integer from 1 to 5)401UNAUTHORIZED404NOT_FOUND409NOT_RESOLVED (open or pending) or ALREADY_RATED422IDEMPOTENCY_KEY_REUSED429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)
نمونه با curl
curl -X POST "https://pixevel.chat/api/v1/contacts/u_123/conversations/number/rating" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"score":1}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/contacts/u_123/conversations/number/rating", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"score": 1
}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);انتقال تاریخچه
Bring conversations over from a previous support system.
post/api/v1/import/conversations
The import API (#189): up to 50 conversations of the business's old support history. Idempotent on sourceId — a re-sent conversation gains only the messages it lacks (by message sourceId) and its status is never changed again. History fires nothing: no webhook, staff alert, assistant turn, customer notice, channel delivery or live event. `dryRun` judges every row and writes nothing (not even a contact). One result per row, in the order sent: a bad row is `refused` alone.
سقف: 600 درخواست در دقیقه برای هر کلید (شمارنده مشتریان)
بدنه درخواست (JSON)
dryRunbooleanJudge and plan only; nothing is written.
پاسخها
200ImportResponseشکل
datadryRunbooleanrequiredstaffPhonesobjectrequiredThe request's distinct staff phones: on the team (written as that person) or not («کارمند قبلی»). On a dry run too.
فیلدها
knownintegerrequiredunknownintegerrequired
resultsobject[]requiredOne per row, in the order sent.
فیلدها
sourceIdstringrequiredstatus"created" | "appended" | "unchanged" | "skipped" | "refused"requiredOn a dry run, what would happen. skipped: imported before, erased or merged away since.
conversationIdstring | nullrequirednumberinteger | nullrequirednewMessagesstring[]requiredThe source ids of the messages written (would be).
errorobject | nullrequiredفیلدها
codestringrequiredmessagestringrequired
400VALIDATION (the body as a whole: not an object, no conversations, more than 50)401UNAUTHORIZED413PAYLOAD_TOO_LARGE429RATE_LIMITED (more than 600 requests a minute on this API key's ingest budget)
نمونه با curl
curl -X POST "https://pixevel.chat/api/v1/import/conversations" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"conversations":[{"sourceId":"string","kind":"ticket","channel":"widget","status":"open","createdAt":"2026-01-01T00:00:00Z","messages":[{"sourceId":null,"author":null,"createdAt":null}]}]}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/import/conversations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"conversations": [
{
"sourceId": "string",
"kind": "ticket",
"channel": "widget",
"status": "open",
"createdAt": "2026-01-01T00:00:00Z",
"messages": [
{
"sourceId": null,
"author": null,
"createdAt": null
}
]
}
]
}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);post/api/v1/import/aliases/lookup
What each old name (an import's sourceId, the old system's number) names now — the conversation it was merged into, when it was. Only the aliases that name a conversation still there are answered.
سقف: 1200 درخواست در دقیقه برای هر کلید (شمارنده گفتوگوها)
بدنه درخواست (JSON)
aliasesstring[]required
پاسخها
200ImportAliasListشکل
dataitemsobject[]requiredفیلدها
aliasstringrequiredconversationIdstringrequirednumberintegerrequiredkind"ticket" | "live"requiredstatus"open" | "pending" | "resolved" | "closed"requiredchannelstringrequiredcontactExternalIdstring | nullrequiredmessageCountintegerrequiredEvery message, internal notes and deleted ones included.
400VALIDATION401UNAUTHORIZED429RATE_LIMITED (more than 1200 requests a minute on this API key's conversations budget)
نمونه با curl
curl -X POST "https://pixevel.chat/api/v1/import/aliases/lookup" \
-H "Authorization: Bearer $PIXEVEL_CHAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"aliases":["string"]}'نمونه با TypeScript
const response = await fetch("https://pixevel.chat/api/v1/import/aliases/lookup", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PIXEVEL_CHAT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"aliases": [
"string"
]
}),
});
const answer = await response.json();
if (!answer.ok) throw new Error(`${answer.error.code}: ${answer.error.message}`);وبهوکها
پیکسولچت برای هر رویدادی که در پنل انتخاب کردهاید یک درخواست POST به نشانی شما میفرستد. امضای X-Pixevel-Signature را پیش از هر کاری بررسی کنید؛ کد بررسی به چند زبان، فهرست رویدادها و قاعده تلاش دوباره در راهنمای وبهوکها است.
postdeskEvent
What pixevel.chat sends to a business's endpoint for each event it subscribed to (and for «send test»). At least once: dedupe on X-Pixevel-Delivery. Answer 2xx fast and do the work afterwards.
Signature: `X-Pixevel-Signature: v1=` + lowercase hex of HMAC-SHA256(secret, `<X-Pixevel-Timestamp>` + "." + the raw body), over the exact bytes received. Recompute it, compare in constant time, and refuse a timestamp more than 300 seconds from your clock. Each attempt is stamped and signed anew; the body and X-Pixevel-Delivery never change. Within one endpoint rows go out in order of when they are due; nothing is ordered across endpoints or per conversation — order by `occurredAt` / `conversation.updatedAt` when it matters. Only https on port 443, to a public address. Verification code in several languages: https://pixevel.chat/docs/webhooks
| سرآیند | نوع | توضیح |
|---|---|---|
X-Pixevel-Event | string | |
X-Pixevel-Delivery | string | The delivery's id (UUIDv7): the same on every retry and redeliver. |
X-Pixevel-Timestamp | string | Unix seconds of this attempt. |
X-Pixevel-Signature | string |
بدنه
یکی از این شکلها: WebhookConversationPayload · WebhookTestPayload
مدلها
شکل هر شیئی که در درخواستها، پاسخها و وبهوکها میآید؛ نام هر مدل در بخشهای بالا به همینجا پیوند دارد.
Attachment
One file of a message. contentType is sniffed from the stored bytes, never the uploader's claim; kind follows from it. Download through the surface's attachments route — never by id alone.
idstringrequirednamestringrequiredThe uploader's file name (display and download name).
contentTypestringrequiredkind"image" | "audio" | "video" | "file"requiredsizeBytesintegerrequiredwidthinteger | nullrequiredAn image's width when it could be read.
heightinteger | nullrequireddurationMsinteger | nullrequiredClient-measured recording length; null when absent.
BatchRequest
contactsContactInput & object[]requiredEach row is a ContactInput plus its `externalId`. Each is judged on its own.
BatchResponse
resultsobject[]requiredOne result per row sent, in the order sent. A bad row does not fail the others.
فیلدها
externalIdstringrequiredEmpty when the row carried no readable one.
okbooleanrequirederrorobjectفیلدها
codestringrequiredmessagestringrequired
CallStatus
"queued" | "ringing" | "connecting" | "answered" | "completed" | "no_answer" | "busy" | "failed" | "canceled" | "missed"Outbound: queued → ringing → answered → completed; a call never answered ends no_answer (no pickup), busy (rejected), failed (trunk or LiveKit), or canceled (the staff member hung up first). Inbound (0.45.0): ringing (the caller holds, the desk rings) → connecting (a staff member answered; the voice gateway puts the caller through) → answered → completed; one nobody talked to ends missed, one that could not be put through failed. Only ever moves forward.
Contact
One customer of a business, as the business's server pushed it. Every field is a string; "" when there is none.
idstringrequiredexternalIdstringrequiredThe business's own id for the customer; unique per business.
namestringrequiredphonestringrequired09… (11 digits), or empty.
emailstringrequiredLowercase, or empty.
urlstringrequiredThis customer in the business's own admin (absolute https), or empty.
flagsContactFlag[]Labels the business pushed. Omitted when none. A flag has no reason.
attributesobjectShort facts the business pushed. Omitted when none.
createdAtstring (date-time)requiredupdatedAtstring (date-time)requiredWhen the business last pushed the customer.
ContactFlag
keystringrequiredA short key such as aml. Not a sentence.
labelstringrequiredWhat staff read. Not a case narrative.
tone"info" | "warning" | "danger"required
ContactInput
One customer. A PUT is a full replace of the customer's own fields (missing or null = empty). Lengths are counted in code points after trimming; a `null` reads as not sent.
namestring | nullphonestring | nullAn Iranian mobile in any usual spelling; stored as 09…. One that is sent but is not a mobile number is VALIDATION.
emailstring | nullLowercased on the way in.
urlstring | nullAbsolute https URL.
telegramIdstring | nullThe customer's Telegram user id as the business knows it (its own Telegram login). A Telegram chat with one of the business's bots is linked to the ONE contact with it, whichever came first (#158). Never shown back.
flagsarray | nullFull replace. Each flag is a key, a short label, and a tone. A reason is VALIDATION.
attributesobject | nullFull replace. At most 30 facts; each value is at most 200 characters.
ordersarray | nullUpserts these orders; never deletes the ones it leaves out.
ContactResponse
ConversationAssistant
state"active" | "paused" | "handoff"requiredactive — it answers; paused — staff stopped it; handoff — it handed the conversation to staff.
repliesintegerrequiredReplies it wrote in this conversation.
handoffReason"" | "customer_asked" | "no_knowledge" | "sensitive" | "unsure" | "limit" | "budget" | "hours" | "error"requiredWhy it handed off ("" otherwise). budget also covers a plan that no longer allows the assistant (grace, suspended).
CustomerStreamConversation
A business server's conversation.updated; number is what the tickets API's routes take.
idstringrequirednumberintegerrequiredstatus"open" | "pending" | "resolved" | "closed"required
CustomerStreamMessage
A business server's message.created or message.updated (0.32.0 — read the whole thread again; never a note). `message` is present on every message.created (TicketsMessage) and absent on message.updated.
conversationIdstringrequirednumberintegerrequiredmessageIdstringrequiredauthor"customer" | "agent" | "assistant" | "system"requiredmessageTicketsMessageThe tickets-API public message. Present on every message.created and absent on message.updated. Never a note.
Deleted
deletedbooleanrequiredfalse when there was nothing to delete (a delete is idempotent).
ErrorEnvelope
okfalserequirederrorobjectrequiredفیلدها
codestringrequiredMachine-readable, e.g. VALIDATION.
messagestringrequiredEnglish, for the developer; may change.
ImportAliasList
itemsobject[]requiredفیلدها
aliasstringrequiredconversationIdstringrequirednumberintegerrequiredkind"ticket" | "live"requiredstatus"open" | "pending" | "resolved" | "closed"requiredchannelstringrequiredcontactExternalIdstring | nullrequiredmessageCountintegerrequiredEvery message, internal notes and deleted ones included.
ImportConversation
sourceIdstringrequiredThe old system's id; the import's idempotency key and an alias.
aliasesstring[]Other old names («PX-123»).
kind"ticket" | "live"requiredchannel"widget" | "api" | "email" | "telegram" | "bale" | "phone" | "manual"requiredstatus"open" | "pending" | "resolved" | "closed"requiredsubjectstringcreatedAtstring (date-time)requiredresolvedAtstring (date-time)Defaults to the last activity for a resolved or closed one.
orderExternalIdstringassigneePhonestringA staff member's mobile; anyone not on the team is left unassigned.
contactobjectThe customer by the business's own id — made from the stub when missing, never changed. A ticket with one is shown to that customer through the tickets API; a live chat never is.
فیلدها
externalIdstringrequirednamestringphonestringemailstring
customerobjectA guest's own details (no contact).
فیلدها
namestringphonestringemailstring
channelIdentityobjectLive only: the chat the conversation continues on — the customer's next message there joins it while it is not closed.
فیلدها
channel"telegram" | "bale"requiredexternalChatIdstringrequiredexternalUserIdstringDefaults to externalChatId (a one-to-one chat).
usernamestringconnectionIdstringOne of the business's connections on that channel; none = its own channel (the API key).
messagesobject[]requiredفیلدها
sourceIdstringrequiredauthor"customer" | "agent" | "system"requiredstaffPhonestringAn agent's mobile; anyone not on the team is written with no author id («کارمند قبلی»).
bodystringRequired unless the message carries attachments.
internalbooleanA staff-only note (agent only).
createdAtstring (date-time)requirededitedAtstring (date-time)deletedAtstring (date-time)attachmentsstring[]Upload ids (POST /api/v1/uploads).
revisionsobject[]فیلدها
bodystringrequiredstaffPhonestringcreatedAtstring (date-time)required
ratingobjectفیلدها
scoreintegerrequiredcommentstringcreatedAtstring (date-time)required
ImportRequest
dryRunbooleanJudge and plan only; nothing is written.
ImportResponse
dryRunbooleanrequiredstaffPhonesobjectrequiredThe request's distinct staff phones: on the team (written as that person) or not («کارمند قبلی»). On a dry run too.
فیلدها
knownintegerrequiredunknownintegerrequired
resultsobject[]requiredOne per row, in the order sent.
فیلدها
sourceIdstringrequiredstatus"created" | "appended" | "unchanged" | "skipped" | "refused"requiredOn a dry run, what would happen. skipped: imported before, erased or merged away since.
conversationIdstring | nullrequirednumberinteger | nullrequirednewMessagesstring[]requiredThe source ids of the messages written (would be).
errorobject | nullrequiredفیلدها
codestringrequiredmessagestringrequired
IngestMe
workspaceobjectrequiredفیلدها
idstringrequirednamestringrequiredsiteUrlstringrequiredaccountIdstringrequiredThe business's billing account (#190): the company's id, else the owner's user id. With id it addresses the dashboard: /dashboard/{accountId}/business/{id}/….
contactsintegerrequired
MessageBlock
یکی از این شکلها: object · object · object · object · object
MessageButton
یکی از این شکلها: object · object
MessageReplyTo
idstringrequiredauthor"customer" | "agent" | "assistant" | "system"requiredexcerptstringrequireddeletedbooleanrequired
Order
One order of a contact as the business describes it — a summary, no line items. A contact keeps its 20 newest by placedAt.
idstringrequiredexternalIdstringrequirednumberstringrequiredWhat the business calls it on screen; defaults to externalId.
titlestringrequiredstatusstringrequiredThe business's own label, free text.
amountstringrequiredA decimal string in `currency`.
currencystringrequiredIRT, IRR, USD, USDT, …
urlstringrequiredThis order in the business's own admin (absolute https), or empty.
placedAtstring (date-time)requiredcreatedAtstring (date-time)requiredupdatedAtstring (date-time)required
OrderConversation
One conversation about an order, for the business's admin.
idstringrequirednumberintegerrequiredkind"live" | "ticket"requiredstatus"open" | "pending" | "resolved" | "closed"requiredsubjectstringrequiredorderExternalIdstringrequiredcontactIdstring | nullrequiredcontactExternalIdstring | nullrequiredThe business's own id for the customer (#190), for reading the thread through the customer's routes; null with no contact or one staff made at the desk.
assistantConversationAssistant | nullrequiredThe AI assistant on it (#190); null when it never answered.
lastActivityAtstring (date-time)requiredcreatedAtstring (date-time)required
OrderConversationList
OrderInput
numberstring | nullDefaults to the order's externalId (required when that is longer than 60).
titlestring | nullstatusstring | nullamountstringrequiredA decimal string (the service also reads a bare JSON number by its digits; the public door and SDK take a string).
currencystringrequiredurlstring | nullAbsolute https URL.
placedAtstring (date-time)requiredISO 8601 with a time zone.
OrderResponse
Role
"OWNER" | "ADMIN" | "MEMBER"The VIEWER's role — OWNER on their own workspace, OWNER / ADMIN as a company's representative, ADMIN / MEMBER as a member of the business's team.
Staff
userIdstring (uuid)required
StreamAborted
message.aborted, the same JSON on every audience that may see it. A signal: no id, never replayed, never stored. That reply will not be stored. The stored reply, when one is written, is an ordinary message.created. A note's abort never leaves the staff stream.
conversationIdstringrequiredmessageIdstringrequired
StreamDelta
message.delta, the same JSON on every audience that may see it. A signal: no id, never replayed, never stored, never sent to a channel or webhook. seq is 1, 2, 3… for this messageId and is contiguous. chunk is decoded reply text, never the model's JSON. A visitor hears it only on that conversation's own stream. A note's delta never leaves the staff stream.
conversationIdstringrequiredmessageIdstringrequiredseqintegerrequiredchunkstringrequired
TicketsCategory
idstringrequirednamestringrequireddescriptionstringrequired
TicketsCategoryList
The live ones, in the dashboard's order.
TicketsConversation
A conversation as the customer's support UI shows it. Never a staff id, an internal note, the assignee, the priority, or the customer's phone and email.
idstringrequirednumberintegerrequiredkind"live" | "ticket"requiredstatus"open" | "pending" | "resolved" | "closed"requiredchannel"widget" | "phone" | "email" | "manual" | "api"requiredopenedByStaffbooleanrequiredStaff opened it at the desk (channel phone, email or manual).
subjectstringrequiredcategoryobject | nullrequiredفیلدها
idstringrequirednamestringrequired
orderExternalIdstring | nullrequiredThe business's order it is about, as sent.
lastMessagestringrequiredlastAuthor"customer" | "agent" | "assistant" | "system"requiredmessageCountintegerrequiredPublic messages.
unreadCountintegerrequiredStaff / assistant messages after the customer's last read through this API.
awaitingCustomerbooleanrequiredstatus pending: staff answered.
canRatebooleanrequiredResolved or closed, and that resolution not rated yet.
ratingTicketsRating | nullrequiredassistantConversationAssistant | nullrequiredThe AI assistant on it (#190), for the business's own staff — its state, replies and why it handed off; null when it never answered.
lastActivityAtstring (date-time)requiredresolvedAtstring | null (date-time)requiredcreatedAtstring (date-time)required
TicketsConversationPage
nextCursorstring | nullrequiredOpaque; null on the last page.
TicketsConversationResult
TicketsCreateRequest
kind"ticket" | "live"requiredsubjectstringRequired for a ticket.
bodystringcategoryIdstring | nullorderExternalIdstring | nullattachmentsstring[]Upload ids from POST /api/v1/uploads, not sent yet; with any, body may be empty.
contactobjectUsed only when the business has no contact with this externalId: the contact is made from it (counted against the contact ceiling). Judged like a push on every request — a malformed one is VALIDATION even for an existing contact, which is never changed by it.
فیلدها
namestring | nullphonestring | nullemailstring | null
TicketsMessage
A public message; one staff deleted is never listed (0.32.0).
replyToMessageReplyTo | nullrequiredidstringrequiredauthor"customer" | "agent" | "assistant" | "system"requiredsenderWidgetTeamMember | nullPublic first name and opaque avatar URL only; omitted without consent or for non-agent messages.
bodystringrequiredRich staff/assistant content; [] for customer messages or none.
createdAtstring (date-time)requirededitedAtstring | null (date-time)requiredWhen staff last edited it (0.32.0); null when never.
TicketsRating
idstringrequiredscoreintegerrequiredcommentstringrequiredcreatedAtstring (date-time)required
TicketsRatingRequest
scoreintegerrequiredcommentstring | null
TicketsRatingResult
TicketsReplyRequest
bodystringRequired unless attachments are sent.
attachmentsstring[]Upload ids from POST /api/v1/uploads, not sent yet.
TicketsReplyResult
TicketsStatusRequest
status"resolved" | "open"required
TicketsThread
Public messages, oldest first.
Upload
PUT the file's bytes to uploadUrl (any Content-Type; at most maxBytes) before expiresAt (5 minutes), then name uploadId in a message's attachments within a day. Media answers 413 over the cap and 415 for a type it does not take.
uploadIdstringrequireduploadUrlstringrequiredexpiresAtstring (date-time)requiredmaxBytesintegerrequiredvisitorTokenstringWidget only: present when this call minted the visitor's token.
UploadRequest
The file about to be PUT. contentType and sizeBytes only pick the cap (a video's or a file's) and refuse an obviously too large file early; the stored bytes decide the type and size.
namestringKept (cleaned, at most 200 characters) as the file's display and download name.
contentTypestringsizeBytesintegerdurationMsintegerOptional client-measured recording length in milliseconds.
WebhookChange
field"status" | "kind" | "assignee"requiredkind: a live chat staff turned into a ticket (0.33.0) — from live, to ticket.
fromstring | nullrequiredtostring | nullrequired
WebhookConversation
The conversation after the change. Never the customer's name, phone, email, page or visitor token.
idstringrequirednumberintegerrequiredkind"live" | "ticket"requiredchannelstringrequiredstatus"open" | "pending" | "resolved" | "closed"requiredpriority"low" | "normal" | "high" | "urgent"requiredsubjectstringrequiredcategoryIdstring | nullrequiredassigneeUserIdstring | nullrequiredcontactobject | nullrequiredThe business's customer the conversation is linked to — our id, theirs, and whether the link is proven.
فیلدها
idstringrequiredexternalIdstringrequiredverifiedbooleanrequiredtrue only when the conversation was started by a visitor the business's page signed in as this customer (a signed widget identity), or by the business's own server through the tickets API, and is still linked to them; then every customer-authored event in it (created, customer_replied) was written by that customer. false for every other link — a phone or email someone typed into the widget, a channel, or staff (a staff-linked ticket is shown to its customer by the tickets API, but is not verified) — which proves nothing about who is writing: a receiver must never treat it as the customer's identity.
createdAtstring (date-time)requiredupdatedAtstring (date-time)requiredlastActivityAtstring (date-time)requiredmergedIntoobjectThe conversation staff merged this one into (0.33.0, #171) — its messages are there now. The last key, absent when never merged.
فیلدها
idstringrequirednumberintegerrequired
WebhookConversationPayload
Every conversation event. `message` is set on created (the first message, when public) and the two *_replied events; `change` on status_changed and assigned; each null otherwise. conversation.rated has both null and adds `rating` — the last key, absent from every other event.
event"conversation.created" | "conversation.customer_replied" | "conversation.staff_replied" | "conversation.status_changed" | "conversation.assigned" | "conversation.rated" | "conversation.message_edited" | "conversation.message_deleted" | "conversation.merged"requiredoccurredAtstring (date-time)requiredUTC, milliseconds.
workspaceIdstringrequiredmessageWebhookMessage | nullrequiredchangeWebhookChange | nullrequiredratingWebhookRatingconversation.rated only.
WebhookMessage
A public message (never an internal note).
idstringrequiredauthor"customer" | "agent" | "assistant"requiredauthorUserIdstring | nullrequiredbodystringrequiredRich staff/assistant content; [] for customer messages or none.
Its files; download one through the tickets API (GET …/conversations/{number}/attachments/{id}).
createdAtstring (date-time)requirededitedAtstring (date-time)message_edited only (absent otherwise): when staff edited it; body is the new text.
deletedAtstring (date-time)message_deleted only (absent otherwise): when staff deleted it; body is empty and blocks and attachments [] — the customer no longer sees it.
WebhookPayload
یکی از این شکلها: WebhookConversationPayload · WebhookTestPayload
WebhookRating
The rating a conversation.rated is about.
idstringrequiredscoreintegerrequiredcommentstringrequired"" for none.
source"api" | "widget"requiredapi: the tickets API; widget: the widget on the site (0.29.0)
resolvedAtstring (date-time)requiredThe resolution rated.
createdAtstring (date-time)required
WebhookTestPayload
event"webhook.test"requiredoccurredAtstring (date-time)requiredworkspaceIdstringrequiredwebhookIdstringrequired
WidgetTeamMember
firstNamestringrequiredavatarUrlstringrequired