نظرة عامةنظرة عامة على API

نظرة عامة

نظرة عامة على API

كل نقاط نهاية Sofia API v1 مع معاملاتها ونطاقاتها وأمثلتها، وساحة تجارب لتجربة كل منها بمفتاح API الخاص بك.

العنوان الأساسي https://api.sofia-post.com/api · مستند OpenAPI

يمنح Sofia API شيفرتك وصولًا إلى حسابك في Sofia: منشآت Workspaces الخاصة بك، وشبكاتها المرتبطة، ومكتبة وسائطها، ومنشوراتها، وإحصاءاتها. إنها واجهة REST عبر HTTPS؛ والطلبات والردود بصيغة JSON. وهي تتيح لك:

  • النشر: الآن أو في تاريخ محدد أو كمسودة، على شبكة واحدة أو عدة شبكات معًا.
  • التحقق قبل النشر: تخبرك المحاكاة ما إذا كانت كل شبكة ستقبل المنشور، دون كتابة أي شيء.
  • إدارة الوسائط: ارفع ملفًا أو استورده من عنوان URL إلى مكتبة وسائط منشأة.
  • متابعة المنشورات: اقرأ حالة كل شبكة ورابطها الدائم أو سبب الفشل. ويمكن أن ينبّهك webhook موقّع أيضًا.
  • قراءة الإحصاءات: آخر الأرقام التي جمعتها Sofia لكل حساب مرتبط.
  • ربط الشبكات: أنشئ رابطًا مستضافًا يتيح للمالك ربط شبكة ناقصة.

SDK والتكاملات

  • CLI `sofia`: واجهة CLI، المنشورة على npm باسم sofia-post، لتشغيل Sofia من طرفية.
  • خادم MCP: اربط Sofia بوكلاء الذكاء الاصطناعي لديك.
  • Zapier: يرتبط تكامل Sofia بمفتاحك الشخصي. ويوفر المشغّلين «Post Published» و«Post Failed» والإجراء «Create Post» (نشر أو جدولة أو حفظ مسودة) والبحث «Find Post». لم يُقدَّم بعد إلى دليل Zapier العام.

المصادقة

يحمل كل استدعاء رمز Bearer في الترويسة Authorization. تعيد Sofia قراءة الصلاحيات المرتبطة به عند كل استدعاء: شخص أُزيل من Workspace، أو إذن أُلغي، يوقف الرمز منذ الطلب التالي.

هناك طريقتان للمصادقة:

الطريقةلـالرمز
مفتاح شخصيأتمتة حسابك الخاص، والسكربتات، والوكلاءsofia_pk_live_…، صالح حتى انتهاء صلاحيته
OAuth، رمز التفويض + PKCEمنتج يعمل لمصلحة مستخدميه، على الـ Workspace الذي يختارونهsofia_live_at_…، ساعة واحدة، قابل للتجديد
OAuth، client credentialsأحد خوادمك الذي يؤتمت Workspace أنت مالكهsofia_live_at_…، ساعة واحدة

مفتاح API

إن كنت تؤتمت Workspace الخاص بك، فيكفي مفتاح شخصي. في تطبيق Sofia، افتح الإعدادات › API، تبويب الوصول الشخصي، وأنشئ مفتاحًا. اختر اسمه ومدة صلاحيته وأذوناته. لا يُعرض المفتاح إلا مرة واحدة: انسخه واحتفظ به في خزنة أسرار، لا في شيفرتك أبدًا.

أنشئ مفتاحًا في الإعدادات › API › الوصول الشخصي. يعمل باسمك على كل Workspaces الخاصة بك: المنشأة التي تسميها تحدد الـ Workspace، ودورك فيه يحدد ما يستطيع المفتاح فعله.

أرسل المفتاح في الترويسة Authorization كرمز Bearer:

curl https://api.sofia-post.com/api/v1/establishments \
  -H "Authorization: Bearer sofia_pk_live_…"

`GET /v1/me` يخبرك لمن يعمل المفتاح، وما Workspaces الخاصة به وأذوناته.

الاستدعاء الذي يفتقر إلى الإذن (scope) المطلوب يرد بـ 403 insufficient_scope.

رمز OAuth2

