تلفن ابری همکارانمرجع مستندات توسعه‌دهندگان
نوع مرکز تماس
ورود به پنل
PBX V2 · Web Service API

وب‌سرویس مرکز تماس

از اولین درخواست تا اتصال کامل CRM؛ تماس بسازید، نتیجه را دنبال کنید و گزارش، ضبط و وضعیت داخلی‌ها را در نرم‌افزار خود داشته باشید.

REST API · JSONاحراز هویت با کلید اتصالنمونه‌های cURL، Node.js و PHP

معرفی و انتخاب مسیر درست#

وب‌سرویس، رابط ارتباط نرم‌افزار شما با مرکز تماس است. درخواست را از سرور CRM، ERP یا برنامه خود ارسال می‌کنید و پاسخ JSON می‌گیرید. برای دریافت خودکار اتفاقات تماس، وب‌هوک را در کنار آن فعال کنید.

مدیریت مکالمه

کلیک برای تماس، قطع، پخش صدا، انتقال و کنفرانس.

گزارش و فایل صوتی

تاریخچه تماس، ضبط، صندوق صوتی و شاخص‌های مرکز.

اتوماسیون ارتباط

تماس صوتی آماده، OTP فارسی و عضویت داخلی در صف.

این راهنما برای مرکز تماس PBX V2 است. نوع مرکز تماس خود را در پنل بررسی کنید. اگر مرکز شما PBX V1 است، راهنمای PBX V1 را بخوانید. انتخاب این تب، نوع مرکز یا کلید شما را تغییر نمی‌دهد.

موضوعات راهنمای PBX V1 اینجا هم پوشش داده شده‌اند؛ تفاوت شناسه‌ها، پاسخ غیرهم‌زمان فرمان‌ها و مسیر سازگاری در بخش مهاجرت توضیح داده شده است.

آدرس پایه و احراز هویت#

Base URL
https://api.hamkaran.cloud/api/hamkaran/v2

در پنل همان مرکز تماس، اتصال «وب‌سرویس» را فعال و کلید آن را دریافت کنید. در تمام درخواست‌ها Header با نام key را ارسال کنید. داده‌های POST را به صورت JSON بفرستید.

HTTP headers
key: YOUR_WEBSERVICE_KEY
Accept: application/json
Content-Type: application/json
هدرچه زمانی لازم است؟کاربرد
keyتمام درخواست‌هاکلید اتصال وب‌سرویس همین مرکز تماس
Acceptپیشنهاد می‌شودapplication/json برای دریافت پاسخ و خطای JSON
Content-TypePOST با JSONapplication/json
Idempotency-Keyفرمان‌های تماسشناسه درخواست تکرارپذیر، ۸ تا ۱۶۰ کاراکتر؛ کنترل صف قرارداد جداگانه دارد.
کلید را فقط روی سرور خود نگه دارید. آن را در کد مرورگر، لینک دانلود عمومی، مخزن Git یا پیام خطا قرار ندهید. مثال‌های JavaScript این صفحه مخصوص Node.js سمت سرور هستند.

شروع سریع؛ اولین درخواست بدون ایجاد تماس#

  1. کلید اتصال را آماده کنید.مرکز باید PBX V2 و اتصال وب‌سرویس فعال باشد؛ IP سرور شما هم باید در محدوده مجاز اتصال باشد.
  2. وضعیت داخلی‌ها را بخوانید.درخواست زیر هیچ گوشی را زنگ نمی‌زند و برای بررسی اتصال مناسب است.
  3. پاسخ را بررسی کنید.HTTP 200 و آرایه داخلی‌ها یعنی درخواست مجاز بوده است. registered: false به وضعیت گوشی مربوط است، نه الزاماً خطای کلید.
cURL
export HAMKARAN_WEBSERVICE_KEY='YOUR_WEBSERVICE_KEY'

curl --request GET 'https://api.hamkaran.cloud/api/hamkaran/v2/extension' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json'
JSON · پاسخ نمونه
{
  "status": "success",
  "data": [
    {
      "extension": "200",
      "registered": true,
      "state": "NOT_INUSE",
      "observed_at": 1789545600
    }
  ]
}

شماره‌ها، زمان‌ها و UUIDهای مثال‌ها ساختگی‌اند. آن‌ها را با داده واقعی مرکز خود جایگزین کنید.

شناسه درخواست، تماس و کانال#

شناسهمعنااز کجا دریافت می‌شود؟
operation_idعملیات درخواست‌شده از API؛ برای پیگیری اجرای فرمانپاسخ ثبت فرمان؛ نام‌های action_id و actionId هم برگردانده می‌شوند.
call_idشناسه مشترک تماس در گزارش و وب‌هوکنتیجه عملیات ایجاد تماس، گزارش یا رویداد تماس
leg_idشناسه یک کانال مشخص در مکالمهرویدادهای زنده مانند call.connected؛ کانال مقابل در data.other_leg_id می‌آید.
recording_idشناسه ضبط عضو کنفرانسرویداد ضبط یا recordings[].id در گزارش

شناسه‌های PBX V2 از نوع UUID هستند. آن‌ها را رشته نگه دارید؛ شناسه قدیمی Asterisk را جایگزین نکنید. برای انتقال و کنفرانس، شناسه کانال مربوط به داخلی انتقال‌دهنده یا میزبان را از رویدادهای همان تماس نگه دارید.

جلوگیری از تماس تکراری#

