API Referansı

10 min readUpdated 27 Apr 2026

VeloCMS'in iki farklı API yüzü var: biri POCKETBASE_URL adresinizdeki standart PocketBase REST endpoint'lerini içeren PocketBase tenant API'si, diğeri ise VeloCMS'in kendi webhook API'si (/api/stripe/webhook ve /api/member-webhook/[tenantSlug]). Bu kılavuzda her ikisini de ele alacağız. Özellikle kimlik doğrulama modeline, webhook imzalamaya ve entegrasyon geliştirirken en çok işinizin düşeceği üç endpoint'e odaklanacağız.

Collection'lara Genel Bakış

CollectionAmacıKimlik Doğrulama Gerekli mi?
`_superusers`VeloCMS blog sahipleri (yöneticiler)Admin
`blog_members`Blog okurları (aboneler)Üye
`posts`Blog yazılarıYok (herkese açık yazılar için)
`media`Yüklenen görseller ve dosyalarAdmin
`site_settings`Tenant bazlı site yapılandırmasıAdmin
`stripe_products`Stripe ürün/fiyat eşleştirmesiAdmin
`stripe_customers`Stripe müşteri ID eşleştirmesiAdmin
`stripe_subscriptions`Stripe abonelik ID eşleştirmesiAdmin

Kimlik Doğrulama Modeli

VeloCMS'te birbiriyle asla karıştırılmaması gereken iki ayrı kimlik doğrulama (auth) sistemi bulunur (P-022). Yönetici kimlik doğrulaması blog sahipleri içindir; giriş bilgileri PocketBase'in dahili `_superusers` collection'ında tutulur ve oturum bilgisi bir `pb_auth` cookie'sinde saklanır. Üye (yani okur) kimlik doğrulaması ise aboneler içindir; bu bilgiler `blog_members` içinde saklanır ve oturum için `pb_member_auth` cookie'si kullanılır. Bir yönetici her şeyi okuyabilirken, bir üye sadece herkese açık yazıları ve kendi üyelik kaydını görebilir.

Authenticate as admin
# Get an admin auth token
    curl -X POST '$POCKETBASE_URL/api/admins/auth-with-password'       -H 'Content-Type: application/json'       -d '{"identity":"[email protected]","password":"yourpassword"}'
    
    # Response includes a token field — use it as a Bearer header:
    # Authorization: Bearer <token>
bash
Authenticate as member
# Member (reader) login — against blog_members collection
    curl -X POST '$POCKETBASE_URL/api/collections/blog_members/auth-with-password'       -H 'Content-Type: application/json'       -d '{"identity":"[email protected]","password":"memberpassword"}'
bash

Yazıları Listeleme

`posts` collection'ı, PocketBase'in filtreleme, sıralama ve sayfalama için kullandığı standart sorgu sözdizimini destekler. Herkese açık olarak yayınlanmış yazıları okumak için kimlik doğrulaması gerekmez:

List published posts
curl '$POCKETBASE_URL/api/collections/posts/records?filter=(status="published")&sort=-published_at&page=1&perPage=20'
bash

Yazı Oluşturma

Bir yazı oluşturmak için yönetici kimlik doğrulaması şart. `content_json` alanı TipTap'in JSON AST'sini tutarken, `content_html` ise gösterim için render edilmiş HTML'i barındırır. İkisini birden veya sadece birini ayarlayabilirsiniz, ama bir şeyi unutmayın: eğer sadece `content_html`'i ayarlarsanız, editör bu yazıyı düzenlemek için tekrar açamaz.

Create a post
curl -X POST '$POCKETBASE_URL/api/collections/posts/records'       -H 'Content-Type: application/json'       -H 'Authorization: Bearer <admin-token>'       -d '{
        "title": "My first post",
        "slug": "my-first-post",
        "content_html": "<p>Hello, world.</p>",
        "status": "draft",
        "visibility": "public",
        "tags": ["intro"],
        "tenant_id": "<your-tenant-id>"
      }'