لمنتج SaaS يعمل لمصلحة مستخدميه على Workspaces الخاصة بهم، سجّل عميل OAuth: يفوّض كل شخص تطبيقك على الـ Workspace الذي يختاره. تبدأ رموز وصول OAuth بـ sofia_live_at_…، وتدوم ساعة، وتُستخدم بالطريقة نفسها:

curl https://api.sofia-post.com/api/v1/establishments \
  -H "Authorization: Bearer sofia_live_at_…"

تبني تطبيقًا تتصل به حسابات Sofia أخرى؟ تُوصف عملية authorization code مع PKCE وclient credentials وscopes في OAuth2.

عنوان URL الأساسي

كل endpoint موجود تحت عنوان URL أساسي واحد:

https://api.sofia-post.com/api/v1

حدود المعدل

يحسب Sofia API الطلبات في ثلاث نوافذ: 15 دقيقة و24 ساعة و30 يومًا.

الرمز15 دقيقة24 ساعة30 يومًا
مفتاح شخصي (لكل حساب)1501 00010 000
عميل OAuth10050015 000
عميل OAuth، لكل شخص مفوِّض (حتى 1 000)+10+25+100

الترويسات

يخبرك كل رد بما تبقى، بصيغة IETF:

RateLimit: "15m";r=144;t=43, "24h";r=994;t=13543, "30d";r=9994;t=13543
RateLimit-Policy: "15m";q=150;w=900, "24h";q=1000;w=86400, "30d";q=10000;w=2592000

r عدد الطلبات المتبقية، وt الثواني قبل إعادة ضبط النافذة، وq الحصة، وw النافذة بالثواني.

حين تمتلئ نافذة

الرد هو 429 rate_limit_exceeded، مع ترويسة Retry-After والنافذة المستنفدة في details. انتظر هذه المهلة قبل إعادة المحاولة.

استخدام API بكفاءة

  • سجّل webhook بدل استطلاع منشور في حلقة.
  • استخدم limit حتى 100 لقراءة القوائم في صفحات أقل.
  • خزّن مؤقتًا ما يتغير نادرًا: المنصات والمنشآت والحسابات المرتبطة.

الأخطاء

يرد كل خطأ بحالة HTTP وغلاف JSON نفسه. code هو العقد الذي تقرؤه شيفرتك؛ وmessage مكتوب لإنسان وقد يتغير.

{
  "error": {
    "code": "invalid_request",
    "message": "The request is invalid",
    "details": [
      { "field": "targets[0].text", "code": "required", "message": "targets[0].text is required" }
    ],
    "docUrl": "https://www.sofia-post.com/developers/errors#invalid_request",
    "requestId": "2aab5943-6333-4caf-8478-25a85ebd4abf"
  }
}
  • details تقول أكثر: الحقل المرفوض، وسبب الشبكة، ونافذة الحد المستنفدة.
  • docUrl يقود إلى شرح الرمز في هذه الصفحة.
  • requestId يعرّف الطلب: أبلغ به الدعم.

رموز الأخطاء