برای هر عملیات منطقی جدید یک Idempotency-Key تازه بسازید. اگر پاسخ شبکه گم شد، همان Body را با همان کلید دوباره ارسال کنید. برای تلاش مجدد کلید تازه تولید نکنید؛ کلید تازه یعنی عملیات تازه.

نمونه هدر درخواست
Idempotency-Key: crm-ticket-4821-call-001
  • تکرار درخواست معتبر با همان کلید و همان پارامترها، همان عملیات را برمی‌گرداند؛ مجوز و معتبر بودن منابع همچنان بررسی می‌شود.
  • استفاده از همان کلید برای فرمان یا پارامتر متفاوت، پاسخ 409 می‌دهد.
  • دامنه کلید، اتصال وب‌سرویس شماست؛ نگهداری پیش‌فرض عملیات نهایی ۳۰ روز است. درخواست قدیمی را خارج از این بازه بی‌بررسی تکرار نکنید.
  • برای فرمان‌های صف، UUID داخل فیلد id نقش شناسه تکرار را دارد؛ قرارداد صف را ببینید.

کدهای HTTP و خطاها#

کدمعنااقدام پیشنهادی
200خواندن موفق داده یا دریافت فایلمحتوای پاسخ را متناسب با endpoint پردازش کنید.
202فرمان ثبت و پذیرفته شده استنتیجه را با شناسه عملیات پیگیری کنید؛ هنوز به معنی پاسخ مخاطب نیست.
403کلید، IP، نوع مرکز یا دسترسی نامعتبروضعیت اتصال و محدودیت داخلی/خط را بررسی کنید.
404تماس، عملیات یا فایل پیدا نشدشناسه و مالکیت را بررسی کنید؛ تماس فعال ممکن است پایان یافته باشد.
405روش HTTP اشتباهفرمان‌های تماس باید با POST ارسال شوند.
409تعارض کلید تکرارهمان کلید را با پارامتر متفاوت استفاده نکنید.
422ورودی ناقص یا نامعتبرنوع فیلدها، UUID، بازه تاریخ و شماره‌ها را اصلاح کنید.
429سقف درخواست یا صف پربا فاصله و تأخیر تصادفی تلاش مجدد کنید؛ از حلقه سریع خودداری کنید.
502 / 503گزارش در دسترس نیست یا سرویس این مرکز فعال نیستبا زمان درخواست و شناسه عملیات از پشتیبانی پیگیری کنید.
500خطای داخلیبرای عملیات تماس وضعیت را بررسی کنید؛ بدون کلید ثابت دوباره شماره‌گیری نکنید.
JSON · نمونه خطای اعتبارسنجی
{
  "message": "The given data was invalid.",
  "errors": {
    "timeout": ["The timeout must be between 5 and 60."]
  }
}

متن خطا ممکن است براساس زبان سرویس متفاوت باشد. بعضی خطاها فقط message دارند؛ برنامه را به وجود همیشگی errors وابسته نکنید.

GET

دریافت گزارش تماس‌ها#

/reports

فهرست تماس‌های همین مرکز را با صفحه‌بندی، فیلتر و مرتب‌سازی بگیرید. گزارش نهایی شامل مقصد نهایی، وضعیت اتصال، زمان انتظار و مکالمه، نام مخاطب و ضبط‌های موجود است.

پارامتر Queryنوع / پیش‌فرضتوضیح
date_from، date_toرشته تاریخفرمت YYYY-MM-DD میلادی یا شمسی؛ پیش‌فرض از هفت روز قبل تا امروز، براساس ایران. روز پایان هم در بازه است. فاصله دو تاریخ حداکثر ۹۳ روز.
datefrom، datetoنام جایگزینبرای سازگاری پذیرفته می‌شوند؛ فقط یکی از دو قالب نام‌گذاری را استفاده کنید.
pageعدد؛ ۱شماره صفحه از ۱ تا ۱۰۰۰۰۰
limitعدد؛ ۲۵تعداد رکورد هر صفحه، بین ۱ تا ۱۰۰
sourceرشته شمارهتطبیق دقیق شماره تماس‌گیرنده؛ فرمت ایرانی برای جست‌وجو یکسان‌سازی می‌شود.
internalnumberرشته؛ ۱ تا ۴ رقمگزارش مربوط به داخلی؛ باید در دسترسی کلید باشد.
directionin / out / intورودی / خروجی / داخلی
answered0 / 1وضعیت نهایی ناموفق / موفق؛ پاسخ IVR به‌تنهایی معیار موفقیت نیست.
sortdatedate / duration / talk / caller / destination
orderdescasc برای صعودی یا desc برای نزولی
cURL
curl --get 'https://api.hamkaran.cloud/api/hamkaran/v2/reports' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode 'date_from=2026-09-09' \
  --data-urlencode 'date_to=2026-09-16' \
  --data-urlencode 'internalnumber=200' \
  --data-urlencode 'direction=in' \
  --data-urlencode 'page=1' \
  --data-urlencode 'limit=25' \
  --data-urlencode 'sort=date' \
  --data-urlencode 'order=desc'
