GET /me
اطلاعات کلید، کلینیک و محدودیت تعداد درخواستها
توسعهدهندگان
با API بوکورا، سایت یا نرمافزار کلینیک رو به نوبتها و مشخصات پایه بیماران متصل کنید. ارتباط بین سرورها انجام میشه و مالک کلینیک کلید دسترسی رو در پنل میسازه.
API در پلن کلینیک و دوره آزمایشی همین پلن در دسترسه. اگر به پلنی بدون دسترسی API تغییر بدید، همهٔ درخواستهای API با خطای FEATURE_NOT_IN_PLAN رد میشن. همچنان میتونید اطلاعات رو در پنل ببینید و خروجی بگیرید.
curl https://bookora.net/api/v1/clinic-api/me \
-H "Authorization: Bearer bk_live_<public-id>_<secret>" نشانی پایه: https://bookora.net/api/v1/clinic-api.
پاسخها ساختار {"data": …, "meta": …} دارن. پاسخ خطا شامل فیلدهای statusCode، code، message، details و requestId میشه؛ متن message فارسیه. مبلغها به ریال و بهصورت عدد صحیح، و زمانها با قالب ISO-8601 و به وقت جهانی (UTC) ارسال میشن.
در هر درخواست، هدر Authorization: Bearer bk_live_<public-id>_<secret> رو ارسال کنید. کلید لغوشده یا منقضیشده بلافاصله رد میشه. هنگام تعویض کلید میتونید برای اعتبار کلید قبلی مهلت تعیین کنید.
| دسترسی | کارهای مجاز | دسترسی لازم در پنل |
|---|---|---|
catalog.read | خدمات، قیمتها و پزشکان — فهرست خدمات فعال با مدت و قیمت، و پزشکان فعال کلینیک. | — |
availability.read | زمانهای آزاد — زمانهای آزاد برای نوبتدهی، بر اساس ساعات کاری و نوبتهای ثبتشده. | — |
appointments.read | مشاهده نوبتها — نوبتهای کلینیک با زمان، وضعیت، پزشک و خدمت؛ بدون یادداشت و مبلغ. | — |
appointments.write | ثبت، لغو و جابهجایی نوبت — ثبت نوبت جدید در زمان خالی، لغو و جابهجایی نوبت. | appointments.readavailability.read |
patients.read | جستوجو و مشاهده مشخصات پایه بیمار — جستوجوی بیمار با تطبیق کامل شماره موبایل یا کد ملی و مشاهده نام و شماره تماس؛ بدون سوابق پزشکی. | — |
patients.write | ثبت بیمار جدید — ثبت پرونده جدید با نام و شماره موبایل. | patients.read |
هیچکس نمیتونه کلیدی با سطح دسترسی بالاتر از دسترسی خودش در پنل بسازه.
هر پاسخ هدرهای X-RateLimit-Limit، X-RateLimit-Remaining، X-RateLimit-Reset رو داره. اگر تعداد درخواستها از سقف مجاز بیشتر بشه، پاسخ ۴۲۹ همراه با Retry-After برمیگرده.
برای ثبت نوبت یا بیمار، باید هدر Idempotency-Key رو ارسال کنید؛ مقدار اون باید بین ۸ تا ۱۲۸ کاراکتر باشه. برای لغو یا جابهجایی نوبت، ارسال این هدر اختیاریه. اگر تا ۲۴ ساعت همون درخواست رو با همون کلید و بدنه دوباره بفرستید، نتیجه قبلی با هدر Idempotent-Replayed: true برمیگرده و نوبت یا پرونده تکراری ثبت نمیشه.
curl -X POST https://bookora.net/api/v1/clinic-api/appointments \
-H "Authorization: Bearer bk_live_<public-id>_<secret>" \
-H "Idempotency-Key: 6f1c2a9e-booking-4411" \
-H "Content-Type: application/json" \
-d '{"patientId":"<patient-id>","serviceId":"<service-id>","startAt":"2026-10-10T07:30:00.000Z"}' GET /meاطلاعات کلید، کلینیک و محدودیت تعداد درخواستها
GET /servicesخدمات فعال کلینیک با مدت و قیمت (ریال)
GET /doctorsپزشکان فعال کلینیک
GET /availabilityزمانهای آزاد در بازهای حداکثر دوهفتهای برای هر درخواست
| نام | محل ارسال | الزامی | توضیح |
|---|---|---|---|
from | query | بله | آغاز بازه (ISO-8601) |
to | query | بله | پایان بازه (ISO-8601) |
serviceId | query | بله | شناسهٔ خدمت |
doctorId | query | نه | فقط زمانهای این پزشک |
branchId | query | نه | فقط این شعبه |
GET /appointmentsفهرست نوبتها؛ بدون یادداشتها و مبلغها
| نام | محل ارسال | الزامی | توضیح |
|---|---|---|---|
from | query | نه | زمان شروع بازه |
to | query | نه | زمان پایان بازه |
status | query | نه | وضعیت نوبت |
doctorId | query | نه | پزشک |
patientId | query | نه | بیمار |
branchId | query | نه | شعبه |
updatedSince | query | نه | تغییرات پس از این زمان؛ ردیفهای حذفشده با deleted مشخص میشن |
page | query | نه | شمارهٔ صفحه |
pageSize | query | نه | تعداد ردیفهای هر صفحه، حداکثر ۱۰۰ |
GET /appointments/{id}جزئیات یک نوبت
| نام | محل ارسال | الزامی | توضیح |
|---|---|---|---|
id | path | بله | شناسهٔ نوبت |
POST /appointmentsثبت نوبت در زمان آزاد، با وضعیت پیشفرض «تأییدشده»
| نام | محل ارسال | الزامی | توضیح |
|---|---|---|---|
patientId | body | بله | شناسهٔ بیمار |
serviceId | body | بله | شناسهٔ خدمت |
startAt | body | بله | زمان شروع (ISO-8601) |
doctorId | body | نه | پزشک دلخواه |
branchId | body | نه | شعبه |
status | body | نه | CONFIRMED یا REQUESTED |
notes | body | نه | یادداشت، حداکثر ۵۰۰ کاراکتر |
POST /appointments/{id}/cancelلغو نوبت
| نام | محل ارسال | الزامی | توضیح |
|---|---|---|---|
id | path | بله | شناسهٔ نوبت |
reason | body | نه | دلیل، حداکثر ۳۰۰ کاراکتر |
POST /appointments/{id}/rescheduleجابهجایی نوبت به زمان آزاد دیگر
| نام | محل ارسال | الزامی | توضیح |
|---|---|---|---|
id | path | بله | شناسهٔ نوبت |
startAt | body | بله | زمان شروع جدید (ISO-8601) |
doctorId | body | نه | پزشک دلخواه |
GET /patients/lookupجستوجوی بیمار با شماره موبایل یا کد ملی کامل و دقیق
| نام | محل ارسال | الزامی | توضیح |
|---|---|---|---|
phone | query | نه | شمارهٔ موبایل؛ وارد کردن شمارهٔ موبایل یا کد ملی الزامی است |
nationalId | query | نه | کد ملی؛ وارد کردن کد ملی یا شمارهٔ موبایل الزامی است |
GET /patients/{id}مشخصات پایه بیمار، بدون سوابق پزشکی
| نام | محل ارسال | الزامی | توضیح |
|---|---|---|---|
id | path | بله | شناسهٔ بیمار |
POST /patientsثبت بیمار جدید
| نام | محل ارسال | الزامی | توضیح |
|---|---|---|---|
firstName | body | بله | نام |
lastName | body | بله | نام خانوادگی |
phone | body | بله | شمارهٔ موبایل |
gender | body | نه | جنسیت |
birthDate | body | نه | تاریخ تولد |
nationalId | body | نه | کد ملی |
acquisitionSource | body | نه | نحوهٔ آشنایی بیمار با کلینیک |
| وضعیت | code | توضیح |
|---|---|---|
| ۴۰۰ | VALIDATION_FAILED | اطلاعات ورودی معتبر نیست؛ فیلد مربوط در details مشخص شده |
| ۴۰۰ | IDEMPOTENCY_KEY_REQUIRED | هدر Idempotency-Key رو ارسال کنید |
| ۴۰۰ | APPOINTMENT_NOT_CANCELLABLE | نوبت پایانیافته رو نمیشه لغو کرد |
| ۴۰۱ | API_KEY_REQUIRED | کلید API ارسال نشده |
| ۴۰۱ | API_KEY_INVALID | کلید API معتبر نیست |
| ۴۰۱ | API_KEY_REVOKED | اعتبار کلید لغو شده |
| ۴۰۱ | API_KEY_EXPIRED | اعتبار کلید تمام شده |
| ۴۰۲ | FEATURE_NOT_IN_PLAN | پلن کلینیک دسترسی API نداره یا اشتراک تموم شده؛ همه درخواستها رد میشن |
| ۴۰۳ | API_SCOPE_DENIED | کلید این سطح دسترسی رو نداره |
| ۴۰۴ | *_NOT_FOUND | رکوردی با این شناسه در کلینیک پیدا نشد |
| ۴۰۹ | APPOINTMENT_SLOT_TAKEN | این زمان دیگه آزاد نیست |
| ۴۰۹ | IDEMPOTENCY_IN_PROGRESS | درخواست قبلی با همین کلید هنوز در حال پردازش است |
| ۴۲۲ | IDEMPOTENCY_KEY_REUSED | این کلید قبلاً برای درخواستی با بدنهٔ متفاوت استفاده شده |
| ۴۲۹ | RATE_LIMITED | تعداد درخواستها از سقف مجاز بیشتر شده؛ زمان انتظار رو در Retry-After ببینید |
| ۵۰۳ | CLINIC_API_DISABLED | API موقتاً غیرفعال است |
با وبهوک میتونید از تغییرات نوبتها باخبر بشید. در پنل برای هر نشانی، رویدادهای موردنیاز رو انتخاب کنید. نشانی باید از HTTPS استفاده کنه و از داخل ایران در دسترس باشه؛ نشانیهای شبکه داخلی پذیرفته نمیشن. پیام وبهوک شامل اطلاعات هویتی بیمار نیست.
appointment.created — ثبت نوبتappointment.confirmed — تأیید نوبتappointment.rescheduled — جابهجایی نوبتappointment.cancelled — لغو نوبتappointment.no_show — عدم مراجعه بیمارappointment_request.created — درخواست نوبت جدید{
"id": "evt_…",
"type": "appointment.cancelled",
"apiVersion": "2026-10-01",
"createdAt": "2026-10-10T07:31:02.000Z",
"clinicId": "…",
"data": {
"appointment": {
"id": "…", "status": "CANCELLED",
"startAt": "…", "endAt": "…",
"patientId": "…", "doctorId": "…",
"serviceIds": ["…"], "source": "API"
},
"cancelledByPatient": false,
"actor": { "type": "api_key", "apiKeyId": "…" }
}
} هر پیام هدر X-Bookora-Signature: t=<unix>,v1=<hex> داره. برای ساخت امضا، مقدار t، یک نقطه و بدنه خام درخواست کنار هم قرار میگیرن و با HMAC-SHA256 و رمز وبهوک امضا میشن. هنگام تعویض رمز، یک امضای v1 دیگه با رمز قبلی هم فرستاده میشه. هدرهای X-Bookora-Event و X-Bookora-Delivery هم همراه پیام ارسال میشن.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody: the request body exactly as received (a Buffer or string).
export function verifyBookoraSignature(rawBody, header, secret, toleranceSec = 300) {
const items = header.split(',').map((part) => part.trim().split('='));
const t = items.find(([key]) => key === 't')?.[1];
const signatures = items.filter(([key]) => key === 'v1').map(([, value]) => value);
if (!t || signatures.length === 0) return false;
if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSec) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return signatures.some(
(sig) => sig.length === expected.length && timingSafeEqual(Buffer.from(sig), Buffer.from(expected)),
);
} function verify_bookora_signature(string $rawBody, string $header, string $secret, int $tolerance = 300): bool
{
$t = null;
$signatures = [];
foreach (explode(',', $header) as $part) {
[$key, $value] = array_pad(explode('=', trim($part), 2), 2, '');
if ($key === 't') { $t = $value; }
if ($key === 'v1') { $signatures[] = $value; }
}
if ($t === null || !$signatures || abs(time() - (int) $t) > $tolerance) {
return false;
}
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
foreach ($signatures as $signature) {
if (hash_equals($expected, $signature)) { return true; }
}
return false;
} import hashlib
import hmac
import time
def verify_bookora_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
t, signatures = None, []
for part in header.split(","):
key, _, value = part.strip().partition("=")
if key == "t":
t = value
elif key == "v1":
signatures.append(value)
if t is None or not signatures or abs(time.time() - int(t)) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(expected, signature) for signature in signatures) پاسخ 2xx یعنی پیام با موفقیت دریافت شده. پاسخهای دیگه، تغییر مسیر یا پاسخ ندادن تا ۱۰ ثانیه، ارسال ناموفق حساب میشن. تلاشهای بعدی با فاصلههای ۱ دقیقه، ۵ دقیقه، ۳۰ دقیقه، ۲ ساعت، ۶ ساعت، ۱۲ ساعت، ۲۴ ساعت انجام میشن و در مجموع حدود دو روز ادامه دارن. اگر ارسال به یک نشانی ۵۰ بار پیاپی ناموفق باشه، یا طی ۷۲ ساعت حداقل ۱۰ بار ارسال ناموفق داشته باشه و هیچ ارسال موفقی نداشته باشه، ارسال به اون نشانی خودکار متوقف میشه و به مالک کلینیک اطلاع داده میشه.
ممکنه پیامها به ترتیب ایجادشون نرسن. اونها رو بر اساس createdAt مرتب کنید و برای دریافت وضعیت فعلی، اطلاعات نوبت رو دوباره از API بخونید.
apiVersion: 2026-10-01 — نسخه اول شامل دوازده مسیر برای نوبتها و بیماران، شش رویداد وبهوک و امکان انتخاب شعبه در نوبتها و زمانهای آزاده.