مستندات API

با این API می‌توانید چت‌بات رایاچت را از سایت، CRM یا پنل خودتان صدا بزنید. درخواست را سرور شما می‌فرستد و پاسخ JSON برمی‌گردد.

Base URL
https://rayachat.net/api/v1

نسخه 2026-08-29 · ساخت کلید در داشبورد · فایل OpenAPI 3.1

شروع سریع

در صفحه چت‌بات، «انتشار ← Developer API» را باز کنید و یک کلید بسازید. مقدار کامل کلید فقط همان یک بار نمایش داده می‌شود، پس آن را در متغیر محیطی سرور بگذارید. SDK لازم نیست؛ همین درخواست کافی است:

اولین درخواست
curl https://rayachat.net/api/v1/messages \
  -H "Authorization: Bearer rk_live_REPLACE_ME" \
  -H "Idempotency-Key: order-1001-message-1" \
  -H "Content-Type: application/json" \
  --data '{
    "thread_id": "order-1001",
    "customer": {
      "id": "customer-42",
      "name": "نام مشتری"
    },
    "message": "سفارش من در چه وضعیتی است؟"
  }'

در پاسخ 200 متن نهایی در response.content است. 202 یعنی گفتگو به اپراتور رفته و پاسخ دستی بعداً با وب‌هوک می‌رسد.

احراز هویت

هر درخواست با یک کلید rk_live_... احراز می‌شود. خود کلید مشخص می‌کند درخواست به کدام چت‌بات و کانال می‌رود، پس شناسه چت‌بات را در بدنه نفرستید.

هدر Authorization
Authorization: Bearer rk_live_REPLACE_ME

کلید را در JavaScript مرورگر، اپ موبایل، Git یا لاگ نگذارید. اگر کلیدی لو رفت، از صفحه چت‌بات‌ها یکی دیگر بسازید؛ می‌شود هم‌زمان دو کلید فعال داشت تا جابه‌جایی بدون قطعی انجام شود.

Scopeهای فعلی

Scopeدسترسی
messages:writeارسال پیام با POST /messages
conversations:readخواندن گفتگو و تاریخچه پیام‌ها

ارسال پیام

POST/messages

ارسال پیام مشتری به چت‌بات

پیام را ثبت می‌کند، تاریخچه همان thread و دانش مرتبط را می‌خواند و پاسخ را در همان درخواست برمی‌گرداند.

Headerها

نامنوعاجباریتوضیح
AuthorizationBearer tokenبلهکلید API با پیشوند rk_live_.
Idempotency-Keystringبله۸ تا ۲۰۰ نویسه ASCII قابل‌نمایش؛ برای هر پیام منطقی یکتا.
Content-Typeapplication/jsonبلهبدنه درخواست باید JSON باشد.

بدنه درخواست

نامنوعاجباریتوضیح
thread_idstringبلهشناسه پایدار گفتگو در سیستم شما؛ ۱ تا ۲۰۰ نویسه.
customer.idstringبلهشناسه پایدار مشتری؛ ۱ تا ۲۰۰ نویسه.
customer.namestringخیرنامی که اپراتور در صندوق ورودی می‌بیند؛ حداکثر ۱۲۰ نویسه.
customer.usernamestringخیرنام کاربری یا شناسه کمکی مشتری؛ حداکثر ۱۲۰ نویسه.
messagestringبلهمتن پیام مشتری؛ ۱ تا ۵۰۰۰ نویسه.

فیلد اضافه در بدنه رد می‌شود. مدل، دستورالعمل و پایگاه دانش از تنظیمات خود چت‌بات خوانده می‌شوند و از اینجا قابل تغییر نیستند.

نمونه سمت سرور

Node.js / TypeScript
Node.js / TypeScript
import { randomUUID } from "node:crypto";

const response = await fetch(
  "https://rayachat.net/api/v1/messages",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.RAYACHAT_API_KEY}`,
      "Idempotency-Key": `message-${randomUUID()}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      thread_id: "order-1001",
      customer: { id: "customer-42", name: "نام مشتری" },
      message: "سفارش من در چه وضعیتی است؟",
    }),
  },
);

const result = await response.json();
if (!response.ok) {
  throw new Error(`${result.code}: ${result.detail}`);
}