JSON · نمونه خلاصه‌شده پاسخ
{
  "data": [
    {
      "call_id": "11111111-1111-4111-8111-111111111111",
      "caller": "9120000001",
      "callee": "200",
      "call_date": "2026-09-16 10:30:00",
      "direction": "in",
      "answered": 1,
      "wait_sec": 12,
      "ring_sec": 6,
      "bill_sec": 45,
      "duration_sec": 57,
      "hangup_cause": "NORMAL_CLEARING",
      "identity": {"name": "مخاطب نمونه", "source": "contacts"},
      "destination_identity": null,
      "recordings": []
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 25
}
فیلد پاسختفسیر
caller / calleeشماره مبدأ / مقصد نهایی ثبت‌شده. شماره‌ها رشته‌اند؛ شماره ایرانی در گزارش بدون ۹۸ و صفر ابتدایی است.
wait_sec / ring_sec / bill_sec / duration_secبه‌ترتیب زمان انتظار، زنگ، مکالمه و طول کل تماس، برحسب ثانیه؛ این مقادیر لزوماً اجزای قابل جمع یکدیگر نیستند.
queue_id / queue_name / agentاطلاعات صف و پاسخ‌دهنده، در صورت ثبت
terminal_type / attempted_destination / endpoint_statusنوع مقصد پایانی، مقصد تلاش‌شده و نتیجه endpoint؛ برای تحلیل تماس بدون پاسخ مفیدند.
identity / destination_identityاطلاعات نام شناسایی‌شده مبدأ و مقصد؛ ممکن است null باشد.
recordingsآرایه فایل‌های آماده؛ خالی بودن یعنی اکنون فایل قابل دریافت در پاسخ نیست.
revisionنسخه حسابداری رکورد؛ اطلاعات تکمیلی ممکن است بعداً برسد.

برای پیمایش، تا وقتی page × limit < total است صفحه بعد را بخوانید. چون داده‌ها در حال تغییرند، هنگام دریافت چند صفحه شناسه call_id را در نرم‌افزار خود یکتا نگه دارید.

CDR بلافاصله پس از قطع، الزاماً نهایی نیست. تکمیل کانال‌ها، مسیر یا ضبط ممکن است زمان ببرد. برای پردازش نتیجه قطعی، رویداد call.cdr و برای فایل، recording.ready را دریافت کنید.
GET

جزئیات یک تماس#

/call

با شناسه تماس، آخرین رکورد گزارش همان تماس را دریافت کنید. این endpoint گزارش ثبت‌شده را می‌خواند؛ فهرست کانال‌های زنده نیست.

cURL
curl --get 'https://api.hamkaran.cloud/api/hamkaran/v2/call' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode 'call_id=11111111-1111-4111-8111-111111111111'

پاسخ HTTP 200 یک آبجکت تماس با همان فیلدهای گزارش و داده‌های تفصیلی موجود است. UUID نامعتبر ۴۲۲، نبود رکورد ۴۰۴ و محدودیت داخلی ۴۰۳ دارد.

GET

داشبورد مرکز تماس#

/dashboard

شاخص‌های تجمیعی تماس، مقایسه بازه قبل، روند روزانه و ساعتی، داخلی‌ها، صف‌ها و کیفیت ثبت‌شده را دریافت کنید. تاریخ‌های date_from و date_to یا نام‌های سازگار datefrom و dateto پذیرفته می‌شوند.

cURL
curl --get 'https://api.hamkaran.cloud/api/hamkaran/v2/dashboard' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode 'date_from=2026-09-09' \
  --data-urlencode 'date_to=2026-09-16'
بخش پاسخمحتوا
range / previous_rangeبازه انتخاب‌شده و بازه مقایسه
current / previousشاخص‌های تجمیعی هر بازه
directionsتفکیک ورودی، خروجی و داخلی
daily / hourlyروند روزانه و توزیع ساعت‌های روز
internals / queuesآمار داخلی‌ها و صف‌ها
quality / recent_missedکیفیت اندازه‌گیری‌شده و تماس‌های ازدست‌رفته اخیر
generated_at / data_until / restrictedزمان تولید، مرز داده و محدود بودن پاسخ به دسترسی داخلی‌های کلید

بازه داشبورد حداکثر ۹۰ روز است و روز پایان نباید آینده باشد. داده داشبورد کوتاه‌مدت کش می‌شود؛ برای پایش لحظه‌ای زنگ و اتصال از وب‌هوک استفاده کنید. نبود اندازه‌گیری کیفیت را معادل کیفیت صفر نگیرید.

GET

وضعیت داخلی‌ها#

/extension

فقط داخلی‌های مجاز برای کلید شما برگردانده می‌شوند. نمونه درخواست و پاسخ کامل در شروع سریع آمده است.

فیلد / مقدارمعنا
extensionشماره کوتاه داخلی، مانند رشته «200»
registeredآخرین وضعیت مشاهده‌شده رجیستر بودن گوشی
NOT_INUSEداخلی رجیستر است و در snapshot اشغال نیست.
INUSEداخلی در snapshot اشغال است.
UNAVAILABLEداخلی در snapshot رجیستر نیست.
UNKNOWNاطلاعات زنده در دسترس نیست؛ با آزاد بودن داخلی متفاوت است.
observed_atUnix timestamp مشاهده وضعیت، یا null در نبود داده

وضعیت به‌صورت دوره‌ای تازه می‌شود. پیش از ساخت تماس، بررسی این فیلد مفید است اما ثبت نهایی فرمان هم دسترسی و رجیستر بودن را دوباره بررسی می‌کند.

POST

دریافت ضبط تماس و کنفرانس#

/get_record

پاسخ موفق این endpoint فایل صوتی است، نه JSON. برای ضبط اصلی فقط call_id را بفرستید.

cURL · ذخیره فایل
curl --fail-with-body --request POST \
  'https://api.hamkaran.cloud/api/hamkaran/v2/get_record' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json' \
  --data '{"call_id":"11111111-1111-4111-8111-111111111111"}' \
  --output call-record.wav
مقدار trackفایل درخواستی
ارسال نشودضبط اصلی تماس؛ مقدار main را به عنوان track نفرستید.
mixedضبط مشترک کنفرانس
UUID ضبط عضوضبط جداگانه عضو؛ از recordings[].id استفاده کنید، نه شماره عضو.
voicemailپیام صندوق صوتی مربوط به تماس
JSON · دریافت ضبط مشترک کنفرانس
{
  "call_id": "11111111-1111-4111-8111-111111111111",
  "track": "mixed"
}

روش دیگر استفاده از recordings[].url در پاسخ گزارش است. این لینک امضاشده حدود ۱۵ دقیقه اعتبار دارد؛ آن را محرمانه نگه دارید. برای لینک تازه، گزارش تماس را دوباره بگیرید. وجود تماس، وجود ضبط را تضمین نمی‌کند؛ فایل آماده‌نشده یا ناموجود پاسخ ۴۰۴ دارد.

GET

فهرست پیام‌های صندوق صوتی#

/voicemails

رویدادهای پیام صندوق صوتی ثبت‌شده را با صفحه‌بندی ۲۵تایی دریافت کنید. این endpoint به کلید با دسترسی کامل مرکز نیاز دارد؛ کلید محدود به داخلی پاسخ ۴۰۳ می‌گیرد.

cURL
curl --get 'https://api.hamkaran.cloud/api/hamkaran/v2/voicemails' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode 'page=1'

پاسخ صفحه‌بندی Laravel است: رویدادها در data، صفحه در current_page، تعداد در total و آخرین صفحه در last_page. هر رویداد شناسه call_id دارد؛ برای دریافت صوت، آن را با track: voicemail به get_record بدهید.

فهرست رویدادها تابع دوره نگهداری وب‌هوک/یکپارچه‌سازی است؛ پیش‌فرض ۳۰ روز. نگهداری فایل صوتی مستقل است. برای آرشیو بلندمدت، سیاست ذخیره‌سازی خود را تعریف کنید.
POST

ایجاد تماس؛ Click to Call#

/create_call

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

فیلدنوع / الزامتوضیح
srcرشته؛ الزامیشماره داخلی مجاز و رجیسترشده؛ مانند «200»
destرشته؛ الزامیداخلی مقصد یا شماره تلفن؛ مانند «09120000001»
timeoutعدد؛ اختیاریمهلت شماره‌گیری بین ۵ تا ۶۰ ثانیه
cURL
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/create_call' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: crm-ticket-4821-call-001' \
  --data '{"src":"200","dest":"09120000001","timeout":30}'
JSON · HTTP 202
{
  "operation_id": "22222222-2222-4222-8222-222222222222",
  "action_id": "22222222-2222-4222-8222-222222222222",
  "actionId": "22222222-2222-4222-8222-222222222222",
  "status": "queued",
  "result": null
}
۱ · ثبت درخواست←۲ · زنگ داخلی←۳ · پاسخ کارشناس←۴ · شماره‌گیری مقصد←۵ · رویداد اتصال
HTTP 202 فقط ثبت فرمان را تأیید می‌کند. ابتدا نتیجه عملیات را بررسی کنید؛ برای تأیید برقراری مکالمه، رویداد call.connected و برای نتیجه نهایی، CDR را بخوانید.
GET

پیگیری نتیجه فرمان#

/operations

شناسه‌ای که از ثبت فرمان گرفته‌اید را با همان کلید اتصال ارسال کنید. این عملیات متعلق به همان اتصال است؛ کلید دیگری لزوماً به آن دسترسی ندارد.

cURL
curl --get 'https://api.hamkaran.cloud/api/hamkaran/v2/operations' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode 'operation_id=22222222-2222-4222-8222-222222222222'
JSON · نمونه پذیرش توسط موتور
{
  "operation_id": "22222222-2222-4222-8222-222222222222",
  "action_id": "22222222-2222-4222-8222-222222222222",
  "actionId": "22222222-2222-4222-8222-222222222222",
  "status": "succeeded",
  "result": {
    "call_id": "11111111-1111-4111-8111-111111111111",
    "state": "accepted",
    "job_id": "33333333-3333-4333-8333-333333333333"
  }
}
statusمعنا و اقدام
queued / runningدر انتظار یا در حال پردازش؛ با فاصله زمانی وضعیت را دوباره بخوانید.
succeededفرمان اجرا/پذیرفته شده است؛ result را بخوانید. accepted به معنی پاسخ مخاطب نیست.
failedاجرای فرمان شکست خورده؛ کد علت در result.error است.
expiredفرمان اجرا‌نشده از مهلت ۹۰ثانیه‌ای عبور کرده است.
unknownنتیجه اجرا قطعی نیست؛ پیش از درخواست تازه، رویدادها و گزارش را بررسی کنید. شماره‌گیری خودکار مجدد نکنید.

نمونه کد دارای polling محدود و کنترل timeout در بخش Node.js و Laravel آمده است.

POST

قطع تماس فعال#

/hang_up_call

برای قطع کانال اصلی، فقط call_id کافی است. برای کانال مشخص leg_id را هم ارسال کنید. کلید محدود به داخلی باید leg_id همان داخلی مجاز را بدهد.

cURL
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/hang_up_call' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: crm-ticket-4821-hangup-001' \
  --data '{"call_id":"11111111-1111-4111-8111-111111111111"}'

پاسخ ۲۰۲ همان ساختار عملیات است. اگر تماس بین ثبت و اجرا تمام شود، ممکن است نتیجه عملیات failed با call_not_active باشد.

POST

پخش فایل روی تماس#

/play_audio_on_channel

فایل موجود در بانک محتوای همین مرکز را روی کانال انتخابی پخش کنید. audio باید نام، file_id یا شناسه فایل مجاز باشد؛ URL دلخواه یا مسیر سرور پذیرفته نمی‌شود.

cURL
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/play_audio_on_channel' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: crm-ticket-4821-audio-001' \
  --data '{
    "call_id":"11111111-1111-4111-8111-111111111111",
    "leg_id":"44444444-4444-4444-8444-444444444444",
    "audio":"welcome.wav"
  }'

call_id و audio الزامی‌اند. leg_id برای کلید کامل اختیاری است و در صورت حذف، کانال اصلی انتخاب می‌شود. پاسخ، عملیات قابل پیگیری است.

POST

انتقال مستقیم یا با مشورت#

/transfer

کانال داخلی انتقال‌دهنده را مشخص و تماس را به داخلی مقصد منتقل کنید. blind برای انتقال مستقیم و attended برای انتقال با مشورت است. در این API مقصد انتقال باید داخلی باشد؛ دعوت شماره بیرونی به کنفرانس endpoint جدا دارد.

cURL
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/transfer' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: crm-ticket-4821-transfer-001' \
  --data '{
    "call_id":"11111111-1111-4111-8111-111111111111",
    "leg_id":"44444444-4444-4444-8444-444444444444",
    "dest":"201",
    "mode":"attended"
  }'

