Herkese Açık REST API

12 min readUpdated 26 Apr 2026

VeloCMS, Pro ve daha üstü planlarda kullanabileceğiniz, `/api/v1` altında versiyonlanmış bir REST API ile geliyor. Bu API'nin amacı ne mi? Yönetici arayüzüyle uğraşmadan, doğrudan kod üzerinden veri aktarmak, mobil uygulamalarla konuşmak veya üçüncü parti servislerle entegrasyon kurmak isteyenler için biçilmiş kaftan. Her bir istek, yalnızca izin verilen işlemleri yapabilen, size özel bir API anahtarıyla doğrulanıyor.

Plan Gereksinimleri

PlanAPI ErişimiGünlük İstek Limiti
FreeYok0
ProVar10.000
BusinessVar100.000
EnterpriseVarÖzel

Kimlik Doğrulama

API anahtarlarınızı `Admin → Settings → API Keys` yolunu izleyerek oluşturabilirsiniz. Her anahtarın bir ismi ve belirli yetki alanları (scope'ları) bulunur ve size sadece bir defalığına gösterilir. Unutmayın, VeloCMS bu anahtarın kendisini değil, yalnızca SHA-256 özetini saklar. Yani anahtarı kaybederseniz, yapacak tek bir şey var: yenisini oluşturmak. Anahtarı `Authorization` başlığı içinde şu şekilde göndermeniz gerekiyor:

Authenticate with API key
curl https://yourblog.velocms.org/api/v1/posts       -H "Authorization: Bearer velo_<your-key-here>"
bash

Kapsamlar

Her API anahtarının yetkileri, oluşturma sırasında sizin belirlediğiniz kapsamlarla sınırlıdır. Örneğin, `posts:read` kapsamına sahip bir anahtar yazıları listeleyebilir ve okuyabilir, ama asla yeni bir yazı oluşturamaz veya mevcut olanı değiştiremez. Anahtarınızın yetkisi olmayan bir endpoint'e istek atarsanız, `INVALID_SCOPE` mesajıyla birlikte bir HTTP 403 hatası alırsınız.

KapsamVerdiği Yetkiler
`posts:read`Tüm yazılara okuma erişimi
`posts:write`Yazı oluşturma, güncelleme ve silme
`uploads:read`Tüm yüklemelere okuma erişimi
`uploads:write`Yükleme yapma ve silme
`settings:read`Tenant ayarlarına okuma erişimi
`settings:write`Tenant ayarlarını güncelleme

Hız Limitleri

API, her bir anahtar için dakikada 100 istek limiti uygular ve bunu kayan bir pencere sistemiyle takip eder. Limite takıldığınızda, HTTP 429 durum koduyla birlikte ne kadar beklemeniz gerektiğini saniye cinsinden belirten bir `Retry-After` başlığı alırsınız. Bu anlık limitin yanı sıra, bir de planınıza bağlı günlük kotalar var (Pro için günde 10 bin, Business için 100 bin). Günlük kotanızı aşarsanız da yine 429 hatası alırsınız, ama bu sefer mesaj `QUOTA_EXCEEDED` olur.

BaşlıkAçıklama
`X-RateLimit-Limit`Mevcut zaman aralığı için hız limiti tavanı.
`X-RateLimit-Remaining`Mevcut zaman aralığında kalan istek sayısı.
`X-RateLimit-Reset`Mevcut zaman aralığının sıfırlanacağı UTC zaman damgası (saniye cinsinden).

Hata Formatı

Tüm hata yanıtları aynı JSON yapısını kullanır. Bu sayede istemci tarafında hataları yönetmek oldukça kolaylaşır:

Error response
{
      "error": {
        "code": "INVALID_SCOPE",
        "message": "This endpoint requires the 'posts:write' scope. Your key has: posts:read.",
        "details": {}
      }
    }
json
KodHTTP durumuAnlamı
`BAD_REQUEST`400İstek hatalı biçimlendirilmiş.
`UNAUTHORIZED`401API anahtarı eksik veya geçersiz.
`INVALID_SCOPE`403API anahtarı gerekli kapsama sahip değil.
`NOT_FOUND`404İstenen kaynak mevcut değil.
`RATE_LIMIT_EXCEEDED`429Dakikalık hız limitini aştınız.
`QUOTA_EXCEEDED`429Planınızın günlük kotasını aştınız.
`INTERNAL_SERVER_ERROR`500Bizim tarafta bir şeyler ters gitti.

Endpoint Referansı

MetotYolKapsamAçıklama
GET`/posts``posts:read`Tüm yazıları listeler
POST`/posts``posts:write`Yeni bir yazı oluşturur
GET`/posts/:id``posts:read`Tek bir yazıyı getirir
PUT`/posts/:id``posts:write`Bir yazıyı günceller
DELETE`/posts/:id``posts:write`Bir yazıyı siler

Sayfalama

Liste döndüren tüm endpoint'ler `page` ve `per_page` sorgu parametrelerini destekler. `per_page` için belirtebileceğiniz en yüksek değer 100'dür. Gelen yanıtlarda ise `items`, `page`, `per_page`, `total` ve `total_pages` alanlarını bulabilirsiniz.

Paginate through posts
# Page 2, 10 per page, published only
    curl "https://yourblog.velocms.org/api/v1/posts?page=2&per_page=10&status=published"       -H "Authorization: Bearer velo_<key>"
bash

Webhook Abonelikleri

Sürekli API'yi yoklamak yerine, tenant'ınızda bir olay gerçekleştiğinde HTTP POST bildirimi almak için bir webhook URL'i kaydedebilirsiniz. Bunun için `Admin → Settings → Webhooks` ekranına gidin. Bir HTTPS URL'i girin, hangi olayları dinlemek istediğinizi seçin ve kaydedin. VeloCMS size özel, 32-byte'lık rastgele bir 'secret' (gizli anahtar) oluşturacak ve bunu sadece bir kez gösterecek. Bu anahtarı mutlaka saklayın, çünkü gelen bildirimlerin gerçekten VeloCMS'ten geldiğini doğrulamak için ona ihtiyacınız olacak.

OlayTetiklendiği durum
`post.created`Yeni bir yazı oluşturulduğunda.
`post.updated`Mevcut bir yazı güncellendiğinde.
`post.deleted`Bir yazı silindiğinde.

Webhook Payload Formatı

post.published payload
{
      "event": "post.published",
      "timestamp": 1714140000,
      "payload": {
        "post_id": "abc123",
        "title": "My first post",
        "slug": "my-first-post",
        "published_at": "2026-04-26T10:00:00.000Z",
        "tenant_id": "tenant_xyz"
      }
    }
json

HMAC İmzasını Doğrulama

Her webhook teslimatı, `v1=<hex>` formatında bir değer içeren `X-VeloCMS-Signature` başlığıyla gelir. Bu imza, `v1:<timestamp>:<raw-body>` metninin HMAC-SHA256 ile özetlenmiş halidir. Buradaki `timestamp` değeri, `X-VeloCMS-Timestamp` başlığından gelir. Gelen payload'u işlemeden önce bu imzayı mutlaka doğrulayın. Bu sizi 'replay' ve 'spoofing' gibi saldırılardan koruyacaktır.

Verify HMAC signature (TypeScript)
import { createHmac, timingSafeEqual } from "node:crypto";
    
    function verifyWebhookSignature(
      rawBody: string,
      signature: string,  // X-VeloCMS-Signature header value
      timestamp: string,  // X-VeloCMS-Timestamp header value
      secret: string      // Your webhook secret from the admin UI
    ): boolean {
      const signingInput = `v1:${timestamp}:${rawBody}`;
      const expectedSig = createHmac("sha256", secret)
        .update(signingInput)
        .digest("hex");
    
      const expectedHeader = `v1=${expectedSig}`;
    
      // Use constant-time comparison to prevent timing attacks
      const a = Buffer.from(signature);
      const b = Buffer.from(expectedHeader);
      if (a.length !== b.length) return false;
      return timingSafeEqual(a, b);
    }
typescript

Yeniden Deneme Davranışı

VeloCMS, başarısız olan webhook teslimatlarını üssel olarak artan bir gecikmeyle (1s, 2s, 4s) 3 kez daha dener. Bir teslimatın başarılı sayılması için endpoint'inizin 10 saniye içinde 2xx ile başlayan bir durum kodu dönmesi gerekir. Üst üste 5 kez hata alınırsa abonelik otomatik olarak devre dışı bırakılır ve yönetici ayarlarında bununla ilgili bir bildirim görürsünüz. Sorunu çözdükten sonra Webhooks panelinden aboneliği tekrar aktif hale getirebilirsiniz.

Örnek: Harici bir CMS'ten Yazıları İçe Aktarma

Bulk import script
const API_KEY = process.env.VELOCMS_API_KEY;
    const BASE = "https://yourblog.velocms.org/api/v1";
    
    interface ExternalPost {
      title: string;
      body_html: string;
      tags: string[];
    }
    
    async function importPost(p: ExternalPost): Promise<void> {
      const res = await fetch(`${BASE}/posts`, {
        method: "POST",
        headers: {
          "Authorization": `Bearer ${API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          title: p.title,
          content_html: p.body_html,
          tags: p.tags,
          status: "draft",
        }),
      });
    
      if (!res.ok) {
        const err = await res.json();
        throw new Error(`Import failed: ${err.error.message}`);
      }
    
      const post = await res.json();
      console.log(`Imported: ${post.id} — ${post.title}`);
    }
typescript