Geliştiriciler
İçeriğini kendi sitene al
CoText'te onaylanıp Kendi web siten kanalına yayınlanan içerik iki yoldan sitene ulaşır: CoText yayınladığında sitene imzalı bir webhook gönderir, ya da siten içeriği okuma API'sinden kendisi çeker. İkisini birlikte de kullanabilirsin. Zapier, Make ve n8n da aynı yolları kullanır.
Webhook ve imza doğrulama
Kanalı eklerken sitenin webhook adresini (yalnız https) yazarsın; CoText bir imza sırrı (whsec_…) üretir ve onu yalnız bir kez gösterir. Her istek Standard Webhooks biçiminde imzalanır; hazır kütüphaneleri (Node, Python, PHP, Go …) doğrudan kullanabilirsin.
webhook-id: olay kimliği. Yeniden denemelerde aynı kalır; daha önce işlediğin kimliği tekrar işleme.webhook-timestamp: Unix saniye. 5 dakikadan eski istekleri reddet (tekrar saldırısı).webhook-signature:v1,<base64>. İmzalanan metin${webhook-id}.${webhook-timestamp}.${ham-gövde}, algoritma HMAC-SHA256, anahtarwhsec_önekinden sonraki base64 değerin baytları. Sır yenilenince 24 saat boyunca boşlukla ayrılmış iki imza gelir.
İmzayı ham gövde üzerinde, JSON'u ayrıştırmadan önce doğrula:
import { createHmac, timingSafeEqual } from "node:crypto";
// secret: CoText'in kanal kaydında bir kez gösterdiği "whsec_…" değeri
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();
// Sır yenilenirken başlıkta boşlukla ayrılmış iki imza olabilir: biri tutarsa yeterli.
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);
});
}Gövde
{
"type": "article.published", // article.updated · article.unpublished · ping
"timestamp": "2026-10-16T09:30:00.000Z",
"data": {
"article": {
"id": "7c0e…", // CoText içerik kimliği: buna göre ekle/güncelle
"title": "Kahve demleme rehberi",
"slug": "kahve-demleme-rehberi",
"status": "published", // tek doğru kaynak: "published" | "draft"
"excerpt": "…", "meta_description": "…", "focus_keyword": "…",
"tags": ["kahve"], "category": "Rehber", "content_type": "guide",
"language": "tr", "language_name": "Türkçe", "translation_group_id": "7c0e…",
"translations": [{ "language": "en", "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.idile ekle ya da güncelle (upsert);article.unpublishedolayında yalnızidgelir.- Olay türü ipucudur; yazının durumu için
data.article.status'a bak. Teslim sırası garanti değildir. - Diller:
languageiçerik dilinin kodudur (tr,en,de,fr,es; CoText tanımıyorsanull),language_namekayıtlı adıdır. Aynı içeriğin dil sürümleri aynıtranslation_group_id'yi taşır (grubun kaynağının kimliği);translationsyayında olan diğer sürümleri adresleriyle listeler (kaynağınarticle_id'sitranslation_group_id'ye eşittir,x-defaultiçin onu kullan).hreflangbağlantılarını buradan kur. Sonradan yayınlanan sürüm öncekileri yeniden göndermez; tam liste her zaman okuma API'sinde. - Kapak görseli:
featured_imagealanı{ url, alt, width, height, mime, credit, ai }ya danull.urlkalıcı, herkese açık bir adrestir (doğrudan göster ya da dosyayı kopyala).creditnulldeğilse görselle birlikte göster; AI ile üretilen görseldeaitrueolur. - Yapılandırılmış veri:
structured_data, schema.org JSON-LD nesnelerinin listesi: birBlogPosting, metinde SSS bölümü varsa (h2 başlığı, altında soru biçiminde h3'ler ve paragraf cevaplar) bir deFAQPage. Her birini sayfanın head bölümüne<script type="application/ld+json">içinde koy. - 2xx dön (isteğe bağlı
{ "url": "https://…" }ile yazının adresini bildir). İmza geçersizse 401 dön. 410 "uç kaldırıldı" demektir ve yeniden denenmez; 408, 429 ve 5xx yeniden denenir; yönlendirmeler izlenmez.
Okuma API'si
Çalışma alanı sahibi Ayarlar > API anahtarları'ndan bir anahtar oluşturur (ctx_live_…). Anahtar yalnız oluşturulduğunda bir kez gösterilir; CoText yalnız özetini saklar. Her istekte Authorization: Bearer ctx_live_… başlığını gönder.
API yalnız Kendi web siten kanalında yayında olan içeriği, o kanala gönderilen sürümüyle verir: yayından sonra CoText'te yapılan ama henüz onaylanıp gönderilmemiş düzenlemeler görünmez; kanalda yayından kaldırılan içerik listeden çıkar.
GET /api/v1/articles: liste. Sorgu:limit(1–100, varsayılan 20),cursor(bir önceki yanıtınnext_cursor'ı),updated_since(ISO 8601, dahil),order(desc|asc,updated_at'e göre),channel(kanal kimliği),language(içerik dili kodu:tr,en,de,fr,es).- Her içerikte
language(kod),language_name,translation_group_idvetranslationsvar: bu API'nin verdiği diğer dil sürümleri,{ language, slug, url }olarak —channelya dalanguagesüzgecinden bağımsız.hreflangbağlantıları ve dil seçici için kullan. featured_image: yayınlanan sürümün kapağı, webhook'takiyle aynı biçimde ({ url, alt, width, height, mime, credit, ai }) ya danull.structured_data: webhook'takiyle aynı schema.org JSON-LD listesi (BlogPosting, SSS bölümü varsaFAQPage).GET /api/v1/articles/{slug}: tek içerik; slug yerine içerik kimliği de olur.GET /api/v1/openapi.json: OpenAPI 3.1 tanımı (anahtar gerekmez).
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": "tr", "language_name": "Türkçe", "translation_group_id": "7c0e…",
"translations": [{ "language": "en", "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
}Eşitleme için order=asc&updated_since=<son eşitleme> kullanıp next_cursor boşalana kadar sayfaları izle. Silinen ya da yayından kaldırılan içeriği öğrenmek için webhook'u (article.unpublished) dinle ya da arada bir tam liste al.
MCP: AI asistanını bağla
CoText bir MCP sunucusudur (Model Context Protocol, Streamable HTTP): Claude, Cursor ve diğer MCP istemcileri çalışma alanında içerik planlar, üretir, düzenler, onaylar ve yayın planlar. Her şey CoText'in akışından geçer: marka sesi, öğrenilen editoryal kurallar, krediler, sürümler ve sürüme bağlı onay.
Sahip, Ayarlar > API anahtarları'ndan MCP: AI asistanı erişimiyle bir anahtar açar (content:write). Asistan, anahtarı açan kişinin adıyla editör yetkisiyle çalışır; o kişi artık sahip ya da editör değilse anahtar çalışmaz. Adres: https://cotext.net/api/mcp. Claude Code'da:
claude mcp add --transport http cotext https://cotext.net/api/mcp \
--header "Authorization: Bearer ctx_live_…"- Araçlar:
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. - Üretim kuyrukta:
generate_articleshemen bir toplu iş döner, asistanget_batchile izler. Onay üç beyan ve güncel revizyon ister; yayın yalnız onaylı sürümü gönderir. - Her yazma işlemi etkinlik kaydında anahtarın ön ekiyle AI asistanı olarak görünür. Asistanın metin düzenlemeleri ayrı bir sürüm olarak kaydedilir ve CoText bunlardan marka kuralı öğrenmez. Erişimi hemen kesmek için anahtarı iptal et.
- claude.ai, ChatGPT ve MCP bağlayıcısını destekleyen diğer uygulamalarda
https://cotext.net/api/mcpadresini bağlayıcı olarak ekle: uygulama seni CoText'e yönlendirir, bir ya da birden çok çalışma alanı seçip izin verirsin — asistan sonra her birinin markalarında çalışabilir (list_workspaces, brand_id) (PKCE'li OAuth 2.1; kopyalanacak anahtar yok). Bağlantılar Ayarlar > API anahtarları'nda görünür ve oradan kesilir.
Hatalar ve sınırlar
- Hata gövdesi:
{ "error": { "code": "…", "message": "…", "message_en": "…" } }.messageisteğin dilindedir (Accept-Languagebaşlığı: İngilizce, Türkçe, Almanca, İspanyolca, Fransızca, Arapça ya da Japonca; diğerlerinde İngilizce);message_enher zaman İngilizcedir. Kodlar:unauthorized,key_revoked,key_expired,insufficient_scope,api_access_disabled,invalid_parameter,not_found,rate_limited. - Anahtar başına saatte 600 istek. Yanıtlarda
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset; sınır aşılınca 429 veRetry-After. - Uçlar her kökenden
GETisteğine açıktır (CORS, çerezsiz). Anahtar tarayıcıda görünür olacağından mümkünse sunucu tarafında kullan; yalnız yayındaki içeriği okur, hiçbir şeyi değiştiremez. Sızdığını düşünürsen iptal edip yenisini oluştur.