برای انتقال مستقیم مقدار mode را blind کنید. شروع، اتصال مقصد و خطا از رویدادهای transfer.started، transfer.completed و transfer.failed دنبال می‌شوند؛ ثبت فرمان پایان فرایند انتقال نیست.

POST

ساخت و مدیریت کنفرانس#

از مکالمه متصل‌شده داخلی شروع کنید. ابتدا آن را به کنفرانس ببرید، سپس پس از مشاهده رویداد ایجاد/عضویت، شرکت‌کننده داخلی یا موبایل را دعوت کنید. نمونه زیر از کانال داخلی میزبان استفاده می‌کند.

cURL · ورود مکالمه به کنفرانس
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/conference_start' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: crm-ticket-4821-room-001' \
  --data '{
    "call_id":"11111111-1111-4111-8111-111111111111",
    "leg_id":"44444444-4444-4444-8444-444444444444"
  }'
cURL · دعوت موبایل توسط میزبان
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/conference_invite' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: crm-ticket-4821-invite-001' \
  --data '{
    "call_id":"11111111-1111-4111-8111-111111111111",
    "leg_id":"44444444-4444-4444-8444-444444444444",
    "dest":"09120000001"
  }'
POST endpointفیلدهای Bodyکانال مورد استفاده
/conference_startcall_id، leg_idداخلی حاضر در مکالمه متصل‌شده
/conference_invitecall_id، leg_id، destمیزبان؛ مقصد داخلی یا شماره بیرونی است.
/conference_mutecall_id، leg_id، mutedعضوی که میکروفنش باید تغییر کند؛ muted از نوع boolean است.
/conference_kickcall_id، leg_idعضوی که باید از کنفرانس خارج شود.
JSON · Body برای conference_mute
{
  "call_id": "11111111-1111-4111-8111-111111111111",
  "leg_id": "55555555-5555-4555-8555-555555555555",
  "muted": true
}

