{
  "title": "فرمت پاسخ API",
  "slug": "team/backend/standard/api-response-format",
  "url": "/docs/team/backend/standard/api-response-format",
  "frontmatter": {
    "layout": "doc",
    "title": "فرمت پاسخ API",
    "description": "ساختار استاندارد پاسخ‌های موفق و خطا — single resource، list، error",
    "version": "1.0.0",
    "status": "PRIVATE",
    "author": "xoxxel",
    "owner": "xoxxel",
    "created_at": "2026-06-07",
    "updated_at": "2026-06-07",
    "tags": "",
    "reviewers": ""
  },
  "sections": [
    {
      "level": 1,
      "heading": "فرمت پاسخ API",
      "content": "**API Response Format**\n\nنسخه 2.0 | الزامی برای همه سرویس‌ها\n\n> **مرجع اصلی:** [راهنمای طراحی API](../../platform/api/api-design-guidelines) — این سند جزئیات فنی بیشتری ارائه می‌دهد.\n\n---"
    },
    {
      "level": 2,
      "heading": "1. ساختار یکتا",
      "content": "همه پاسخ‌های API از یک ساختار ثابت پیروی می‌کنند:\n\n```json\n{\n  \"success\": true,\n  \"data\": {},\n  \"meta\": {}\n}\n```\n\n- `success` (boolean): `true` برای موفق، `false` برای خطا\n- `data`: محتوای اصلی پاسخ (object یا array)\n- `meta`: فراداده (اختیاری) — pagination, request_id, warnings\n\nبرای خطاها به جای `data` و `meta` از `error` استفاده می‌شود.\n\n---"
    },
    {
      "level": 2,
      "heading": "2. پاسخ موفق — منبع تکی (Single Resource)",
      "content": "```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": \"01901234-5678-7abc-def0-123456789abc\",\n    \"status\": \"pending\",\n    \"amount\": 150.00,\n    \"createdAt\": \"2026-07-01T12:00:00.000Z\"\n  },\n  \"meta\": {\n    \"request_id\": \"req_abc123\"\n  }\n}\n```\n\n**کد HTTP:** `200`\n\n---"
    },
    {
      "level": 2,
      "heading": "3. پاسخ موفق — لیست (Collection)",
      "content": "```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"id\": \"01901234-...\",\n      \"status\": \"pending\",\n      \"amount\": 150.00\n    },\n    {\n      \"id\": \"01901234-...\",\n      \"status\": \"paid\",\n      \"amount\": 250.00\n    }\n  ],\n  \"meta\": {\n    \"pagination\": {\n      \"next_cursor\": \"abc123...\",\n      \"prev_cursor\": null,\n      \"has_more\": true,\n      \"limit\": 20\n    },\n    \"request_id\": \"req_abc123\"\n  }\n}\n```\n\n| فیلد meta.pagination | نوع | توضیح |\n|---|---|---|\n| `next_cursor` | string\\|null | کاتسور برای صفحه بعد — `null` اگر صفحه بعدی نباشد |\n| `prev_cursor` | string\\|null | کاتسور برای صفحه قبل — `null` اگر در صفحه اول باشیم |\n| `has_more` | boolean | **الزامی** — آیا داده بیشتری وجود دارد |\n| `limit` | integer | تعداد درخواست‌شده |\n\n> **تغییر از نسخه ۱:** سیستم صفحه‌بندی از Offset-based (`page`, `total`) به Cursor-based تغییر کرده است. جزئیات در [راهنمای طراحی API](../../platform/api/api-design-guidelines#۴-قرارداد-صفحه‌بندی-pagination-contract).\n\n**کد HTTP:** `200`\n\n---"
    },
    {
      "level": 2,
      "heading": "4. پاسخ موفق — ایجاد (Created)",
      "content": "```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": \"01901234-5678-7abc-def0-123456789abc\",\n    \"status\": \"pending\"\n  },\n  \"meta\": {\n    \"request_id\": \"req_abc123\"\n  }\n}\n```\n\n**کد HTTP:** `201`  \n**Header:** `Location: /v1/orders/01901234-...`\n\n---"
    },
    {
      "level": 2,
      "heading": "5. پاسخ موفق — بدون بدنه (No Content)",
      "content": "```json\n// بدون بدنه — فقط status code\n```\n\n**کد HTTP:** `204`  \n**موارد استفاده:** DELETE, برخی PATCHها\n\n---"
    },
    {
      "level": 2,
      "heading": "6. پاسخ خطا (Error)",
      "content": "```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ORDER_NOT_FOUND\",\n    \"message\": \"The requested order does not exist\",\n    \"details\": [\n      {\n        \"field\": \"orderId\",\n        \"message\": \"Must be a valid UUID\"\n      }\n    ]\n  }\n}\n```\n\n| فیلد | نوع | قانون |\n|---|---|---|\n| `code` | string | `SCREAMING_SNAKE_CASE` — Platform codes از Proto (nons-api/contracts/errors.proto)، Domain codes از سرویس مربوطه |\n| `message` | string | انگلیسی، قابل خواندن توسط انسان، ایمن برای نمایش |\n| `details` | array | فقط برای خطاهای اعتبارسنجی — در غیر این صورت آرایه خالی `[]` |\n\n**قوانین:**\n- هرگز stack trace به کلاینت نشان ندهید\n- هرگز مسیرهای داخلی یا خطاهای دیتابیس را به کلاینت نشان ندهید\n- `details` فقط برای Validation Errors استفاده شود\n\n---"
    },
    {
      "level": 2,
      "heading": "7. خطای اعتبارسنجی (Validation Error)",
      "content": "```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"email\",\n        \"message\": \"Must be a valid email address\"\n      },\n      {\n        \"field\": \"age\",\n        \"message\": \"Must be at least 18\"\n      }\n    ]\n  }\n}\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "8. خطای کسب‌وکار (Business Error)",
      "content": "```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ORDER_INVALID_STATUS\",\n    \"message\": \"Cannot transition order from 'pending' to 'completed'\",\n    \"details\": []\n  }\n}\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "9. قوانین کلی",
      "content": "| قانون | ✅ درست | ❌ غلط |\n|---|---|---|\n| کلید سطح بالا | `success` + `data` یا `success` + `error` | `data` و `error` همزمان |\n| کد خطا | `ORDER_NOT_FOUND` | `404`, `not_found` |\n| زبان خطا | انگلیسی | فارسی |\n| stack trace | هرگز | `\"message\": \"Error: at OrderService.getById (...)\"` |\n| خطای دیتابیس | `\"message\": \"Internal server error\"` | `\"message\": \"Duplicate key violation on table orders\"` |\n| خطای ۲۰۰ | هرگز برای خطا | `{ \"data\": null, \"error\": {...} }` |\n| `success` | همیشه `true` یا `false` | absence یا `null` |\n\n---"
    },
    {
      "level": 2,
      "heading": "10. مثال‌های سریع",
      "content": "| سناریو | کد | body |\n|---|---|---|\n| دریافت کاربر | 200 | `{ \"success\": true, \"data\": { \"id\": \"...\", \"name\": \"...\" }, \"meta\": {} }` |\n| لیست کاربران | 200 | `{ \"success\": true, \"data\": [...], \"meta\": { \"pagination\": {...} } }` |\n| ایجاد کاربر | 201 | `{ \"success\": true, \"data\": { \"id\": \"...\" }, \"meta\": {} }` |\n| حذف کاربر | 204 | — |\n| کاربر یافت نشد | 404 | `{ \"success\": false, \"error\": { \"code\": \"USER_NOT_FOUND\", \"message\": \"...\", \"details\": [] } }` |\n| ورودی نامعتبر | 400 | `{ \"success\": false, \"error\": { \"code\": \"VALIDATION_ERROR\", \"message\": \"...\", \"details\": [...] } }` |\n\n---"
    },
    {
      "level": 2,
      "heading": "11. پاسخ خطاهای Currency Service",
      "content": "Currency Service از ساختار خطای استاندارد پیروی می‌کند، با کدهای خطای زیر:\n\n```json\n// 422 — جفت ارز پشتیبانی‌نشده\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNSUPPORTED_PAIR\",\n    \"message\": \"Currency pair IRR/USDT is not supported\",\n    \"details\": []\n  }\n}\n\n// 424 — سرویس وابسته در دسترس نیست (Failed Dependency)\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"RATE_UNAVAILABLE\",\n    \"message\": \"Exchange rate source is unavailable and no cached rate exists\",\n    \"details\": []\n  }\n}\n\n// 400 — مقدار نامعتبر\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_AMOUNT\",\n    \"message\": \"Amount must be a positive integer\",\n    \"details\": [{ \"field\": \"amount\", \"message\": \"Must be greater than zero\" }]\n  }\n}\n```\n\n| کد خطا | HTTP Status | سناریو |\n|--------|-------------|--------|\n| `UNSUPPORTED_PAIR` | 422 | جفت ارز درخواستی پشتیبانی نمی‌شود |\n| `RATE_UNAVAILABLE` | 424 | منبع نرخ ارز در دسترس نیست و cache موجود نیست |\n| `INVALID_AMOUNT` | 400 | مقدار ورودی منفی، صفر یا نامعتبر است |"
    }
  ]
}