bash

Medya Yükleme

Medya yüklemeleri, `media` collection'ına `multipart/form-data` olarak yapılır. VeloCMS backend'i dosyayı Cloudflare R2'de saklar ve herkese açık URL'i `url` alanına kaydeder:

Upload a file
curl -X POST '$POCKETBASE_URL/api/collections/media/records'       -H 'Authorization: Bearer <admin-token>'       -F 'file=@/path/to/image.jpg'       -F 'tenant_id=<your-tenant-id>'
bash

Webhook İmzalama — HMAC SHA-256

Her iki webhook endpoint'i de (`/api/stripe/webhook` ve `/api/member-webhook/[tenantSlug]`), gelen payload'ları HMAC-SHA256 imzalarıyla doğrular. Bu yöntem, "replay attack" olarak bilinen tekrar saldırılarını önler ve payload'un yolda değiştirilmediğinden emin olmamızı sağlar.

Stripe webhook'ları için imza, `Stripe-Signature` header'ında bulunur ve `STRIPE_WEBHOOK_SECRET`'ınız kullanılarak doğrulanır (aman dikkat, CLI'dan gelen değil, Stripe Dashboard'daki endpoint'ten aldığınız secret olmalı). `member-webhook` endpoint'i içinse imza `X-VeloCMS-Signature` header'ındadır ve tenant başına özel olarak `site_settings.member_webhook_secret` içinde saklanan HMAC secret'ını kullanır.

Verify member-webhook signature (example)
import { createHmac } from "crypto";
    
    function verifyMemberWebhook(
      rawBody: string,
      signature: string,
      secret: string
    ): boolean {
      const expected = createHmac("sha256", secret)
        .update(rawBody)
        .digest("hex");
      return `sha256=${expected}` === signature;
    }
typescript

Üye Webhook'u Payload'ı

`/api/member-webhook/[tenantSlug]` endpoint'i, bir okurun aboneliğinde değişiklik olduğunda Stripe'tan olayları (event) alır. Gelen payload'un yapısı olayın türüne göre değişiklik gösterir:

Olay TürüPayload AlanlarıGerçekleşen Eylem
`customer.subscription.created``id`, `customer`, `items`Yeni bir abonelik oluşturuldu.
`customer.subscription.updated``id`, `customer`, `items`Mevcut bir abonelik güncellendi (örneğin, plan değişikliği).
`customer.subscription.deleted``id`, `customer`Bir abonelik iptal edildi veya sona erdi.

HMAC ile Korunan Abonelikten Çıkma Linkleri

Bülten aboneliğinden çıkma linkleri, linkin VeloCMS tarafından belirli bir e-posta adresi için üretildiğini kanıtlayan bir HMAC token'ı içerir. Bu sayede bir abonenin başka bir abonenin aboneliğini sonlandırması engellenmiş olur. Token sunucu tarafında oluşturulur ve `/member/unsubscribe` rotasında doğrulanır:

Generate unsubscribe token (server-side)
import { createHmac } from "crypto";
    
    function generateUnsubscribeToken(email: string, secret: string): string {
      return createHmac("sha256", secret).update(email).digest("hex");
    }
    
    // Link format: /member/unsubscribe?email=<encoded>&token=<hmac>
typescript

Referans

Collection'lara Genel Bakış

CollectionAnahtar AlanlarKimlik DoğrulamaVarsayılan Listeleme Kuralı
`_superusers``email`, `password`AdminSadece Admin
`blog_members``email`, `stripe_customer_id`Üye`@request.auth.id = id`
`posts``slug`, `title`, `published_at`, `visibility`Yok (herkese açık)`visibility = "public" && published_at != null`
`media``file`, `url`AdminSadece Admin
`site_settings``slug`, `custom_domain`AdminSadece Admin

PocketBase REST Endpoint Kalıpları