برای وصل میکروفن muted را false کنید. برای خروج عضو همین شناسه‌ها را به conference_kick بفرستید. هر فرمان باید Idempotency-Key مستقل داشته باشد. عضویت واقعی را با conference.member_joined تأیید کنید؛ موفقیت دعوت به معنی پاسخ مخاطب نیست.

ضبط مشترک و ضبط اعضای موجود از recordings گزارش و get_record دریافت می‌شوند؛ وجود فایل هر عضو را از پاسخ بخوانید، نه از روی حدس شماره تلفن.

POST

ارسال تماس از پیش ضبط‌شده#

/send-pre-recorded

از یک خط متعلق به مرکز تماس، با مقصد تماس بگیرید و پس از پاسخ، فایل بانک محتوا را پخش کنید. این فرمان نیازمند کلید با دسترسی کامل مرکز است.

فیلدالزام / نوعتوضیح
srcالزامی؛ رشتهشماره خط متعلق به مرکز؛ مانند «02190000000»
destالزامی؛ رشتهشماره مخاطب
audioالزامی؛ رشتهنام، شناسه یا file_id فایل صوتی در بانک محتوای همین مرکز
timeoutاختیاری؛ عدد۵ تا ۶۰ ثانیه
cURL
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/send-pre-recorded' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: reminder-order-4821-001' \
  --data '{"src":"02190000000","dest":"09120000001","audio":"reminder.wav","timeout":30}'
src شناسه داخلی یا Caller ID دلخواه نیست؛ یک خط مجاز متعلق به مرکز است. اگر خط انتخابی در مسیر موجود در دسترس نباشد، سیستم بدون اطلاع از خط دیگری تماس نمی‌گیرد.
POST