if (result.status === "handoff") {
  // پاسخ دستی اپراتور از webhook می‌رسد.
} else {
  console.log(result.response.content);
}
PHP
PHP
<?php
$body = json_encode([
  'thread_id' => 'order-1001',
  'customer' => ['id' => 'customer-42', 'name' => 'نام مشتری'],
  'message' => 'سفارش من در چه وضعیتی است؟',
], JSON_UNESCAPED_UNICODE);

$request = curl_init('https://rayachat.net/api/v1/messages');
curl_setopt_array($request, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer ' . getenv('RAYACHAT_API_KEY'),
    'Idempotency-Key: order-1001-message-1',
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => $body,
]);

$result = curl_exec($request);
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
$data = json_decode($result, true);

if ($status >= 400) {
  throw new RuntimeException($data['code'] . ': ' . $data['detail']);
}

ساختار پاسخ

پاسخ موفق دو حالت دارد و تفاوتشان را از وضعیت HTTP بفهمید.

200

completed

پاسخ چت‌بات آماده است و متن آن در response.content قرار دارد.

HTTP 200
{
  "object": "chat.message",
  "status": "completed",
  "message_id": "a17f...",
  "conversation_id": "0a9e...",
  "thread_id": "order-1001",
  "response": {
    "role": "assistant",
    "content": "سفارش شما ارسال شده است.",
    "citations": []
  },
  "usage": {
    "input_tokens": 418,
    "output_tokens": 37
  },
  "latency_ms": 860,
  "request_id": "req_..."
}
202

handoff

پیام ذخیره شده و گفتگو به اپراتور ارجاع شده است؛ پاسخ با message.created می‌رسد.

HTTP 202
{
  "object": "chat.message",
  "status": "handoff",
  "message_id": null,
  "conversation_id": "0a9e...",
  "thread_id": "order-1001",
  "response": null,
  "request_id": "req_..."
}

فیلدهای اصلی پاسخ

فیلدکاربرد
statuscompleted یا handoff؛ تصمیم اصلی کلاینت.
response.contentمتن نهایی چت‌بات در پاسخ ۲۰۰.
conversation_idشناسه رایاچت برای endpointهای خواندن گفتگو.
response.citationsارجاع‌های پایگاه دانش؛ ممکن است خالی باشد.
usageتعداد توکن ورودی و خروجی پاسخ.
request_idبرای ثبت در لاگ و پیگیری با پشتیبانی.

شناسه‌ها و ارسال مجدد

چهار شناسه در جریان است و جای هم را نمی‌گیرند؛ قاطی‌شدنشان یعنی گفتگوهای درهم یا پیام تکراری.

شناسهعمرکار
thread_idکل گفتگوشناسه گفتگو در سیستم شما؛ برای تمام پیام‌های همان گفتگو ثابت است.
customer.idکل مشتریشناسه مشتری در سیستم شما؛ به thread_id متصل می‌شود.
conversation_idکل گفتگوشناسه داخلی رایاچت؛ در پاسخ دریافت می‌شود و برای درخواست‌های GET کاربرد دارد.
Idempotency-Keyفقط یک پیامکلید یکتای درخواست؛ هنگام ارسال مجدد بدون تغییر استفاده می‌شود.

ارسال مجدد درخواست

نتیجه هر Idempotency-Key تا ۲۴ ساعت نگه داشته می‌شود. اگر اتصال قطع شد، همان کلید و همان بدنه را دوباره بفرستید؛ پاسخ ذخیره‌شده برمی‌گردد و مدل دوباره اجرا نمی‌شود. قاعده‌اش ساده است: کلید تکراری برای پیام تکراری، کلید تازه برای پیام تازه.

پاسخ تکراری هدر Idempotent-Replayed: true دارد. X-Request-Id هم روی همه پاسخ‌ها هست.

اتفاقکار درست
Timeout یا قطع اتصالهمان کلید و بدنه را دوباره ارسال کنید.
409 request_in_progressبه اندازه Retry-After صبر کنید و همان درخواست را تکرار کنید.
409 idempotency_conflictکلید با بدنه متفاوت ارسال شده است؛ منطق کلاینت را اصلاح کنید.
502 generation_failedبرای اجرای دوباره مدل، درخواست را با یک کلید جدید ارسال کنید.

خواندن گفتگوها

شناسه conversation_id در پاسخ POST قرار دارد. با این شناسه می‌توانید وضعیت گفتگو و تاریخچه پیام‌ها را بخوانید. هر دو endpoint به scope conversations:read نیاز دارند.

