المطوّرون
محتواك على موقعك الخاص
يصل المحتوى المعتمد في CoText والمنشور على قناة موقعك الخاص إلى موقعك بطريقتين: يرسل CoText إلى موقعك طلب Webhook موقّعًا عند النشر، أو يسحب موقعك المحتوى من واجهة القراءة (API). ويمكنك استخدام الطريقتين معًا. وتعتمد Zapier وMake وn8n المسارات نفسها.
Webhook والتحقق من التوقيع
عند إضافة القناة، تُدخل عنوان URL الخاص بـ Webhook في موقعك (https فقط). يُنشئ CoText سرّ توقيع (whsec_…) ويعرضه مرة واحدة فقط. كل طلب موقَّع بتنسيق Standard Webhooks، لذا يمكنك استخدام المكتبات الجاهزة (Node وPython وPHP وGo …) كما هي.
webhook-id: معرّف الحدث. يبقى كما هو عند إعادة المحاولة؛ يُرجى عدم معالجة معرّف سبقت معالجته.webhook-timestamp: الوقت بالثواني بتوقيت Unix. يُرجى رفض الطلبات الأقدم من خمس دقائق (هجمات إعادة الإرسال).webhook-signature:v1,<base64>. النص الموقَّع هو${webhook-id}.${webhook-timestamp}.${raw-body}، والخوارزمية HMAC-SHA256، والمفتاح هو بايتات قيمة base64 التي تلي البادئةwhsec_. وخلال 24 ساعة بعد تدوير السر، يصل توقيعان يفصل بينهما فراغ.
يُرجى التحقق من التوقيع على متن الطلب الخام قبل تحليل JSON:
import { createHmac, timingSafeEqual } from "node:crypto";
// secret: قيمة «whsec_…» التي يعرضها CoText مرة واحدة عند إضافة القناة
export function verify(rawBody, headers, secret) {
const id = headers["webhook-id"];
const ts = headers["webhook-timestamp"];
const sigs = headers["webhook-signature"] ?? "";
if (!id || !ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const expected = createHmac("sha256", key).update(`${id}.${ts}.${rawBody}`).digest();
// أثناء تدوير السر، قد تحمل الترويسة توقيعين يفصل بينهما فراغ: يكفي تطابق أحدهما.
return sigs.split(" ").some((s) => {
const [v, b64] = s.split(",");
const got = Buffer.from(b64 ?? "", "base64");
return v === "v1" && got.length === expected.length && timingSafeEqual(got, expected);
});
}متن الطلب
{
"type": "article.published", // article.updated · article.unpublished · ping
"timestamp": "2026-10-16T09:30:00.000Z",
"data": {
"article": {
"id": "7c0e…", // معرّف المحتوى في CoText: الإدراج أو التحديث وفقه
"title": "دليل تحضير القهوة",
"slug": "دليل-تحضير-القهوة",
"status": "published", // المصدر الوحيد للحقيقة: "published" | "draft"
"excerpt": "…", "meta_description": "…", "focus_keyword": "…",
"tags": ["قهوة"], "category": "دليل", "content_type": "guide",
"language": "ar", "language_name": "العربية", "translation_group_id": "7c0e…",
"translations": [{ "language": "tr", "url": "https://…", "article_id": "9d41…", "channel_kind": "custom" }],
"html": "<h2>…</h2><p>…</p>", "markdown": "## …",
"canonical_url": null, "author_name": null,
"featured_image": { "url": "https://…/kapak.jpg", "alt": "…", "width": 1600, "height": 900, "mime": "image/jpeg", "credit": null, "ai": false },
"published_at": "2026-10-16T09:30:00.000Z", "version_id": "…"
}
}
}- يكون الإدراج أو التحديث وفق
data.article.id(upsert)؛ أما حدثarticle.unpublishedفلا يحمل إلاid. - نوع الحدث مجرد إشارة؛ يُرجى التحقق من
data.article.statusلمعرفة حالة المنشور. ترتيب التسليم غير مضمون. - اللغات:
languageهو رمز لغة المحتوى (trوenوdeوfrوes؛ وnullإن لم يتعرّف CoText عليها)، وlanguage_nameهو الاسم المخزَّن. تشترك النسخ اللغوية للمحتوى نفسه فيtranslation_group_id(معرّف المصدر في المجموعة)؛ ويسردtranslationsالنسخ الأخرى المنشورة بالفعل مع عناوينها (قيمةarticle_idللمصدر تساويtranslation_group_id، ويمكنك استخدامها لـx-default). ومنه يمكنك بناء روابطhreflang. والنسخة التي تُنشر لاحقًا لا تعيد إرسال النسخ السابقة؛ أما واجهة القراءة (API) فتعرض القائمة الكاملة دائمًا. - صورة الغلاف:
featured_imageهو{ url, alt, width, height, mime, credit, ai }أوnull.urlعنوان عام دائم (اعرضه مباشرةً أو انسخ الملف). إذا لم يكنcreditمساويًا لـnull، فاعرضه مع الصورة؛ وتكون قيمةaiهيtrueفي الصور المُنشأة بالذكاء الاصطناعي. - البيانات المنظَّمة:
structured_dataقائمة بكائنات JSON-LD من schema.org: كائنBlogPosting، ومعهFAQPageإذا احتوى النص على قسم أسئلة شائعة (عنوان h2 تليه أسئلة h3 بإجابات في فقرات). ضع كل كائن في رأس الصفحة داخل<script type="application/ld+json">. - يُرجى إرجاع 2xx (ويمكن إرفاق
{ "url": "https://…" }للإبلاغ عن عنوان المنشور)، وإرجاع 401 إذا كان التوقيع غير صالح. الرمز 410 يعني «تمت إزالة نقطة النهاية» ولا تُعاد المحاولة بعده؛ أما 408 و429 و5xx فتُعاد المحاولة بعدها؛ ولا تُتبَع عمليات إعادة التوجيه.
واجهة القراءة (API)
ينشئ مالك مساحة العمل مفتاحًا من الإعدادات › مفاتيح API (ctx_live_…). لا يُعرض المفتاح إلا مرة واحدة عند إنشائه؛ ولا يخزّن CoText منه إلا قيمة مجزّأة (hash). يُرجى إرسال الترويسة Authorization: Bearer ctx_live_… مع كل طلب.
لا تعرض الواجهة إلا المحتوى المنشور على قناة موقعك الخاص، بالنسخة التي أُرسلت إلى تلك القناة: فالتعديلات التي تُجرى في CoText بعد النشر ولم تُعتمد وتُرسَل بعد لا تظهر، والمحتوى الذي أُلغي نشره من القناة يختفي من القائمة.
GET /api/v1/articles: القائمة. معاملات الاستعلام:limit(1–100، والافتراضي 20)، وcursor(قيمةnext_cursorمن الاستجابة السابقة)، وupdated_since(ISO 8601، شاملًا)، وorder(desc|asc، حسبupdated_at)، وchannel(معرّف القناة)، وlanguage(رمز لغة المحتوى:trوenوdeوfrوes).- يحمل كل عنصر
language(الرمز) وlanguage_nameوtranslation_group_idوtranslations: نسخه اللغوية الأخرى التي تقدّمها هذه الواجهة، بصيغة{ language, slug, url }، بصرف النظر عن عامل التصفيةchannelأوlanguage. ويمكنك استخدامها في روابطhreflangوفي مبدّل اللغة. featured_image: غلاف النسخة المنشورة، بالشكل نفسه الموجود في الـ webhook ({ url, alt, width, height, mime, credit, ai })، أوnull.structured_data: قائمة JSON-LD نفسها من schema.org الموجودة في الـ webhook (BlogPosting، ومعهFAQPageإذا وُجد قسم أسئلة شائعة).GET /api/v1/articles/{slug}: محتوى واحد؛ ويمكن أيضًا استخدام معرّف المحتوى بدلًا من slug.GET /api/v1/openapi.json: تعريف OpenAPI 3.1 (لا يلزم مفتاح).
curl https://cotext.net/api/v1/articles?limit=20 \
-H "Authorization: Bearer ctx_live_…"
{
"data": [
{
"id": "7c0e…", "title": "…", "slug": "…", "excerpt": "…", "meta_description": "…",
"tags": ["…"], "category": "…", "content_type": "blog",
"language": "ar", "language_name": "العربية", "translation_group_id": "7c0e…",
"translations": [{ "language": "tr", "slug": "…", "url": "https://…" }],
"html": "<p>…</p>", "markdown": "…",
"published_at": "…", "updated_at": "…", "version_id": "…", "canonical_url": null,
"featured_image": { "url": "https://…/kapak.jpg", "alt": "…", "width": 1600, "height": 900, "mime": "image/jpeg", "credit": null, "ai": false }
}
],
"next_cursor": "WyIyMDI2LTEwLTE2…",
"has_more": true
}للمزامنة، يُرجى استخدام order=asc&updated_since=<last sync> ومتابعة الصفحات حتى تصبح قيمة next_cursor فارغة. ولمعرفة المحتوى المحذوف أو الذي أُلغي نشره، يمكنك الاستماع إلى Webhook (article.unpublished) أو جلب القائمة الكاملة من حين لآخر.
MCP: اربط مساعد الذكاء الاصطناعي
CoText خادم MCP (Model Context Protocol، Streamable HTTP): يستطيع Claude وCursor وغيرهما من عملاء MCP تخطيط المحتوى وإنشاءه وتحريره واعتماده وجدولة نشره في مساحة عملك. يمر كل شيء عبر مسار CoText: صوت العلامة التجارية، والقواعد التحريرية المتعلَّمة، والرصيد، والنسخ، والاعتماد المرتبط بالنسخة.
ينشئ المالك مفتاحًا بصلاحية MCP: مساعد الذكاء الاصطناعي من الإعدادات > مفاتيح API (content:write). يعمل المساعد بصلاحيات المحرر باسم من أنشأ المفتاح؛ وإذا لم يعد مالكًا أو محررًا يتوقف المفتاح عن العمل. العنوان: https://cotext.net/api/mcp. في Claude Code:
claude mcp add --transport http cotext https://cotext.net/api/mcp \
--header "Authorization: Bearer ctx_live_…"- الأدوات:
workspace_overview،get_brand_voice،list_content،get_content،create_idea،plan_quick_pack،generate_articles،get_batch،update_content،run_ai_editor،submit_for_review،approve_content،request_changes،schedule_publish،cancel_publish. - يجري الإنشاء عبر قائمة الانتظار: تعيد
generate_articlesدفعة فورًا ويتابعها المساعد عبرget_batch. يتطلب الاعتماد ثلاثة تأكيدات والنسخة الحالية، ولا يُنشر إلا الإصدار المعتمد. - تظهر كل عملية كتابة في سجل النشاط باسم مساعد الذكاء الاصطناعي مع بادئة المفتاح. تُحفظ تعديلات المساعد على النص نسخةً منفصلة، ولا يتعلم CoText منها قواعد العلامة التجارية. ألغِ المفتاح لقطع الوصول فورًا.
- في claude.ai وChatGPT وغيرهما من التطبيقات التي تدعم موصِّلات MCP، أضف
https://cotext.net/api/mcpموصِّلًا: يرسلك التطبيق إلى CoText، فتختار مساحة عمل واحدة أو أكثر وتسمح بالوصول، ثم يستطيع المساعد العمل في كل علامة تجارية فيها (list_workspaces، brand_id) (OAuth 2.1 مع PKCE؛ لا مفتاح لنسخه). تظهر الاتصالات في الإعدادات > مفاتيح API ويمكن قطعها من هناك.
الأخطاء والحدود
- متن الخطأ:
{ "error": { "code": "…", "message": "…", "message_en": "…" } }. تأتيmessageبلغة الطلب (الترويسةAccept-Language: الإنجليزية أو التركية أو الألمانية أو الإسبانية أو الفرنسية أو العربية أو اليابانية؛ وفي غير ذلك بالإنجليزية)، أماmessage_enفبالإنجليزية دائمًا. الرموز:unauthorized،key_revoked،key_expired،insufficient_scope،api_access_disabled،invalid_parameter،not_found،rate_limited. - 600 طلب في الساعة لكل مفتاح. تحمل الاستجابات
X-RateLimit-LimitوX-RateLimit-RemainingوX-RateLimit-Reset؛ وعند تجاوز الحد تتلقى الرمز 429 معRetry-After. - تقبل نقاط النهاية طلبات
GETمن أي مصدر (CORS، بلا ملفات تعريف ارتباط). المفتاح الموجود في المتصفح مكشوف للجميع، لذا يُفضَّل استخدامه على الخادم متى أمكن؛ فهو يقرأ المحتوى المنشور فقط ولا يستطيع تغيير أي شيء. وإذا اشتبهت في تسرّبه، يُرجى إلغاؤه وإنشاء مفتاح جديد.