نظرة عامة
نظرة عامة على 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 يومًا |
|---|---|---|---|
| مفتاح شخصي (لكل حساب) | 150 | 1 000 | 10 000 |
| عميل OAuth | 100 | 500 | 15 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=2592000r عدد الطلبات المتبقية، و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يعرّف الطلب: أبلغ به الدعم.
رموز الأخطاء
| الحالة | الرمز | المعنى |
|---|---|---|
| 400 | invalid_request | الجسم أو الاستعلام أو معامل غير صالح. تفصيل واحد لكل حقل مرفوض: field وcode (required وis_string وmax…) وmessage. |
| 400 | invalid_json | الجسم ليس JSON صالحًا. |
| 400 | invalid_cursor | المؤشر لم تُعدْه Sofia API. |
| 400 | webhook_url_invalid | عنوان webhook ليس https، أو يقود إلى عنوان خاص. |
| 400 | webhook_event_invalid | حدث webhook غير معروف. |
| 400 | application_name_invalid | الإعدادات › API: اسم العميل مفقود أو طويل جدًا. |
| 400 | application_profile_invalid | الإعدادات › API: حقل في ملف العميل غير صالح؛ وتذكره التفاصيل. |
| 400 | logo_invalid | الإعدادات › API: الشعار مرفوض (180×180 px على الأقل، بصيغة PNG أو JPEG أو WebP، و2 MB كحد أقصى). |
| 401 | invalid_token | الرمز مفقود أو غير معروف أو منتهي أو ملغى، أو أن الشخص الذي وراءه فقد الصلاحيات التي يحملها. راجع الترويسة WWW-Authenticate. |
| 401 | unauthorized | المصادقة مطلوبة. |
| 402 | subscription_required | الكتابة الحية (بما فيها التجربة الجافة) تتطلب اشتراكًا نشطًا أو فترة تجريبية. |
| 403 | insufficient_scope | الرمز يفتقر إلى نطاق نقطة النهاية؛ ويسميه التفصيل. |
| 403 | insufficient_role | دورك في مساحة العمل هذه لا يسمح بهذا الإجراء. |
| 403 | live_mode_required | كتابة حقيقية برمز اختبار. |
| 403 | route_closed_to_applications | نقطة النهاية هذه غير مفتوحة للتطبيقات. |
| 403 | forbidden | الوصول مرفوض. |
| 403 | session_required | الإعدادات › API: يتطلب هذا الإجراء جلسة Sofia وليس رمزًا. |
| 403 | impersonation_forbidden | الإعدادات › API: هذا الإجراء مرفوض في جلسة مستعارة. |
| 403 | workspace_owner_required | وحده مالك مساحة العمل (Workspace) يستطيع فعل ذلك. |
| 403 | workspace_member_required | أنت لست عضوًا في مساحة العمل (Workspace) هذه. |
| 404 | route_not_found | لا توجد نقطة نهاية تجيب عن هذه الطريقة وهذا المسار. |
| 404 | establishment_not_found | منشأة غير معروفة، أو خارج متناول الرمز. |
| 404 | media_not_found | وسائط غير معروفة، أو خارج متناول الرمز. |
| 404 | post_not_found | منشور غير معروف، أو خارج متناول الرمز. |
| 404 | webhook_not_found | webhook غير معروف، أو يخص تطبيقًا آخر. |
| 404 | not_found | المورد غير موجود. |
| 404 | application_not_found | الإعدادات › API: عميل غير معروف. |
| 404 | grant_not_found | تفويض غير معروف أو ملغى بالفعل. |
| 404 | personal_key_not_found | الإعدادات › API: مفتاح شخصي غير معروف. |
| 404 | consent_not_found | انتهى طلب التفويض أو لا وجود له: ابدأ من جديد من نقطة نهاية التفويض. |
| 405 | method_not_allowed | هذه الطريقة غير مسموح بها على نقطة النهاية هذه. |
| 409 | post_in_flight | شبكة ترسل المنشور: أعد محاولة الحذف بعد قليل. |
| 409 | post_not_editable | أرسلت شبكة المنشور بالفعل: لم يعد قابلًا للتعديل. |
| 409 | account_already_connected | هذه الشبكة متصلة بالفعل بالمنشأة. |
| 409 | webhook_limit_reached | لدى التطبيق بالفعل 10 webhooks. |
| 409 | idempotency_in_progress | الطلب الأول بهذا Idempotency-Key لا يزال قيد التنفيذ. |
| 409 | conflict | يتعارض الطلب مع الحالة الحالية للمورد. |
| 409 | scope_removal_forbidden | الإعدادات › API: يمكن إضافة صلاحية إلى عميل لكن لا يمكن إزالتها. |
| 409 | public_client_has_no_secret | الإعدادات › API: العميل العام ليس له سر. |
| 413 | payload_too_large | الملف أكبر من حد الرفع. |
| 415 | unsupported_media_type | نوع محتوى غير مدعوم. |
| 422 | post_refused | شبكة أو المنشأة أو باقتك سترفض المنشور. تفصيل واحد لكل سبب، لكل منها message خاصة به. |
| 422 | media_import_refused | تعذّر استيراد الوسائط من هذا الرابط؛ ويذكر التفصيل السبب. |
| 422 | media_upload_refused | الملف المرفوع ليس صورة أو فيديو مدعومًا. |
| 422 | platform_not_offered | هذه الشبكة غير متاحة للربط. |
| 422 | idempotency_key_reused | استُخدم هذا Idempotency-Key مع جسم مختلف. |
| 422 | unprocessable_entity | الطلب مفهوم لكن لا يمكن معالجته. |
| 429 | rate_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_past | scheduledAt في الماضي. |
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
/v1/oauth/authorizeابدأ تفويضًاافتحه في متصفح الشخص. عميل غير معروف أو redirect_uri غير مسجّل يعطي خطأ 400، وإلا يُعاد التوجيه إلى شاشة موافقة Sofia.GET/.well-known/oauth-authorization-server/api/v1/oauthاقرأ بيانات الخادم الوصفيةيعيد البيانات الوصفية لخادم التفويض OAuth 2.0 في Sofia API (RFC 8414)، ليكتشف العميل نقاط النهاية والخيارات المدعومة.POST/v1/oauth/tokenأصدر رمز وصوليستبدل client_credentials أو authorization_code مع PKCE أو refresh_token للاستخدام مرة واحدة برمز معتم صالح 3600 ثانية لمساحة عمل واحدة.الحساب
المنصات
المنشآت
/v1/establishmentsاعرض المنشآتيسرد منشآت مساحة العمل التي يتصرف فيها الرمز. استخدم معرّفاتها كـ establishmentId عند النشر أو سرد الوسائط أو قراءة الإحصاءات.GET/v1/establishments/{id}/accountsاعرض الحسابات المتصلةيسرد الشبكات المتصلة بمنشأة، بلا رمز ولا سر شبكة، فقط ما يلزم لتسمية حساب واختياره كـ accountId في المنشورات المقبلة.POST/v1/establishments/{id}/connection-linksأنشئ رابط ربطيعيد رابطًا مستضافًا يفوّض فيه الشخص شبكة مع Sofia، فلا يصل أي رمز شبكة إلى تطبيقك. صالح 30 دقيقة، للتطبيق الفعلي فقط.GET/v1/establishments/{id}/insightsاعرض إحصاءات المنشأةيعيد أحدث الأرقام الصالحة التي جمعتها Sofia لكل حساب متصل. الحساب الذي لم يُجمع بعد يظهر awaiting، وليس صفرًا أبدًا.الوسائط
/v1/mediaاعرض الوسائطيسرد مكتبة وسائط منشأة، الأحدث أولًا، صفحة بصفحة، بروابط موقّعة للنافذة الحالية. يعمل في الوضعين الاختباري والفعلي.POST/v1/mediaارفع وسائط أو استوردهايضيف ملفًا (multipart) أو رابط https تجلبه Sofia إلى مكتبة وسائط منشأة. يتطلب تطبيقًا فعليًا واشتراكًا نشطًا للحساب.GET/v1/media/{id}اعرض وسائطيعيد وسائط واحدة من المكتبة برابط موقّع صالح للنافذة الحالية، لتعيد استخدام معرّفها في منشور دون رفعها من جديد.DELETE/v1/media/{id}أزل وسائطيزيل وسائط من مكتبة منشأتها كما يفعل التطبيق. لا يُتلف الملف نفسه، ويحتفظ المنشور المجدول بصورته كما كانت من قبل.المنشورات
/v1/postsاعرض المنشوراتيسرد منشورات المنشآت التي يصل إليها الرمز، الأحدث أولًا، مع تصفية حسب المنشأة والحالة والفترة والكاتب، صفحة بصفحة.POST/v1/postsانشر منشورًاتنشر التطبيقات الفعلية فورًا أو تجدول بموعد أو تحفظ مسودة، أما الاختبارية فتحاكي فقط. مع ?dry_run=true يفحص كل شيء ولا يكتب شيئًا.GET/v1/posts/{id}اعرض منشورًايعيد منشورًا مع حالة كل شبكة ورابطه بعد النشر وخطئه إن فشل. يقرأ الوضع الفعلي أي منشور في مساحة العمل، والاختباري منشورًا محاكى.PATCH/v1/posts/{id}عدّل منشورًاقبل أن تبدأ أي شبكة بالإرسال، يستبدل targets أو يضبط scheduledAt أو ينقل المنشور بين draft وscheduled. تبقى الحقول المحذوفة كما هي.DELETE/v1/posts/{id}احذف منشورًايحذف منشورًا كما يفعل التطبيق: لا يُرسل شيء مجدول ويُزال المنشور المنشور من شبكته. التطبيقات الاختبارية لا تملك شيئًا حقيقيًا لحذفه.Webhooks
/v1/webhooksاعرض الـ webhooksيسرد webhooks التطبيق دون سرّ التوقيع، الذي لا يُعاد إلا مرة واحدة عند تسجيل الـ webhook، فلا يمكن استرجاعه لاحقًا.POST/v1/webhooksسجّل webhookيسجّل رابط https تستدعيه Sofia بحدث JSON موقّع، مع إعادة المحاولة حتى خمس مرات عند الفشل. يظهر السر مرة واحدة؛ 10 webhooks كحد أقصى.DELETE/v1/webhooks/{id}احذف webhookيحذف webhook. لن يتلقى شيئًا بعد ذلك، حتى عمليات التسليم التي كانت تنتظر إعادة محاولة، ولا يعيد الاستدعاء أي جسم.POST/v1/webhooks/{id}/testأرسل حدث اختباريسلّم حدث webhook.test موقّعًا كأي حدث آخر، دون انتظار المستقبِل، لتتحقق من توقيعك. خمسة أحداث كحد أقصى في الدقيقة.