الأخطاء
تُعاد جميع الأخطاء بصيغة موحدة: ok: false مع كائن error:
{ "ok": false, "error": { "code": "INVALID_API_KEY", "message": "Invalid API key" }}code: رمزUPPER_SNAKE_CASEقابل للقراءة آليًا، ثابت، ولا يتغير دون رفع رئيسي للإصدار. بدّل (switch) عليه، لا علىmessage.message: شرح مقروء للإنسان، بالإنجليزية. قد يُحسَّن النص دون رفع الإصدار.details: اختياري. فيINVALID_INPUTهو مصفوفة{ path, message }لكل حقل فشل. فيINVALID_FIELDيُضاف إلى كل عنصرexpectedمع الاسم القانوني للحقل.docs: اختياري، رابط إلى قسم في هذه الصفحة عندما يوجد للرمز شرح أوفى.request_id: يُضاف فقط إلى استجابات5xxوBAD_JSON؛ اذكره عند التواصل مع الدعم.
رموز HTTP
Section titled “رموز HTTP”| الحالة | المعنى |
|---|---|
200 OK | نجاح |
400 Bad Request | JSON تالف، أو حقول ناقصة، أو فشل التحقق عبر Zod |
401 Unauthorized | X-Api-Key غائب أو غير صالح |
402 Payment Required | يلزم خطة أعلى أو حزمة، أو الاشتراك غير نشط |
403 Forbidden | المفتاح صالح لكن بلا صلاحيات (scope، origin، suspended) |
404 Not Found | المورد أو المفتاح أو الـ job غير موجود |
422 Unprocessable Entity | المدخلات صالحة لكن المحرك لم يتمكن من الحساب |
429 Too Many Requests | تجاوز الـ rate limit، أو نفاد الأرصدة، أو بلوغ spend-cap |
500 Internal Server Error | خطأ داخلي من جهتنا، اذكر request_id |
503 Service Unavailable | بوابة الذكاء الاصطناعي أو قائمة الانتظار غير متاحة مؤقتًا |
رموز الأخطاء
Section titled “رموز الأخطاء”المصادقة (401)
Section titled “المصادقة (401)”| الرمز | المعنى |
|---|---|
MISSING_API_KEY | لم تُرسَل الترويسة X-Api-Key |
INVALID_API_KEY | المفتاح غير موجود أو أُلغي (revoked) |
MISSING_BEARER | اختيرت صيغة Authorization: Bearer لكن الرمز المميز غائب |
WRONG_AUTH_METHOD | أُرسل X-Api-Key إلى /v1/me/* (Bearer فقط). للتحقق من المفتاح: GET /v1/auth/keys/me؛ ولاستدعاءات الـ API: أي /v1/* مع X-Api-Key |
الصلاحيات (403)
Section titled “الصلاحيات (403)”| الرمز | المعنى |
|---|---|
INSUFFICIENT_SCOPE | ليس للمفتاح صلاحية على هذا الإجراء |
DOMAIN_MISMATCH / FORBIDDEN_ORIGIN | الـ Origin الخاص بالطلب ليس في قائمة السماح للمفتاح |
ENDPOINT_NOT_IN_SCOPE | المفتاح مقيّد بقائمة نقاط نهاية (allowed_endpoints)، وهذا المسار ليس فيها. details يذكر القائمة |
KEY_SUSPENDED | المفتاح معلّق |
الفوترة (402)
Section titled “الفوترة (402)”| الرمز | المعنى |
|---|---|
PLAN_UPGRADE_REQUIRED | نقطة النهاية تتطلب خطة أعلى أو حزمة add-on |
PLAN_PACK_MISMATCH | نقطة النهاية جزء من حزمة غير موجودة على المفتاح |
SUBSCRIPTION_EXPIRED | انتهت الفترة المدفوعة منذ أكثر من 3 أيام. في الأيام الثلاثة الأولى بعد الانتهاء تُخدم الطلبات بحسب الخطة وتحمل الترويسة X-Subscription-Grace-Ends. يحتوي details على expired_at وgrace_ended_at وrenew_url |
Rate limiting والأرصدة (429)
Section titled “Rate limiting والأرصدة (429)”| الرمز | المعنى |
|---|---|
RATE_LIMIT | تجاوز حد الـ RPM. تحتوي الاستجابة على الترويسة Retry-After: <sec> |
CREDITS_EXHAUSTED | استُنفدت ميزانية الأرصدة الشهرية |
SPEND_CAP_REACHED | بُلغ حد الإنفاق على نقاط نهاية الذكاء الاصطناعي |
KEY_BUDGET_EXHAUSTED | استنفد المفتاح ميزانيته الخاصة (credits_cap_cycle). قد تبقى أرصدة في الحساب: القيد يخص هذا المفتاح تحديدًا |
التحقق (400)
Section titled “التحقق (400)”| الرمز | المعنى |
|---|---|
INVALID_INPUT | فشل حقل أو أكثر في التحقق عبر Zod. details مصفوفة { path, message } |
INVALID_FIELD | يحتوي الجسم على اسم حقل مختصر بدل الاسم القانوني. انظر أدناه |
MISSING_FIELDS | حقل إلزامي غائب |
BAD_JSON | JSON تالف في جسم الطلب |
مثال على 400:
{ "ok": false, "error": { "code": "INVALID_INPUT", "message": "Validation failed: date: Invalid input: expected string, received undefined", "details": [ { "path": "date", "message": "Invalid input: expected string, received undefined" }, { "path": "time", "message": "Invalid input: expected string, received undefined" } ] }}INVALID_FIELD
Section titled “INVALID_FIELD”نقاط نهاية الخرائط تقبل أسماء الحقول الكاملة فقط. الصيغ المختصرة تُرفض بـ 400، ولا تُقرأ بصمت:
| المُرسَل | الحقل القانوني | المتوقع |
|---|---|---|
lat | latitude | درجات عشرية، الشمال موجب |
lng، lon، long | longitude | درجات عشرية، الشرق موجب |
tz مع رقم، tzOffset، utcOffset، timezone_offset | timezoneOffset | عدد الساعات عن UTC، مثل 5.75 |
tz مع اسم، timeZone، time_zone | timezone | اسم منطقة IANA، مثل Europe/Kyiv، أو auto |
تسرد الاستجابة جميع الحقول الموجودة دفعة واحدة، بترتيب جسم الطلب، فتتم إعادة التسمية في مرور واحد:
{ "ok": false, "error": { "code": "INVALID_FIELD", "message": "Unsupported fields: \"tz\", \"lat\", \"lng\". Rename them to \"timezoneOffset\", \"latitude\", \"longitude\". See details for what each one expects.", "details": [ { "path": "tz", "expected": "timezoneOffset", "message": "numeric hours from UTC, e.g. 5.75; a zone name goes in timezone" }, { "path": "lat", "expected": "latitude", "message": "decimal degrees, north positive" }, { "path": "lng", "expected": "longitude", "message": "decimal degrees, east positive" } ], "docs": "https://api.astroway.info/en/errors/#invalid_field" }}يشير path إلى المسار الكامل، لذا يظهر في synastry أي خريطة بالضبط: chart1.lat، لا مجرد lat.
يُعاد الرمز نفسه عندما يتعذر تحليل timezone: قيمة فارغة، أو اختصار مثل EST (وهو -5 ثابت طوال السنة، فيخرج نيويورك في يوليو خاطئًا بساعة)، أو اسم غير موجود في قاعدة المناطق الزمنية، أو إزاحة مكتوبة كنص مثل +03:00، أو auto بلا latitude وlongitude. تقترح الرسالة ما ترسله بدلًا من ذلك، ويشير path إلى الكائن، مثل chart2.timezone.
أمران يستحقان الانتباه.
هذا ليس إهمالًا تدريجيًا (deprecation). الأسماء المختصرة لم تكن يومًا جزءًا من العقد، ولم تظهر في /openapi.json كحقول جسم. قبل 2026-08-01 لم تكن تعمل، بل كانت تُتجاهل بصمت: للإحداثيات قيمة صفرية افتراضية، فكانت الخريطة تُحسب لـ 0°N 0°E بتوقيت UTC. الطالع والبيوت وكل ما يُشتق منها كان خاطئًا، والاستجابة لا تقول ذلك. لذلك لن تُقبل الصيغ المختصرة مجددًا: اسمان لحقل واحد إلى الأبد، وtz فوق ذلك ملتبس (رقم أم اسم منطقة).
الكائن المتداخل point أمر مختلف. في /v1/acg-zones و/v1/acg/by-category و/v1/ccg-analysis و/v1/eclipse-analysis الكائن point: { lat, lng } أسماء حقول مُعلنة، وهي تمر.
الإحداثيات الغائبة: 400 بدءًا من 2026-11-09
Section titled “الإحداثيات الغائبة: 400 بدءًا من 2026-11-09”الاسم الصحيح للحقل نصف المسألة. الجسم بلا latitude وlongitude كان يعطي 200 أيضًا، لأن للإحداثيات قيمة صفرية افتراضية، فكانت الاستجابة تصف 0°N 0°E.
حتى 2026-11-09 يعمل طلب كهذا، لكن الاستجابة تحمل Deprecation: true وSunset وWarning والحقل _warning في الجسم. بدءًا من 2026-11-09 يصبح 400 INVALID_INPUT مع latitude is required, in decimal degrees.
القيمتان الصريحتان latitude: 0, longitude: 0 تبقيان صالحتين: المشكلة في غياب الحقل لا في قيمته. يحتفظ timezoneOffset بالصفر الافتراضي، لأن الصفر هناك يعني UTC. لا يحل city محل الإحداثيات: لا أحد يحوّله جغرافيًا إلى إحداثيات. يُطبَّق التحقق فقط على الكائنات التي تحمل date وtime معًا.
أخطاء الحساب (422)
Section titled “أخطاء الحساب (422)”| الرمز | المعنى |
|---|---|
CALCULATION_ERROR | المدخلات صالحة لكن المحرك لم يتمكن من الحساب (مثلًا خط عرض قطبي مع Placidus). جرّب Whole Sign / Equal أو تاريخًا آخر. |
أخطاء الخادم
Section titled “أخطاء الخادم”| الرمز | HTTP | المعنى |
|---|---|---|
INTERNAL_ERROR | 500 | خطأ غير متوقع. نسجله مع stack trace، اذكر request_id |
GATEWAY_UNAVAILABLE / AI_UNAVAILABLE / LLM_UNAVAILABLE | 503 | بوابة الذكاء الاصطناعي غير متاحة. أعد المحاولة مع backoff أسّي |
QUEUE_UNAVAILABLE | 503 | قائمة انتظار المهام الخلفية غير متاحة |
المعالجة في كود العميل
Section titled “المعالجة في كود العميل”type ApiError = { ok: false; error: { code: string; message: string; details?: unknown; request_id?: string; };};
async function chart(input: ChartInput) { const res = await fetch('https://api.astroway.info/v1/chart', { method: 'POST', headers: { 'X-Api-Key': process.env.ASTROWAY_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify(input), });
const body = await res.json(); if (!body.ok) { throw new AstrowayError(body.error.code, body.error.message, body.error.request_id); }
return body.data;}
class AstrowayError extends Error { constructor( public code: string, message: string, public requestId?: string, ) { super(`[${code}] ${message}${requestId ? ` (request_id=${requestId})` : ''}`); }}بدّل على code، لا على message. الـ SDK الرسمية (@astroway/sdk، astroway) ترمي أصنافًا فرعية مُنمَّطة من الأخطاء بحسب حالة HTTP (AuthenticationError، RateLimitError، BadRequestError …)، فلن تحتاج إلى كتابة كود المعالجة.
طلب الدعم
Section titled “طلب الدعم”للتواصل أرسل إلى support@astroway.info:
request_idمن الاستجابة (إذا كان5xx)- نقطة النهاية وجسم JSON مختصرًا (بلا أسرار)
- السلوك المتوقع مقابل الفعلي
- المنطقة الزمنية والوقت التقريبي للطلب (UTC)
الرد ضمن SLA خطتك (48 ساعة لـ Starter، 24 ساعة لـ Pro، وخاص بـ Enterprise).