صفحهبندی، فیلتر، خطاها و محدودیت نرخ
فهرستها چطور صفحهبندی میشوند، درخواستها چطور رد میشوند، هر کد وضعیت چه معنایی دارد، و وقتی محدود شدید چه کنید.
آخرین بازنگری: 2026-08-23
صفحهبندی
هر نقطهٔ پایانی فهرستی page و page_size میگیرد و همان پاکت را برمیگرداند. total شمار همهٔ رکوردهای منطبق با فیلترهاست، نه اندازهٔ این صفحه - پس شمار صفحهها Math.ceil(total / page_size) است.
| پارامتر | پیشفرض | بازه |
|---|---|---|
page | ۱ | از ۱ به بالا |
page_size | ۲۵ | ۱ تا ۲۰۰ (مقادیر بزرگتر بریده میشوند، نه رد) |
curl -sS "https://your-host/api/v1/crm/contacts?page=2&page_size=100" \
-H "Authorization: Bearer $CLIENTO_TOKEN"{
"items": [ { "id": 4182, "display_name": "Sara Ahmadi", "…": "…" } ],
"page": 2,
"page_size": 100,
"total": 3417
}فیلتر و جستوجو
q جستوجوی متن آزاد روی فیلدهایی است که یک آدم جستوجو میکند - نام، ایمیل، شرکت. بقیه پارامترهای پرسوجوی صریحاند و هر مجموعه، مجموعهٔ خودش را در سند OpenAPI مستند میکند.
# Free-text
curl -sS "https://your-host/api/v1/crm/contacts?q=ahmadi" \
-H "Authorization: Bearer $CLIENTO_TOKEN"
# Typed filters (tasks)
curl -sS "https://your-host/api/v1/crm/tasks?status=open&priority=high&overdue=true" \
-H "Authorization: Bearer $CLIENTO_TOKEN"پارامتر پرسوجوی ناشناخته نادیده گرفته میشود نه رد - پس یک غلط تایپی بهجای خطا، نتیجهٔ فیلترنشده برمیگرداند. اگر فیلتری انگار هیچ کاری نمیکند، نام پارامترها را با سند OpenAPI بسنجید.
پاکت خطا
هر رد شدنی، از بدنهٔ نادرست تا خطای داخلی، همان سه فیلد را برمیگرداند. هیچچیز صفحهٔ خطای HTML برنمیگرداند، پس کلاینت هرگز مجبور نیست حدس بزند بدنه قابل تجزیه هست یا نه.
{
"error": "validation_error",
"message": "Invalid payload",
"details": { "email": ["Not a valid email address."] }
}- `error` - کد ماشینی پایدار. شرطها را بر همین بگذارید و بومیسازی را هم بر همین.
- `message` - متن تشخیصی انگلیسی برای توسعهدهنده. به کاربر نهایی نشانش ندهید و رویش تطبیق ندهید؛ ممکن است بازنویسی شود.
- `details` - ساختاری که با نوع خطا فرق میکند: خطای فیلدها برای اعتبارسنجی،
requiredبرای رد مجوز،reasonبرای قاعدهٔ کسبوکار،retry_afterبرای محدودیت نرخ. میتواند null باشد.
کدهای وضعیت
| کد | error | معنا و کاری که باید کرد |
|---|---|---|
| ۲۰۰ | - | خواندن یا بهروزرسانی موفق بود. |
| ۲۰۱ | - | ایجاد شد. بدنه همان رکورد تازه است. |
| ۲۰۴ | - | حذف شد. بدون بدنه. |
| ۴۰۰ | validation_error یا کد قاعده | بار درخواست نادرست است. details فیلدها را نام میبرد؛ نقض یک قاعدهٔ کسبوکار (مثلاً payment_amount_not_positive) کد خودش را دارد. بدون تغییر دوباره نفرستید. |
| ۴۰۱ | unauthorized | اعتبارنامه نبوده، منقضی یا باطل شده. یک بار تازهسازی کنید و بعد بایستید. |
| ۴۰۳ | forbidden | احراز هویت شده اما مجاز نیست. details.required کدها را نام میبرد. |
| ۴۰۴ | not_found | چنین رکوردی نیست - یا رکوردی است که اجازهٔ دیدنش را ندارید. این دو عمداً یک پاسخ دارند. |
| ۴۰۹ | conflict یا کد قاعده | قاعدهٔ یکتایی یا وضعیت. قاعدههای مالی، فروش، CRM و تیکت کد خودشان را دارند (مثلاً invoice_has_payments، payment_exceeds_due) و اعداد در details میآیند؛ در بقیه details.reason میگوید کدام. اگر کد را نمیشناسید، بر اساس وضعیت HTTP تصمیم بگیرید. |
| ۴۲۹ | rate_limited | محدود شدهاید. به اندازهٔ Retry-After صبر و بعد تلاش کنید. |
| 5xx | internal_error | خطا از سمت ماست. با عقبنشینی نمایی دوباره تلاش کنید؛ ممکن است درخواست اعمال شده باشد یا نه. |
محدودیت نرخ
نقاط پایانی حساس - ورود، کدهای یکبارمصرف پرتال مشتری، ارسال فرمهای عمومی، صفحهٔ عمومی فاکتور و بازگشتهای پرداخت - بهازای هر فراخوانیکننده محدود میشوند. CRUD معمولیِ احرازهویتشده بهطور پیشفرض محدود نیست، اما اپراتور فضای کاری میتواند محدودیت تنظیم کند؛ پس کلاینت خوشرفتار در هر حال ۴۲۹ را مدیریت میکند.
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json
{
"error": "rate_limited",
"message": "Too many requests",
"details": { "retry_after": 42 }
}-
Retry-After(به ثانیه) را رعایت کنید. این پاسخ خودِ سرور است - انتظار کوتاهتر فقط دوباره رد میشود. - برای ۵xx با عقبنشینی نمایی و jitter عقب بکشید. فاصلهٔ تلاش ثابت، یک قطعی کوتاه را در لحظهٔ بازیابی به هجوم تبدیل میکند.
-
GETوDELETEرا آزادانه دوباره بفرستید - خودتواناند. برایPOSTبخش بعدی را ببینید.
خودتوانی (Idempotency)
هدر سراسری Idempotency-Key وجود ندارد. هرجا تکرار زیانبار باشد، خود نقطهٔ پایانی هویت خودتوانیاش را در بدنه میآورد و آن را اجباری میکند نه اختیاری - هویت در اختیار کلاینت است، چون فقط کلاینت میداند که این تلاش، تلاش دوباره است.
| نقطهٔ پایانی | هویت | رفتار در تکرار |
|---|---|---|
POST /chat/conversations/{id}/messages | client_message_id | همان پیام موجود را با ۲۰۰ برمیگرداند نه ۲۰۱ |
POST /chat/conversations/support | خود فراخوانیکننده | به همان گفتوگو میرسد |
| تحویل وبهوک (ورودی به شما) | delivery_id | راهنمای وبهوک را ببینید - تحویل «دستکم یکبار» است |
برای نقاط پایانی بدون فیلد خودتوانی، POSTای که تایماوت شود سرانجام نامعلومی دارد. کورکورانه دوباره نفرستید: با یک GET روی چیزی که خودتان کنترل میکنید - مثلاً یک شناسهٔ بیرونی که روی رکورد ذخیره کردهاید - بازخوانی کنید و فقط اگر واقعاً نبود بسازید.