پیگیری تماس صوتی آماده#

/get-pre-recorded-call-detail

برای حفظ نام endpoint آشنا در PBX V1، این مسیر هم وجود دارد. در PBX V2 پاسخ آن وضعیت عملیات است؛ برای نتیجه مکالمه، call_id داخل result را در گزارش بررسی کنید.

cURL
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/get-pre-recorded-call-detail' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{"actionId":"22222222-2222-4222-8222-222222222222"}'

این درخواست خواندنی است و Idempotency-Key نیاز ندارد. پاسخ همان ساختار operation_id، status و result در پیگیری فرمان است. برای اتصال تازه می‌توانید مستقیماً از operations استفاده کنید.

POST

ارسال کد تأیید صوتی فارسی#

/send-otp

پس از پاسخ مقصد، پیام «رمز یک‌بارمصرف شما» پخش و ارقام کد فارسی خوانده می‌شوند. پس از یک ثانیه مکث، پیام تکرار پخش می‌شود و همان کد یک بار دیگر رقم‌به‌رقم خوانده می‌شود. src و dest رشته شماره و code رشته ۳ تا ۸ رقم انگلیسی است. برای حفظ صفر اول، کد را عدد JSON نفرستید.

cURL
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/send-otp' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: verification-session-4821-001' \
  --data '{"src":"02190000000","dest":"09120000001","code":"048291","timeout":30}'

src الزامی و متعلق به مرکز است؛ کلید محدود به داخلی مجاز نیست. timeout اختیاری و ۵ تا ۶۰ ثانیه است. نتیجه ارسال مانند سایر فرمان‌ها پیگیری می‌شود. مقدار OTP وارد لاگ و ضبط این مسیر نمی‌شود.

تولید کد امن، انقضا، محدودیت تلاش و اعتبارسنجی کدی که کاربر وارد می‌کند مسئولیت برنامه شماست. این endpoint فقط تحویل صوتی را انجام می‌دهد؛ موفقیت فرمان به معنی تأیید هویت کاربر نیست.

عضویت و وضعیت داخلی در صف#

عضویت داینامیک همان صف‌هایی کنترل می‌شود که مدیر در سناریو تعریف کرده است. داخلی باید عضو مجاز داینامیک صف باشد؛ API مجوز تازه‌ای ایجاد نمی‌کند.

روش / endpointکاربرددسترسی
GET /queuessnapshot صف‌ها و اعضاکلید کامل مرکز
POST /queue_controlورود، خروج، استراحت یا پایان استراحتداخلی در محدوده مجاز کلید
GET /queue_operation?id=UUIDپیگیری نتیجه همان فرمان صفکلید کامل مرکز
cURL · ورود به صف
curl --request POST 'https://api.hamkaran.cloud/api/hamkaran/v2/queue_control' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{
    "id":"66666666-6666-4666-8666-666666666666",
    "extension":"200",
    "action":"join",
    "queue_code":"21"
  }'

id یک UUID تازه برای عملیات منطقی است؛ در تلاش مجدد همان id را بفرستید. action یکی از join، leave، break یا resume است. برای ورود/خروج، کد واقعی صف را مشخص کنید؛ استراحت/پایان استراحت وضعیت داخلی را در صف‌هایش تغییر می‌دهد.

cURL · پیگیری نتیجه عضویت
curl --get 'https://api.hamkaran.cloud/api/hamkaran/v2/queue_operation' \
  --header "key: $HAMKARAN_WEBSERVICE_KEY" \
  --header 'Accept: application/json' \
  --data-urlencode 'id=66666666-6666-4666-8666-666666666666'

HTTP 202 ثبت درخواست است؛ نتیجه اعمال واقعی را پیگیری کنید. خروج از صف و استراحت، مکالمه جاری داخلی را قطع نمی‌کند.

نمونه کامل Node.js و PHP / Laravel#

این نمونه‌ها از کلید ذخیره‌شده روی سرور استفاده می‌کنند. برای اجرای مثال ایجاد تماس، داخلی و مقصد واقعی خود را جایگزین کنید؛ اجرای آن گوشی‌ها را زنگ می‌زند.

Node.js؛ ثبت و پیگیری تماس

برای Node.js دارای fetch داخلی، مانند نسخه ۲۰ یا جدیدتر. کلید درخواست را در رکورد CRM ذخیره کنید تا تلاش مجدد پس از timeout همان عملیات را دنبال کند.

Node.js · create-call.mjs
import { setTimeout as sleep } from 'node:timers/promises';

const base = 'https://api.hamkaran.cloud/api/hamkaran/v2';
const key = process.env.HAMKARAN_WEBSERVICE_KEY;
if (!key) throw new Error('HAMKARAN_WEBSERVICE_KEY is required');

async function request(path, { body, requestId } = {}) {
  const response = await fetch(base + path, {
    method: body ? 'POST' : 'GET',
    headers: {
      key,
      Accept: 'application/json',
      ...(body ? { 'Content-Type': 'application/json' } : {}),
      ...(requestId ? { 'Idempotency-Key': requestId } : {}),
    },
    ...(body ? { body: JSON.stringify(body) } : {}),
    signal: AbortSignal.timeout(15000),
  });
  const data = await response.json();
  if (!response.ok) {
    // Do not log request headers or OTP values.
    throw new Error('HTTP ' + response.status + ': ' + (data.message || 'API error'));
  }
  return data;
}