GET/conversations/{conversation_id}

وضعیت یک گفتگو

اطلاعات مشتری، وضعیت اتوماسیون، نیاز به اپراتور و تعداد پیام‌ها را برمی‌گرداند.

درخواست گفتگو
curl \
  https://rayachat.net/api/v1/conversations/0a9e135b-2b51-41f2-b5ba-bcc74ea9b3d8 \
  -H "Authorization: Bearer rk_live_REPLACE_ME"
نمونه پاسخ گفتگو
نمونه پاسخ گفتگو
{
  "object": "conversation",
  "id": "0a9e135b-2b51-41f2-b5ba-bcc74ea9b3d8",
  "thread_id": "order-1001",
  "customer": {
    "id": "customer-42",
    "name": "نام مشتری",
    "username": null
  },
  "status": "open",
  "automation": "active",
  "needs_attention": false,
  "attention_reason": null,
  "message_count": 4,
  "created_at": "2026-08-29T10:12:00.000Z",
  "last_message_at": "2026-08-29T10:14:18.000Z",
  "request_id": "req_..."
}
GET/conversations/{conversation_id}/messages

تاریخچه پیام‌ها

پیام‌ها را از قدیمی به جدید و با صفحه‌بندی cursor برمی‌گرداند.

Query parameterها

نامنوعاجباریتوضیح
limitintegerخیرتعداد پیام‌ها؛ بین ۱ تا ۱۰۰ و پیش‌فرض ۵۰.
afterUUIDخیرمقدار next_cursor صفحه قبلی.
اولین صفحه پیام‌ها
curl \
  "https://rayachat.net/api/v1/conversations/0a9e135b-2b51-41f2-b5ba-bcc74ea9b3d8/messages?limit=50" \
  -H "Authorization: Bearer rk_live_REPLACE_ME"
