تخطَّ إلى المحتوى الرئيسي

Developer Page Specifications

API Contracts

GET listing/summary/details endpoints + POST subscription. Query params, response shapes, caching.

مكتمل

ثلاث endpoints تُمَكِّن سطح Opportunity Radar العام: بحث القائمة، عَدَّادات الملخّص، وجلب التفاصيل. endpoint داخلي واحد يُمَكِّن الاشتراكات. كل الاستجابات تُدرك المحلّية عبر header Accept-Language و query param احتياطي ?lang= (lang يَفوز حين كلاهما حاضر). الاستجابات JSON، مضغوطة gzip، cache-control محترم في CDN.

1 · GET /api/opportunities — القائمة

Query params
q (string ≥2) · status (open|closing-soon|rolling|closed|all) · type (csv من slugs) · country (csv من ISO codes) · stage (csv) · sector (csv) · sort (most-relevant|deadline-soonest|newest|recently-verified|closing-soon) · page (int ≥1، افتراضي 1) · pageSize (int ≤100، افتراضي 24) · lang (en|ar).
شكل الاستجابة
{ items: OpportunityListItem[]، totalCount: int، pageSize: int، page: int، filters: { applied، available }، counts: { open، closingSoon، rolling، closed، all } }.
حقول OpportunityListItem
id، slug، title، shortDescription، status، type {slug، label}، country {code، name}، stage[]، sectors[]، organizer {id، name، logoUrl}، deadlineDate (ISO 8601 أو null)، isRolling، isSponsored، isVerified، officialUrl، publishedAt، lastVerifiedAt.
التخزين المؤقّت
Cache-Control: public، max-age=120، s-maxage=300، stale-while-revalidate=600. أبطل عند نشر / قلب حالة Opportunity.

2 · GET /api/opportunities/summary — عَدَّادات اللوحة

  • لا params (المحلّية عبر header). يُرجع { open: int، closingSoon: int، rolling: int، tracked: int، latest: OpportunityListItem[] } حيث latest هو أحدث 5 فرص نشطة.
  • Cache-Control: public، max-age=60، s-maxage=120، stale-while-revalidate=300.

3 · GET /api/opportunities/{slug} — التفاصيل

Path params
slug (string، مطلوب، kebab-case).
Query params
lang (en|ar، اختياري). يَفترض Accept-Language.
شكل الاستجابة
{ id، slug، title، shortDescription، fullDescription، programStructure?، whyItMatters?، founderProfile?، minimumRequirements?، applicationRequirements?، bestForStages?، status، type، country، multiCountries[]، stages[]، sectors[]، organizer {full}، deadlineDate، isRolling، isSponsored، isVerified، officialUrl، valueSignal، urgency، timeRequired، benefits[]، eligibilityCriteria[]، applicationRequirements {struct}، timeline[]، relatedOpportunities[]، connectedSignals[]، seo {metaTitle، metaDescription، ogImage، jsonLd}، publishedAt، lastVerifiedAt، verifiedBy {displayName} }.
شكل connectedSignals[]
[{ sourceEntityType، sourceEntityId، sourceTitle، sourceCoverUrl، reason، sourceRoute، relationType، displayOrder }]. مَحدود في 8 جانب-الخادم؛ العميل يَستطيع طلب /api/opportunities/{slug}/connected?offset=8 للقائمة الكاملة.
التخزين المؤقّت
Cache-Control: public، max-age=300، s-maxage=600، stale-while-revalidate=1800. أبطل عند update / verify لـ Opportunity.
الأخطاء
404 حين slug مجهول. 410 Gone حين IsActive=false (نادر — soft-deletes تَحفظ المحتوى لكن قد تُرجع 410 لو التحرير قَرَّر التقاعد). أبداً 500 للعميل — سَجِّل جانب-الخادم، أرجع 503 مع تلميح إعادة المحاولة.

4 · POST /api/subscriptions/opportunity-digest

  • الـ Body: { email، country?، stage?، consentPlatform (bool، يجب true)، consentPartners (bool، افتراضي false)، source: 'listing' | 'details'، opportunityIdContext? (uuid، فقط حين source='details') }.
  • الاستجابة: 201 { subscriptionId } عند النجاح. 422 مع أخطاء على مستوى الحقول عند فشل التحقّق.
  • جانب-الخادم: يُزيل تكرار البريد؛ المشترك الحالي يَحصل على حقول التفضيل مُدمَجة. consentPartners=true يُحَفِّز webhook منفصل لـ sponsor-CRM.
  • لا header rate-limit مكشوف للعميل؛ احترم 429 لو رَجع.