// Persist this value with the CRM task. Reuse it after a network timeout.
const requestId = 'crm-ticket-4821-call-001';
let operation = await request('/create_call', {
  body: { src: '200', dest: '09120000001', timeout: 30 },
  requestId,
});

for (let attempt = 0; attempt < 10; attempt++) {
  if (!['queued', 'running'].includes(operation.status)) break;
  await sleep(5000);
  operation = await request(
    '/operations?' + new URLSearchParams({ operation_id: operation.operation_id })
  );
}

console.log({
  operation_id: operation.operation_id,
  status: operation.status,
  call_id: operation.result?.call_id,
});

// "succeeded" + "accepted" is not proof of an answered call.
// Continue with call.connected / call.cdr webhooks.
// If still queued/running, retain operation_id and check later.
// Never automatically redial an "unknown" operation.

Node.js؛ گزارش و دریافت فایل

کد زیر را می‌توانید در همان ماژول بالا، به جای بخش ساخت تماس، استفاده کنید. برای دانلود، به شناسه call_id یک گزارش آماده نیاز دارید.

Node.js · خواندن گزارش و ذخیره ضبط
import { writeFile } from 'node:fs/promises';

const query = new URLSearchParams({
  date_from: '2026-09-09',
  date_to: '2026-09-16',
  page: '1',
  limit: '25',
});
const report = await request('/reports?' + query);
console.log({ total: report.total, page: report.page });

const callId = '11111111-1111-4111-8111-111111111111';
const response = await fetch(base + '/get_record', {
  method: 'POST',
  headers: { key, 'Content-Type': 'application/json', Accept: 'application/json' },
  body: JSON.stringify({ call_id: callId }),
  signal: AbortSignal.timeout(30000),
});
if (!response.ok) throw new Error('Recording HTTP ' + response.status);
await writeFile('call-record.wav', Buffer.from(await response.arrayBuffer()));
// Store recordings in private storage. For large files, stream to disk.

PHP / Laravel؛ تنظیم کلید و ساخت تماس

در متغیر محیطی برنامه خود HAMKARAN_WEBSERVICE_KEY را تعریف کنید؛ سپس آن را از config بخوانید.

PHP · config/services.php
return [
    // Other service configurations...
    'hamkaran' => [
        'key' => env('HAMKARAN_WEBSERVICE_KEY'),
        'base_url' => 'https://api.hamkaran.cloud/api/hamkaran/v2',
    ],
];
PHP · Laravel HTTP Client
use Illuminate\Support\Facades\Http;

$base = config('services.hamkaran.base_url');
$key = config('services.hamkaran.key');
$requestId = 'crm-ticket-4821-call-001'; // Persist with the CRM task.

$response = Http::withHeaders([
    'key' => $key,
    'Accept' => 'application/json',
    'Idempotency-Key' => $requestId,
])->connectTimeout(3)->timeout(15)->post($base . '/create_call', [
    'src' => '200',
    'dest' => '09120000001',
    'timeout' => 30,
]);

$response->throw();
$operation = $response->json();
$operationId = $operation['operation_id'];

// In a later queued job, query the same operation.
// Do not keep a web request blocked while waiting for a call.
$result = Http::withHeaders([
    'key' => $key,
    'Accept' => 'application/json',
])->connectTimeout(3)->timeout(15)->get($base . '/operations', [
    'operation_id' => $operationId,
])->throw()->json();

// queued/running: schedule another bounded check.
// unknown: reconcile with call events before any new request.
// succeeded: inspect result; accepted is not an answered call.

PHP / Laravel؛ گزارش و ضبط

PHP · Laravel
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;

$headers = [
    'key' => config('services.hamkaran.key'),
    'Accept' => 'application/json',
];
$base = config('services.hamkaran.base_url');

$report = Http::withHeaders($headers)->timeout(20)
    ->get($base . '/reports', [
        'date_from' => '2026-09-09',
        'date_to' => '2026-09-16',
        'internalnumber' => '200',
        'page' => 1,
        'limit' => 25,
    ])->throw()->json();

$recording = Http::withHeaders($headers)->timeout(30)
    ->post($base . '/get_record', [
        'call_id' => '11111111-1111-4111-8111-111111111111',
        // 'track' => 'mixed', // Optional conference mix.
    ])->throw();

// Use a private disk; do not publish the recording URL.
Storage::disk('local')->put('private/calls/example.wav', $recording->body());

مثال‌های دانلود برای فایل‌های کوچک‌اند. برای فایل‌های طولانی، از streaming و سقف اندازه متناسب با برنامه خود استفاده کنید.

مهاجرت از PBX V1 و مسیر سازگاری#

نسخه مرکز تماس با شماره مسیر API یکی نیست. مراکز PBX V1 همچنان مسیر قبلی را دارند. درخواست مسیر قدیمی برای یک مرکز PBX V2 توسط پردازشگر PBX V2 انجام می‌شود؛ برای اتصال جدید، مسیر صریح v2 را انتخاب کنید.