نمونه پاسخ پیام‌ها
نمونه پاسخ پیام‌ها
{
  "object": "list",
  "conversation_id": "0a9e135b-2b51-41f2-b5ba-bcc74ea9b3d8",
  "data": [
    {
      "object": "message",
      "id": "6c2044d5-69e-4fb2-90f8-43bc53070f8d",
      "role": "assistant",
      "content": "سفارش شما ارسال شده است.",
      "citations": [],
      "model": "deepseek-ai/DeepSeek-V4-Flash-0731",
      "usage": { "input_tokens": 418, "output_tokens": 37 },
      "created_at": "2026-08-29T10:14:18.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "request_id": "req_..."
}

تا زمانی که has_more برابر true است، مقدار next_cursor را در فیلد after درخواست بعدی بفرستید. اگر گفتگو متعلق به این کلید و کانال نباشد، API پاسخ 404 not_found می‌دهد.

وب‌هوک‌ها

اگر اپراتور از صندوق ورودی رایاچت پاسخ می‌دهد، وب‌هوک را تنظیم کنید. در غیر این صورت به این بخش نیازی ندارید.

رویدادها

typeزمان ارسال
conversation.handoffگفتگو به اپراتور ارجاع شده یا اتوماسیون متوقف شده.
message.createdاپراتور یک پاسخ دستی ارسال کرده است؛ این رویداد پس از پاسخ ۲۰۲ دریافت می‌شود.
webhook.testیک رویداد آزمایشی از داشبورد ارسال شده است.

ممکن است پاسخ POST برابر 200 completed باشد و کمی بعد رویداد conversation.handoff هم برسد؛ یعنی چت‌بات جواب اول را داده و ادامه گفتگو به اپراتور رسیده است.

مقصد وب‌هوک

  • آدرس باید HTTPS عمومی روی پورت ۴۴۳ باشد.
  • آدرس خصوصی، loopback، redirect یا URL دارای رمز پذیرفته نمی‌شود.
  • پاسخ 2xx در کمتر از ۱۰ ثانیه یعنی رویداد تحویل شده.
  • هر رویداد ممکن است بیش از یک بار برسد؛ با event id تکراری‌ها را کنار بگذارید.
  • هر تحویل چهار هدر دارد: X-Rayachat-Delivery، X-Rayachat-Event، X-Rayachat-Timestamp و X-Rayachat-Signature.
نمونه رویداد message.created
نمونه رویداد message.created
{
  "id": "2e915c30-75da-46c8-b0c8-787ed2442b54",
  "object": "event",
  "api_version": "2026-08-29",
  "created_at": "2026-08-29T10:18:42.000Z",
  "type": "message.created",
  "data": {
    "conversation_id": "0a9e135b-2b51-41f2-b5ba-bcc74ea9b3d8",
    "thread_id": "order-1001",
    "message": {
      "id": "a68b5cff-21f8-4b42-8a72-50ee745ff3d4",
      "role": "assistant",
      "content": "پاسخ اپراتور",
      "created_at": "2026-08-29T10:18:41.000Z"
    }
  }
}

بررسی امضا

امضا را روی بایت‌های raw body و پیش از parse کردن JSON بررسی کنید. timestamp قدیمی‌تر از پنج دقیقه نیز باید رد شود.

فرمول امضای v1
signed_payload = X-Rayachat-Timestamp + "." + raw_request_body
expected = HMAC_SHA256(webhook_secret, signed_payload)

X-Rayachat-Signature: v1=<hex digest>
بررسی امضا با Node.js
بررسی امضا با Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyRayachatWebhook(
  rawBody: Buffer,
  timestamp: string,
  receivedSignature: string,
  secret: string,
) {
  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!Number.isFinite(age) || age > 300) return false;

  const expected =
    "v1=" +
    createHmac("sha256", secret)
      .update(timestamp + ".")
      .update(rawBody)
      .digest("hex");

  const left = Buffer.from(expected);
  const right = Buffer.from(receivedSignature);
  return left.length === right.length && timingSafeEqual(left, right);
}

ارسال ناموفق تقریباً بعد از ۱ دقیقه، ۵ دقیقه، ۳۰ دقیقه، ۲ ساعت و ۸ ساعت تکرار می‌شود. handler باید امضا را بررسی و رویداد را ذخیره کند، سپس پاسخ 2xx بدهد.

خطاها و محدودیت‌ها

همه خطاها با application/problem+json برمی‌گردند. در منطق برنامه از code استفاده کنید، نه متن detail. مقدار request_id را نیز برای پیگیری در لاگ نگه دارید.

نمونه خطا
{
  "type": "https://rayachat.net/problems/rate-limit-exceeded",
  "title": "Rate limit exceeded",
  "status": 429,
  "code": "rate_limit_exceeded",
  "detail": "The API rate limit was exceeded...",
  "request_id": "req_...",
  "retry_after": 12
}

کدهای خطا

HTTPcodeکار درست
400invalid_requestJSON، فیلدها یا cursor را اصلاح کنید.
400invalid_idempotency_keyهدر را با ۸ تا ۲۰۰ نویسه ASCII ارسال کنید.
401invalid_api_keyBearer، مقدار کلید و وضعیت لغو را بررسی کنید.
403insufficient_scopeاز کلیدی با scope لازم استفاده کنید.
403account_suspendedوضعیت حساب رایاچت را بررسی کنید.
403channel_disabledکانال API را از داشبورد فعال کنید.
403customer_blockedوضعیت مسدودی مشتری را در صندوق ورودی بررسی کنید.
403subscription_inactiveاشتراک را فعال یا تمدید کنید.
404not_foundconversation_id و کلید همان کانال را بررسی کنید.
409idempotency_conflictاز یک کلید برای دو بدنه متفاوت استفاده نکنید.
409request_in_progressپس از زمان Retry-After درخواست را با همان کلید تکرار کنید.
409conversation_mismatchاز یک thread_id برای مشتری دیگری استفاده نکنید.
429rate_limit_exceededپس از زمان Retry-After و با backoff دوباره تلاش کنید.
429quota_exceededسهمیه پلن یا موجودی کیف پول را بررسی کنید.
500internal_errorدرخواست را دوباره ارسال و request_id را برای پیگیری نگه دارید.
502generation_failedبرای اجرای دوباره مدل، یک Idempotency-Key جدید بسازید.

Rate limitها

سطحمحدودیت
هر کلید API۳۰ پیام در دقیقه
هر مشتری در یک کانال۶ پیام در دقیقه
هر حساب رایاچت۶۰ پیام در دقیقه
هر کانال API۵۰۰۰ پیام در روز
خواندن گفتگوها۱۲۰ درخواست در دقیقه برای هر کلید

این محدودیت‌ها جدا از سهمیه پلن هستند. اگر ارسال مجدد مجاز باشد، هدر Retry-After نیز در پاسخ قرار می‌گیرد.