İşlemMetotURL KalıbıKimlik Doğrulama
Kayıtları listeleGET`/api/collections/[collection]/records`Değişken
Kaydı görüntüleGET`/api/collections/[collection]/records/[id]`Değişken
Kayıt oluşturPOST`/api/collections/[collection]/records`Değişken
Kaydı güncellePATCH`/api/collections/[collection]/records/[id]`Değişken
Kaydı silDELETE`/api/collections/[collection]/records/[id]`Değişken

Filtreleme Sözdizimi

PocketBase filter examples
# Simple equality
    ?filter=(status="published")
    
    # Combined with AND
    ?filter=(status="published" && visibility="public")
    
    # Date range
    ?filter=(published_at >= "2026-01-01" && published_at < "2026-02-01")
    
    # Relation field (tenant isolation)
    ?filter=(tenant_id.owner_id = "{userId}")
    
    # Full-text search (requires @search index on field)
    ?filter=(title ~ "velocms")
    
    # Multiple values (OR)
    ?filter=(status="published" || status="draft")
bash

Sayfalama Parametreleri

ParametreTürVarsayılanAçıklama
`page`integer`1`Döndürülecek sayfa numarası
`perPage`integer`30`Sayfa başına kayıt sayısı
`sort`string`created` (azalan)Sıralama düzeni (`-` ön eki azalan sıralama içindir)
`filter`string(yok)PocketBase filtre ifadesi
`expand`string(yok)Genişletilecek ilişkilerin virgülle ayrılmış listesi

API Hata Kodları

HTTP KoduPB Hata KoduAnlamıYaygın Neden
400`validation_invalid_data`Geçersiz istek gövdesi veya sorgu parametreleriZorunlu bir alan eksik veya yanlış türde veri gönderildi.
401`unauthorized`Eksik veya geçersiz kimlik doğrulama token'ıİstekle birlikte geçerli bir `pb_auth` veya `pb_member_auth` cookie'si gönderilmedi.
403`forbidden`Kimlik doğrulama token'ı bu işlem için gerekli izne sahip değilBir üye, admin yetkisi gerektiren bir endpoint'e erişmeye çalışıyor.
404`not_found`Kayıt veya collection bulunamadıİstek yapılan ID veya collection adı mevcut değil.

Curl Örnekleri — posts Collection'ı

List published posts (no auth)
curl -G '$POCKETBASE_URL/api/collections/posts/records'       --data-urlencode 'filter=(status="published" && visibility="public")'       --data-urlencode 'sort=-published_at'       --data-urlencode 'page=1'       --data-urlencode 'perPage=20'       --data-urlencode 'fields=id,title,slug,excerpt,published_at,tags'
bash
Get one post (no auth)
curl '$POCKETBASE_URL/api/collections/posts/records/{id}'
bash
Update post status (admin auth required)
curl -X PATCH '$POCKETBASE_URL/api/collections/posts/records/{id}'       -H 'Content-Type: application/json'       -H 'Authorization: Bearer <admin-token>'       -d '{"status":"published","published_at":"2026-04-27T12:00:00Z"}'
bash
Create blog_members record (admin auth)
curl -X POST '$POCKETBASE_URL/api/collections/blog_members/records'       -H 'Content-Type: application/json'       -H 'Authorization: Bearer <admin-token>'       -d '{
        "email": "[email protected]",
        "name": "Jane Reader",
        "tier": "free",
        "newsletter": true,
        "tenant_id": "<tenant-id>"
      }'
bash

VeloCMS Webhook Endpoint'leri

EndpointMetotİmza Header'ıAmacı
`/api/stripe/webhook`POST`Stripe-Signature`Stripe'tan abonelik olaylarını alır
`/api/member-webhook/[tenantSlug]`POST`X-VeloCMS-Signature`İşlenmiş Stripe olaylarını sizin kendi backend'inize yönlendirir