موضوعPBX V1 / اتصال قبلیPBX V2
آدرس API/api/hamkaran/v1//api/hamkaran/v2/؛ مسیر قدیمی برای مرکز V2 نیز قابل استفاده است.
شناسه تماسشناسه اعلام‌شده در قرارداد V1UUID؛ شناسه Asterisk را منتقل نکنید.
پاسخ فرمانقرارداد قبلی۲۰۲ و operation_id؛ در مسیر سازگاری ۲۰۰، همراه همان عملیات قابل پیگیری
نام پارامتر مقصدdst یا destdest؛ dst نیز پذیرفته می‌شود.
شناسه ورودی تماسuid / channel_idبه call_id نگاشت می‌شوند؛ برای کانال مشخص leg_id را جدا ارسال کنید.
کد صوتیotp یا codecode؛ otp نیز پذیرفته می‌شود. src خط الزامی است.
جزئیات تماس صوتیساختار قرارداد V1وضعیت عملیات؛ نتیجه مکالمه از گزارش یا وب‌هوک
گزارش در مسیر قدیمیساختار قبلیبرای مرکز V2، نگاشت سازگاری در data.rows ارائه می‌شود؛ مسیر جدید data/total/page/limit دارد.
جلوگیری از تکراربسته به اتصال قبلیIdempotency-Key ثابت برای retry. مسیر قدیمی در نبود هدر کلید تازه می‌سازد؛ این حالت جلوی تکرار CRM را نمی‌گیرد.
نام endpoint آشنا به معنی یکسان بودن تمام پاسخ‌ها نیست. قبل از جابه‌جایی مرکز، خواندن گزارش، اجرای فرمان و مصرف وب‌هوک را با قرارداد PBX V2 تطبیق دهید. مستندات PBX V1 برای مراکز همان نوع معتبر می‌ماند.

دسترسی، محدودیت و طراحی اتصال#

  • پیش‌فرض سقف درخواست هر کلید ۱۲۰ درخواست در دقیقه است؛ در تنظیمات اتصال بین ۱ تا ۶۰۰۰ قابل تنظیم است. کد polling را با سهم درخواست خود تنظیم کنید.
  • کلید را به IP و داخلی‌های مورد نیاز محدود کنید. کنترل کانال با کلید محدود، نیازمند leg_id همان داخلی مجاز است.
  • تماس صوتی خودکار، فهرست صندوق صوتی، snapshot صف‌ها و خواندن عملیات صف به کلید کامل مرکز نیاز دارند.
  • برای هر مرکز حداکثر ۱۰۰ فرمان queued پذیرفته می‌شود. ۴۲۹ را با انتظار و backoff مدیریت کنید، نه حلقه تکرار فوری.
  • شماره‌ها و کدها را رشته و با ارقام انگلیسی ارسال کنید. برای نمایش، خروجی عددی رشته را به عدد زبان برنامه‌نویسی تبدیل نکنید.
  • از وب‌هوک برای اتفاقات زنده و از گزارش برای حسابداری نهایی استفاده کنید. گزارش‌گیری دائمی سنگین جای وب‌هوک را نمی‌گیرد.
  • درخواست و نتیجه فرمان در لاگ ماژول اتصال ثبت می‌شود؛ برای پشتیبانی operation_id، زمان و نام endpoint کافی است. کلید و OTP را ارسال نکنید.

عیب‌یابی قدم‌به‌قدم#

نشانهچه چیزی را بررسی کنم؟
خطای ۴۰۳ از اولین درخواستHeader key، فعال بودن اتصال، نوع PBX V2، IP مبدأ و محدودیت داخلی را بررسی کنید؛ ابتدا extension را بخوانید.
عملیات accepted است ولی مخاطب پاسخ ندادهaccepted فقط پذیرش توسط موتور است. رویداد اتصال و CDR را ببینید؛ ممکن است هنوز در حال زنگ باشد.
extension_not_registeredگوشی داخلی مبدأ را رجیستر کنید و observed_at وضعیت داخلی را ببینید.
call_not_active / call_not_ownedتماس ممکن است تمام شده یا کانال مربوط به تماس/مرکز دیگری باشد. call_id و leg_id را از همان رویداد زنده بردارید.
conference_host_requiredبرای دعوت، leg_id میزبان را بفرستید؛ شناسه عضو عادی کافی نیست.
انتقال یا کنفرانس ۲۰۲ می‌دهد اما عمل نمی‌کندresult عملیات را بررسی کنید؛ داخلی باید در مکالمه مناسب باشد. وضعیت واقعی را از رویدادهای انتقال/عضویت دنبال کنید.
ضبط ۴۰۴ استمالکیت تماس، آماده‌شدن فایل و track درست را بررسی کنید. لینک امضاشده قدیمی را با گرفتن مجدد گزارش تازه کنید.
خط انتخابی تماس صوتی کار نمی‌کندsrc باید خط متعلق به مرکز و در مسیر قابل استفاده باشد؛ داخلی مبدأ یا Caller ID دلخواه قابل قبول نیست.
HTTP timeout پس از ارسال تماسبا همان درخواست و Idempotency-Key ادامه دهید؛ کلید جدید می‌تواند تماس دوم ایجاد کند.
داشبورد خطای تاریخ داردروز پایان نباید آینده باشد؛ فاصله بازه داشبورد حداکثر ۹۰ روز است.
گزارش خالی یا در حال تکمیل استبازه ایران، فیلترها و دسترسی داخلی را بررسی کنید؛ برای نهایی‌شدن CDR و ضبط زمان در نظر بگیرید.
برای پشتیبانی: نام endpoint، کد HTTP، زمان رخداد، operation_id و call_id را در تیکت پنل بفرستید. کلید وب‌سرویس، secret وب‌هوک و کد OTP را در تیکت قرار ندهید.
↑