الحالةالرمزالمعنى
400invalid_requestالجسم أو الاستعلام أو معامل غير صالح. تفصيل واحد لكل حقل مرفوض: field وcode (required وis_string وmax…) وmessage.
400invalid_jsonالجسم ليس JSON صالحًا.
400invalid_cursorالمؤشر لم تُعدْه Sofia API.
400webhook_url_invalidعنوان webhook ليس https، أو يقود إلى عنوان خاص.
400webhook_event_invalidحدث webhook غير معروف.
400application_name_invalidالإعدادات › API: اسم العميل مفقود أو طويل جدًا.
400application_profile_invalidالإعدادات › API: حقل في ملف العميل غير صالح؛ وتذكره التفاصيل.
400logo_invalidالإعدادات › API: الشعار مرفوض (180×180 px على الأقل، بصيغة PNG أو JPEG أو WebP، و2 MB كحد أقصى).
401invalid_tokenالرمز مفقود أو غير معروف أو منتهي أو ملغى، أو أن الشخص الذي وراءه فقد الصلاحيات التي يحملها. راجع الترويسة WWW-Authenticate.
401unauthorizedالمصادقة مطلوبة.
402subscription_requiredالكتابة الحية (بما فيها التجربة الجافة) تتطلب اشتراكًا نشطًا أو فترة تجريبية.
403insufficient_scopeالرمز يفتقر إلى نطاق نقطة النهاية؛ ويسميه التفصيل.
403insufficient_roleدورك في مساحة العمل هذه لا يسمح بهذا الإجراء.
403live_mode_requiredكتابة حقيقية برمز اختبار.
403route_closed_to_applicationsنقطة النهاية هذه غير مفتوحة للتطبيقات.
403forbiddenالوصول مرفوض.
403session_requiredالإعدادات › API: يتطلب هذا الإجراء جلسة Sofia وليس رمزًا.
403impersonation_forbiddenالإعدادات › API: هذا الإجراء مرفوض في جلسة مستعارة.
403workspace_owner_requiredوحده مالك مساحة العمل (Workspace) يستطيع فعل ذلك.
403workspace_member_requiredأنت لست عضوًا في مساحة العمل (Workspace) هذه.
404route_not_foundلا توجد نقطة نهاية تجيب عن هذه الطريقة وهذا المسار.
404establishment_not_foundمنشأة غير معروفة، أو خارج متناول الرمز.
404media_not_foundوسائط غير معروفة، أو خارج متناول الرمز.
404post_not_foundمنشور غير معروف، أو خارج متناول الرمز.
404webhook_not_foundwebhook غير معروف، أو يخص تطبيقًا آخر.
404not_foundالمورد غير موجود.
404application_not_foundالإعدادات › API: عميل غير معروف.
404grant_not_foundتفويض غير معروف أو ملغى بالفعل.
404personal_key_not_foundالإعدادات › API: مفتاح شخصي غير معروف.
405method_not_allowedهذه الطريقة غير مسموح بها على نقطة النهاية هذه.
409post_in_flightشبكة ترسل المنشور: أعد محاولة الحذف بعد قليل.
409post_not_editableأرسلت شبكة المنشور بالفعل: لم يعد قابلًا للتعديل.
409account_already_connectedهذه الشبكة متصلة بالفعل بالمنشأة.
409webhook_limit_reachedلدى التطبيق بالفعل 10 webhooks.
409idempotency_in_progressالطلب الأول بهذا Idempotency-Key لا يزال قيد التنفيذ.
409conflictيتعارض الطلب مع الحالة الحالية للمورد.
409scope_removal_forbiddenالإعدادات › API: يمكن إضافة صلاحية إلى عميل لكن لا يمكن إزالتها.
409public_client_has_no_secretالإعدادات › API: العميل العام ليس له سر.
413payload_too_largeالملف أكبر من حد الرفع.
415unsupported_media_typeنوع محتوى غير مدعوم.
422post_refusedشبكة أو المنشأة أو باقتك سترفض المنشور. تفصيل واحد لكل سبب، لكل منها message خاصة به.
422media_import_refusedتعذّر استيراد الوسائط من هذا الرابط؛ ويذكر التفصيل السبب.
422media_upload_refusedالملف المرفوع ليس صورة أو فيديو مدعومًا.
422platform_not_offeredهذه الشبكة غير متاحة للربط.
422idempotency_key_reusedاستُخدم هذا Idempotency-Key مع جسم مختلف.
422unprocessable_entityالطلب مفهوم لكن لا يمكن معالجته.
429rate_limit_exceededنافذة حد المعدل ممتلئة: انتظر انقضاء Retry-After.

أسباب رفض منشور

يسرد post_refused كل سبب في details[].code:

الرمزالمعنى
unsupported_platformهذه الشبكة غير مدعومة.
duplicate_platformذُكرت الشبكة نفسها مرتين.
unsupported_formatهذه الشبكة لا تقدم هذه الصيغة.
caption_too_longالنص طويل جدًا (max، actual).
too_many_hashtagsهاشتاغات كثيرة جدًا (max، actual).
too_many_mentionsإشارات كثيرة جدًا (max، actual).
too_many_thread_postsالسلسلة طويلة جدًا (max، actual).
too_many_mediaوسائط كثيرة جدًا (max، actual).
media_mix_refusedلا يمكن الجمع بين الصور والفيديوهات على هذه الشبكة.
media_requiredتتطلب هذه الشبكة صورة أو فيديو.
media_type_refusedهذه الشبكة لا تقبل هذا النوع من الوسائط.
scheduled_in_pastscheduledAt في الماضي.
scheduled_at_invalidالمنشور المجدول يحتاج إلى scheduledAt في المستقبل.
account_not_connectedلا يوجد حساب متصل على هذه الشبكة لهذه المنشأة.
account_ambiguousعدة حسابات متصلة على هذه الشبكة: سمِّ أحدها بـ accountId.
media_not_foundالوسائط ليست في مكتبة هذه المنشأة.
media_url_invalidيجب أن يكون رابط الوسائط عنوان https عامًا.
platform_unavailableهذه الشبكة غير مفتوحة على Sofia بعد.
publication_quota_exceededبلغت حصة النشر في باقتك.

معالجة الأخطاء

  • ابنِ منطقك على code لا على message أبدًا.
  • أعد محاولة 429 بعد Retry-After، و5xx بتأخير أُسّي.
  • لا تعد محاولة 4xx كما هو أبدًا: صحّح الطلب أولًا.
  • سجّل requestId عند كل فشل.

الترقيم

تُعاد القوائم (GET /v1/posts وGET /v1/media) الأحدث أولًا، صفحة صفحة. limit يحدد حجم الصفحة من 1 إلى 100 (20 افتراضيًا).

{
  "data": [ … ],
  "nextCursor": "WyIyMDI2LTEwLTI3VDE1OjExOjU5LjAxN1oiLCIx…"
}

مرّر nextCursor بصفتها cursor لقراءة الصفحة التالية. حين تغيب nextCursor، تكون قد بلغت النهاية.

let cursor;
do {
  const url = new URL('https://api.sofia-post.com/api/v1/posts');
  url.searchParams.set('limit', '100');
  if (cursor) url.searchParams.set('cursor', cursor);
  const page = await globalThis.fetch(url, { headers: { Authorization: `Bearer ${key}` } }).then((r) => r.json());
  handle(page.data);
  cursor = page.nextCursor;
} while (cursor);

المؤشر معتم: لا تبنِه ولا تعدّله أبدًا. المؤشر الذي لم تعده Sofia يرد بـ 400 invalid_cursor.

قابلية التكرار الآمن

قد يتركك خطأ شبكي في شك من إنشاء منشور. أرسل ترويسة Idempotency-Key مع POST /v1/posts وPOST /v1/media، وأعد المحاولة بالمفتاح نفسه: ترد Sofia بما ردت به أول مرة، ولا تنشئ شيئًا مكررًا.

curl -X POST https://api.sofia-post.com/api/v1/posts \
  -H "Authorization: Bearer $SOFIA_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d @post.json
  • استخدم مفتاحًا عشوائيًا جديدًا (UUID) لكل منشور جديد، والمفتاح نفسه لكل محاولة جديدة لذلك المنشور.
  • المفتاح نفسه مع جسم مختلف يرد بـ 422 idempotency_key_reused.
  • ما دام الطلب الأول جاريًا، يرد المفتاح نفسه بـ 409 idempotency_in_progress: أعد المحاولة بعد لحظة.

وضع الاختبار والمحاكاة

المحاكاة. أضف ?dry_run=true إلى POST /v1/posts: تنفّذ Sofia فحوص منشور حقيقي (قواعد كل شبكة، والحساب المرتبط، والوسائط، والتاريخ، وباقتك) دون حفظ أو إرسال أي شيء. تفصيل الردود في صفحة النشر.

وضع الاختبار. يقرأ التطبيق المنشأ في وضع test (رموز sofia_test_at_…) الـ Workspace الحقيقي لديك لكنه لا يكتب فيه أبدًا: تُحاكى منشوراته وتُقيَّم بالقواعد نفسها، وكل كتابة حقيقية ترد بـ 403 live_mode_required. المفاتيح والعملاء المنشأون في الإعدادات › API هم في وضع live؛ استخدم المحاكاة للتحقق دون نشر.

OAuth

الحساب

المنصات

المنشآت

الوسائط

المنشورات

Webhooks