==============================================================================
RABISU BAYI (RESELLER) API — TAM DOKUMANTASYON
==============================================================================
Bu dosya Rabisu Bayi API dokumantasyonunun tamamini tek bir duz metin dosyasinda toplar.
Bir yapay zeka asistanina (ChatGPT, Claude vb.) yukleyip entegrasyon sorularinizi sorabilirsiniz.
Web surumu: https://www.rabisu.com/tr/docs/bayi-api/giris
API surumu: 1.0.0
Uretim tarihi: 2026-10-06
Production temel URL: https://api.rabisu.com/api/v1
ICINDEKILER
BOLUM 1 — REHBERLER
- Rabisu Bayi API — Genel Bakış
- Kimlik Doğrulama
- Idempotency (Tekrar Güvenli İstekler)
- Hız Sınırlama (Rate Limit)
- Hata Kodları
- Sipariş Yaşam Döngüsü
- Webhooks
BOLUM 2 — API REFERANSI (ENDPOINT'LER) — 36 endpoint
[System]
GET /health — Saglik kontrolu (auth gerektirmez)
GET /openapi.yaml — OpenAPI 3.0 spec'inin ham YAML icerigi
[Auth]
GET /auth/me — Authenticated key meta bilgisini doner
GET /auth/balance — Bayi WHMCS kredi bakiyesi (APCu 60s cache)
GET /auth/balance/movements — Bayi bakiye hareketleri (canli WHMCS tblcredit)
[Products]
GET /products — Urun katalogu listesi (+ stok aktiflik flag'i)
GET /products/{id} — Tek urun detayi
[Orders]
GET /orders — Bayinin siparis listesi (provision kayitlari)
POST /orders — Yeni siparis olustur + bakiyeden OTOMATIK odeme dene
GET /orders/{id} — Tek siparis detayi (bayi izolasyonu zorunlu)
POST /orders/{id}/settle — Bekleyen odemeyi krediyle tahsil et (InvoicePaid)
[Services]
GET /services — Bayinin hizmetleri — toplu liste (imlec veya sayfa + arama)
GET /services/{id} — VPS hizmet detayi (WHMCS + Virtualizor birlesimi)
DELETE /services/{id} — Servisi WHMCS modul hook'u ile terminate et (asenkron)
GET /services/{id}/status — VPS anlik metrikleri (CPU/RAM/disk/net)
GET /services/{id}/credentials — Erisim bilgileri (username/password/ip/hostname/panel_url)
GET /services/{id}/os-templates — Bu VPS icin rebuild'e izinli OS sablonlari (servis-spesifik filtre)
POST /services/{id}/power/{action} — VPS power islemi (start/stop/restart/poweroff)
POST /services/{id}/rebuild — VPS OS reinstall (asenkron, 202 Accepted)
POST /services/{id}/vnc-token — Kisa omurlu (60s) NoVNC baglanti bilgisi
POST /services/{id}/password — VPS root parolasini degistir
POST /services/{id}/suspend — Servisi WHMCS modul hook'u ile suspend et
POST /services/{id}/unsuspend — Servisin suspend'ini WHMCS modul hook'u ile geri al
POST /services/{id}/upgrade — Servisin config option'larini yukselt (upgrade)
[Webhooks]
GET /webhooks — Bayinin webhook kayitlarini listele
POST /webhooks — Yeni webhook endpoint kaydet
GET /webhooks/{id} — Tek webhook detayi
PATCH /webhooks/{id} — Webhook kismi guncelle (reaktive / event_types / aciklama)
DELETE /webhooks/{id} — Webhook sil (soft delete)
[ModuleReleases]
GET /module-releases/{module}/latest — Modulun en yeni gecerli surumu
GET /module-releases/{module}/{version}/download — Imzali modul zip'ini indir
[Admin]
GET /admin/keys — Bir bayinin aktif API key'lerini listele (secret YOK)
POST /admin/keys — Bayi icin yeni API key uret (secret TEK SEFER doner)
POST /admin/keys/{id}/webhook-secret — Eski anahtara webhook secret ver (yoksa, tek sefer)
POST /admin/keys/{id}/rotate — Bayi API key secret'ini yenile (secret TEK SEFER doner)
POST /admin/keys/{id}/revoke — Bayi API key'ini iptal et (soft delete)
BOLUM 3 — VERI SEMALARI (components/schemas)
==============================================================================
BOLUM 1 — REHBERLER
==============================================================================
# Rabisu Bayi API — Genel Bakış
Rabisu Bayi API, bayilerin kendi WHMCS kurulumları üzerinden Rabisu Virtualizor VPS
ürünlerini satabilmesi için tasarlanmış bir REST API'dir. Sipariş oluşturma, servis
yaşam döngüsü yönetimi (askıya alma, iptal, yeniden kurulum), kaynak yükseltme,
kredi bakiyesi sorgulama ve webhook bildirimleri sunar.
:::tip[Tüm dokümantasyon tek dosyada (AI için)]
Sayfaları tek tek gezmek yerine API dokümantasyonunun tamamını (rehberler, tüm
endpoint'ler, parametreler, yanıtlar, örnekler ve veri şemaları) tek bir metin
dosyası olarak indirebilirsiniz. Dosyayı ChatGPT, Claude gibi bir yapay zeka
asistanına yükleyip entegrasyon sorularınızı doğrudan sorabilirsiniz.
📄 Türkçe dokümanı indir (.txt) · English version (.txt)
:::
## Temel Bilgiler
| Özellik | Değer |
|---|---|
| **Production temel URL** | `https://api.rabisu.com/api/v1` |
| **Protokol** | HTTPS zorunlu (TLS) |
| **Kimlik doğrulama** | HTTP Basic Auth (API Key + Secret) |
| **İçerik tipi** | `application/json` |
| **Sürüm** | Tüm yollar `/api/v1` altında |
## İstek Formatı
Tüm istek gövdeleri JSON olmalıdır. Kimlik doğrulama her istekte HTTP Basic Auth
başlığı ile gönderilir (bkz. Kimlik Doğrulama).
```bash
curl -u "rsk_live_xxx:rsks_yyy" \
https://api.rabisu.com/api/v1/auth/me
```
## Standart Yanıt Zarfı
Tüm yanıtlar aynı zarf yapısını kullanır. Başarılı yanıt:
```json
{
"success": true,
"data": {
"reseller_id": 42,
"name": "Örnek Bayi"
},
"meta": {
"trace_id": "1f9a...uuid",
"timestamp": "2026-07-22T12:00:00Z"
}
}
```
Hata yanıtı:
```json
{
"success": false,
"error": {
"code": "insufficient_credit",
"message": "Kredi bakiyesi yetersiz.",
"details": { "required": "10.00", "available": "3.50" }
},
"meta": {
"trace_id": "1f9a...uuid",
"timestamp": "2026-07-22T12:00:00Z"
}
}
```
- **`success`**: İşlemin sonucu (`true` / `false`).
- **`data`**: Yalnızca başarılı yanıtlarda bulunur.
- **`error`**: Yalnızca hatalı yanıtlarda bulunur — `code` (İngilizce slug),
`message` (insan-okunur) ve opsiyonel `details`.
- **`meta.trace_id`**: Her istek için benzersiz izleme kimliği. Destek talebi
açarken bu değeri paylaşın.
- **`meta.timestamp`**: ISO 8601 UTC zaman damgası.
## İzleme Kimliği (Trace ID)
Her yanıtta `X-Request-ID` başlığı ve `meta.trace_id` alanı döner. İsteğinize kendi
`X-Request-ID` başlığınızı eklerseniz sistem onu korur; eklemezseniz otomatik bir
UUID üretilir. Sorun giderirken bu kimliği loglarınızda saklayın.
## Sonraki Adımlar
- Kimlik Doğrulama — HTTP Basic Auth ile API Key/Secret kullanımı
- Idempotency — Tekrar güvenli istekler
- Hız Sınırlama (Rate Limit) — Kota başlıkları
- Hata Kodları — Tüm hata slug'ları
- Sipariş Yaşam Döngüsü — POST /orders ve provizyon akışı
- Webhooks — Olay bildirimleri ve imza doğrulama
------------------------------------------------------------------------------
# Kimlik Doğrulama
Rabisu Bayi API **HTTP Basic Authentication** kullanır. Her istekte API Key
(kullanıcı adı) ve API Secret (şifre) gönderilir. JWT veya oturum (session)
kullanılmaz — API tamamen durumsuzdur (stateless).
## Kimlik Bilgileri
Bayi olduğunuzda size iki değer verilir:
| Alan | Örnek | Rol |
|---|---|---|
| **API Key** | `rsk_live_ab55mtkb7azdr1pyh5xs56whcab4pjv9` | Kullanıcı adı |
| **API Secret** | `rsks_zfe4yk848aa737k3s1q1h3mmnd963jk6651fcc3` | Şifre |
> **Önemli:** API Secret **yalnızca bir kez** gösterilir. Güvenli bir yerde
> saklayın. Kaybederseniz secret'ı yenilemeniz (rotate) veya yeni anahtar
> oluşturmanız gerekir.
## İstek Örneği
Basic Auth başlığı `Authorization: Basic base64(api_key:api_secret)` biçimindedir.
Çoğu HTTP istemcisi bunu sizin için üretir:
```bash
curl -u "rsk_live_ab55mtkb7azdr1pyh5xs56whcab4pjv9:rsks_zfe4yk848..." \
https://api.rabisu.com/api/v1/auth/me
```
PHP (cURL) örneği:
```php
$ch = curl_init('https://api.rabisu.com/api/v1/auth/me');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, $apiKey . ':' . $apiSecret);
$response = curl_exec($ch);
```
## Başarısız Kimlik Doğrulama
Kimlik bilgileri eksik veya hatalıysa **401 Unauthorized** döner. Güvenlik gereği
tüm hata durumları aynı genel mesajı verir (hangi alanın yanlış olduğu sızdırılmaz):
```json
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik doğrulama başarısız."
},
"meta": { "trace_id": "...", "timestamp": "..." }
}
```
401 yanıtlarında RFC 7235 uyarınca şu başlık da döner:
```
WWW-Authenticate: Basic realm="Rabisu Reseller API", charset="UTF-8"
```
## Güvenlik Notları
- API Secret'ı **asla** istemci tarafı (tarayıcı) kodunda kullanmayın. API yalnızca
sunucudan-sunucuya (server-to-server) çağrılar için tasarlanmıştır.
- `Authorization` başlığını **hiçbir loga** düz metin yazmayın.
- Secret sızması durumunda anahtarı derhal yenileyin (rotate) veya iptal edin (revoke).
- IP whitelist tanımlıysa yalnızca izin verilen IP'lerden erişebilirsiniz.
## Ana Uç Nokta: `GET /auth/me`
Kimlik bilgilerinizi doğrulamak için en basit uç nokta budur. Anahtarın sahibi,
izinleri ve `auto_apply_credit` bayrağını döner:
```json
{
"success": true,
"data": {
"reseller_id": 42,
"name": "Örnek Bayi",
"permissions": ["*"],
"auto_apply_credit": true,
"last_used_ip": "203.0.113.5"
},
"meta": { "trace_id": "...", "timestamp": "..." }
}
```
------------------------------------------------------------------------------
# Idempotency (Tekrar Güvenli İstekler)
Ağ hataları veya zaman aşımları nedeniyle bir isteği tekrar göndermeniz gerekebilir.
Idempotency, aynı isteğin yanlışlıkla iki kez işlenmesini (örneğin çift sipariş,
çift ücretlendirme) önler.
## Nasıl Çalışır?
Mutasyon içeren isteklere (`POST`, `DELETE`) `Idempotency-Key` başlığı ekleyin.
Bu başlık **UUID v4** biçiminde benzersiz bir değer olmalıdır:
```bash
curl -X POST https://api.rabisu.com/api/v1/orders \
-u "rsk_live_xxx:rsks_yyy" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9f1c2d3e-4b5a-6c7d-8e9f-0a1b2c3d4e5f" \
-d '{ "product_id": 101, "period": "monthly" }'
```
Davranış:
| Durum | Sonuç |
|---|---|
| **Key yok** | Normal işlenir (idempotency korumasız). |
| **Key var, aynı gövde** | 24 saat içindeki ilk yanıt cache'ten aynen döner — işlem tekrar yürütülmez. |
| **Key var, farklı gövde** | `422 idempotency_key_reuse` hatası — aynı anahtar farklı içerikle kullanılamaz. |
## Zorunlu Olduğu Uç Noktalar
Bazı finansal / yıkıcı uç noktalarda `Idempotency-Key` **zorunludur**; eksikse
`400 missing_idempotency_key` döner:
- `POST /orders` — sipariş oluşturma
- `POST /services/{id}/upgrade` — kaynak yükseltme (fark faturası)
- `POST /services/{id}/rebuild` — işletim sistemi yeniden kurulumu
- `POST /services/{id}/power/{action}` — güç işlemleri
- `DELETE /services/{id}` — servis iptali (disk silinir)
## Öneriler
- Her mantıksal işlem için **kararlı (stable)** bir anahtar üretin. Örneğin
"servis 500 için aylık yükseltme" isteğini tekrar denerken **aynı** anahtarı
kullanın ki çift faturalama olmasın.
- Yeni bir işlem için **yeni** anahtar üretin.
- Anahtarlar 24 saat boyunca saklanır; bu süre sonunda aynı anahtar yeniden
kullanılabilir.
------------------------------------------------------------------------------
# Hız Sınırlama (Rate Limit)
API'yi aşırı yüklenmeye karşı korumak için istek sayısı dakika bazında sınırlanır.
Sınır, kayan pencere (sliding window) yöntemiyle hesaplanır.
Her API anahtarının limiti **ayrıdır** — ortak/global bir limit yoktur. Limit, uç
nokta grubuna (kova) göre uygulanır; bir kovadaki tüketim diğerlerini etkilemez.
## Kotalar
| Kova (bucket) | Varsayılan limit | Kapsam |
|---|---|---|
| `default` | 60 istek / dakika | Sipariş, ürün, bakiye, webhook, VNC, erişim bilgileri, yaşam döngüsü (suspend/unsuspend/terminate) |
| `services_read` | 150 istek / dakika | `GET /services`, `GET /services/{id}` |
| `services_status` | 300 istek / dakika | `GET /services/{id}/status` (yüksek frekanslı polling) |
| `services_changes` | 120 istek / dakika | Değişiklik uç noktaları (power, rebuild, parola, upgrade) |
Büyük bayiler için kova bazlı artışlar Rabisu tarafından tanımlanır. Anahtarınızın
**etkin** limitlerini `GET /auth/me` yanıtındaki `rate_limits` alanından okuyabilirsiniz:
```json
"rate_limits": {
"default": 60,
"services_read": 150,
"services_status": 300,
"services_changes": 120
}
```
## Yanıt Başlıkları
Her yanıtta güncel kota bilgisi döner:
| Başlık | Anlamı |
|---|---|
| `X-RateLimit-Limit` | Dakikadaki toplam kota |
| `X-RateLimit-Remaining` | Kalan istek sayısı |
| `X-RateLimit-Reset` | Kotanın yenileneceği Unix zaman damgası |
## Kota Aşımı (429)
Kota aşıldığında **429 Too Many Requests** döner ve `Retry-After` başlığı kaç
saniye beklemeniz gerektiğini bildirir:
```
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1785000000
```
```json
{
"success": false,
"error": {
"code": "rate_limited",
"message": "İstek limiti aşıldı. Lütfen daha sonra tekrar deneyin."
},
"meta": { "trace_id": "...", "timestamp": "..." }
}
```
## Öneriler
- `429` alırsanız `Retry-After` süresi kadar bekleyip **üstel geri çekilme
(exponential backoff)** ile tekrar deneyin.
- Servis durumu için sürekli polling yerine mümkünse Webhooks
kullanın — bildirimler anlık gelir, kota tüketmez.
- Toplu işlemlerde istekleri zamana yayın; aynı anda yüzlerce istek göndermeyin.
------------------------------------------------------------------------------
# Hata Kodları
Tüm hatalar standart zarf içinde `error.code` (İngilizce slug) ve `error.message`
(insan-okunur, Türkçe) ile döner. Kod (slug) programatik kontrol için stabildir;
mesaj metni değişebilir.
```json
{
"success": false,
"error": {
"code": "validation_error",
"message": "Girdi doğrulaması başarısız.",
"details": { "field": "period" }
},
"meta": { "trace_id": "...", "timestamp": "..." }
}
```
## Genel HTTP Hataları
| HTTP | `code` | Anlamı |
|---|---|---|
| 400 | `invalid_input` | Geçersiz veya eksik girdi |
| 400 | `missing_idempotency_key` | Zorunlu `Idempotency-Key` başlığı yok |
| 401 | `unauthorized` | Kimlik doğrulama başarısız (eksik/hatalı key veya secret) |
| 403 | `forbidden` | Erişim yetkisi yok (örn. servis sahibi değilsiniz) |
| 404 | `not_found` | Kaynak bulunamadı |
| 405 | `method_not_allowed` | HTTP metodu bu uç noktada desteklenmiyor |
| 409 | `conflict` | Çakışan istek (örn. downgrade denemesi) |
| 422 | `validation_error` | İş kuralı doğrulaması başarısız |
| 429 | `rate_limited` | İstek limiti aşıldı (bkz. Rate Limit) |
| 500 | `internal_error` | Sunucu tarafı beklenmeyen hata |
## Alan (Domain) Hataları
| HTTP | `code` | Anlamı |
|---|---|---|
| 402/422 | `insufficient_credit` | Kredi bakiyesi işlem için yetersiz |
| 422 | `idempotency_key_reuse` | Aynı `Idempotency-Key` farklı gövde ile kullanıldı |
| 422 | `location_locked` | Yükseltme sırasında lokasyon değiştirilemez |
| 404 | `webhook_not_found` | Webhook endpoint kaydı bulunamadı |
| 409 | `webhook_limit_exceeded` | Bayi başına maksimum aktif webhook sayısı aşıldı |
## Nasıl Ele Almalı?
- **Programatik kontrolde daima `error.code`'a bakın**, mesaj metnine değil.
- `5xx` ve `429` hatalarında tekrar deneme (retry) mantıklıdır; `4xx` hatalarında
(429 hariç) istek düzeltilmeden tekrar denemeyin.
- Destek talebinde `meta.trace_id` değerini paylaşın — sunucu loglarında ilgili
isteği bulmayı sağlar.
------------------------------------------------------------------------------
# Sipariş Yaşam Döngüsü
Bir VPS siparişi oluşturmaktan servisin aktif hale gelmesine kadar geçen akış
asenkrondur: sipariş anında oluşur, gerçek VPS kurulumu arka planda tamamlanır.
## 1. Sipariş Oluşturma — `POST /orders`
`Idempotency-Key` **zorunludur** (çift sipariş koruması).
```bash
curl -X POST https://api.rabisu.com/api/v1/orders \
-u "rsk_live_xxx:rsks_yyy" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9f1c2d3e-4b5a-6c7d-8e9f-0a1b2c3d4e5f" \
-d '{
"product_id": 101,
"period": "monthly",
"config_options": { "12": 3, "15": 1 },
"extra": { "domain": "vps01.musteri.com" }
}'
```
Yanıt (202 Accepted) bir **provizyon kaydı** döner. `id` alanı bayi tarafındaki
korelasyon anahtarınızdır (yerel kaydınızda saklayın):
```json
{
"success": true,
"data": {
"id": 5001,
"status": "pending_provision",
"order_id": 88123,
"service_id": null,
"payment_applied": true
},
"meta": { "trace_id": "...", "timestamp": "..." }
}
```
## 2. Kredi Uygulama (auto_apply_credit)
Sipariş oluşturulunca fatura kesilir. `auto_apply_credit` bayrağınıza göre:
- **Açık (varsayılan):** Fatura otomatik olarak bayi kredinizden ödenir. Yeterli
kredi varsa provizyon kuyruğa girer (`pending_provision`).
- **Kapalı:** Kredi uygulanmaz, sipariş `pending_payment` kalır. Ödemeyi sonra
manuel olarak yaparsınız (bkz. adım 4). Bu, kurulum öncesi inceleme penceresi
sağlar (fraud/hostname kontrolü vb.).
Kredi yetersizse `insufficient_credit` bilgisi döner ve sipariş `pending_payment`
durumunda bekler.
## 3. Provizyon Durumu — `GET /orders/{id}`
VPS kurulumu tamamlanınca `service_id` atanır ve durum `active` olur. Durumu
sorgulayın:
```bash
curl -u "rsk_live_xxx:rsks_yyy" \
https://api.rabisu.com/api/v1/orders/5001
```
Provizyon durumları:
| Durum | Anlamı |
|---|---|
| `pending_payment` | Ödeme bekleniyor (kredi uygulanmadı) |
| `pending_provision` | Ödeme tamam, VPS kurulumu kuyrukta |
| `active` | VPS kuruldu, `service_id` atandı |
| `failed` | Provizyon başarısız |
> **İpucu:** Sürekli polling yerine Webhooks kullanın —
> `service.created` olayı VPS hazır olduğunda anında bildirilir.
## 4. Bekleyen Ödemeyi Tahsil Etme — `POST /orders/{id}/settle`
Sipariş `pending_payment` durumundaysa (kredi yetersizdi veya auto-apply kapalıydı),
bakiye yükledikten sonra ödemeyi tetikleyin:
```bash
curl -X POST https://api.rabisu.com/api/v1/orders/5001/settle \
-u "rsk_live_xxx:rsks_yyy"
```
Bu uç nokta doğal olarak idempotenttir: zaten ödenmiş bir sipariş `already_applied`
ile no-op döner. Başarılı tahsil sonrası provizyon kuyruğa girer.
## 5. Servisi Yönetme
Servis `active` olduktan sonra servis uç noktalarını kullanabilirsiniz:
- `GET /services/{id}` — özet (IP, hostname, durum)
- `GET /services/{id}/status` — anlık CPU/RAM/disk
- `POST /services/{id}/power/{action}` — start/stop/restart/poweroff
- `POST /services/{id}/rebuild` — işletim sistemi yeniden kurulumu
- `POST /services/{id}/upgrade` — kaynak yükseltme (prorata fark faturası)
- `POST /services/{id}/suspend` · `/unsuspend` — askıya al / kaldır
- `DELETE /services/{id}` — iptal (disk silinir, `Idempotency-Key` zorunlu)
Tüm alan detayları için OpenAPI referansına bakın.
------------------------------------------------------------------------------
# Webhooks
Webhook'lar, sunucunuzu sürekli sorgulamak (polling) yerine olayları anlık olarak
almanızı sağlar. Bir olay gerçekleştiğinde Rabisu, kayıtlı URL'nize imzalı bir
`POST` isteği gönderir.
## 1. Endpoint Kaydı — `POST /webhooks`
```bash
curl -X POST https://api.rabisu.com/api/v1/webhooks \
-u "rsk_live_xxx:rsks_yyy" \
-H "Content-Type: application/json" \
-d '{
"url": "https://bayi.com/modules/addons/rabisu/webhook.php",
"event_types": ["service.*", "credit.added"],
"description": "Üretim webhook"
}'
```
Yanıtta HMAC **secret yalnızca bir kez** döner — güvenli saklayın:
```json
{
"success": true,
"data": {
"id": 12,
"url": "https://bayi.com/.../webhook.php",
"event_types": ["service.*", "credit.added"],
"secret": "whsec_...TEK_SEFER...",
"is_active": true
},
"meta": { "trace_id": "...", "timestamp": "..." }
}
```
### URL Kuralları (SSRF Koruması)
- `https://` zorunludur.
- URL'de `user:pass@` bulunamaz.
- DNS çözümlenen tüm IP'ler public olmalıdır (özel/dahili IP'ler reddedilir).
## 2. Abonelik Pattern'leri
`event_types` dizisi şu biçimleri destekler:
| Pattern | Anlamı |
|---|---|
| `*` | Tüm olaylar (catch-all) |
| `service.*` | Namespace wildcard — `service` ile başlayan tüm olaylar |
| `service.created` | Tam olay adı (exact) |
Geçerli namespace'ler: `service`, `product`, `credit`. Geçersiz bir pattern
gönderirseniz kayıt `422` ile reddedilir (sessizce "hiç olay almama" yerine net hata).
## 3. Olay Kataloğu
| Olay | Ne zaman | Payload alanları (özet) |
|---|---|---|
| `service.created` | VPS provizyonu tamamlandı (aktif) | `provision_id`, `service_id`, `order_id`, `product_id`, `status` |
| `service.suspended` | Servis askıya alındı | `service_id`, `reason` |
| `service.unsuspended` | Askı kaldırıldı | `service_id` |
| `service.terminated` | Servis iptal edildi | `service_id` |
| `product.price_changed` | Ürün fiyatı değişti (bildirim) | `product_id` |
| `product.stock_changed` | Ürün stok durumu değişti | `product_id`, `stock_active` |
| `product.disabled` | Ürün satışa kapatıldı | `product_id` |
| `credit.added` | Bayi kredisi eklendi (iade/manuel) | `reseller_id`, `amount`, `currency`, `reason`, `new_balance` |
> **Not:** `product.*` olayları yalnızca **bildirim** taşır. Güncel veriyi
> `GET /products/{id}` ile çekin (hibrit notification + pull).
Her payload'ın kökünde `schema_version` alanı bulunur (şu an `1`).
## 4. İmza Doğrulama (KRİTİK)
Her teslimatta iki imza başlığı gönderilir:
| Başlık | İçerik | Kullanım |
|---|---|---|
| `X-Rabisu-Signature-V2` | `t=,v1=` — `HMAC-SHA256(secret, "{t}.{X-Rabisu-Event-Type}.{ham gövde}")` | **Önerilen.** Replay ve olay tipi değiştirme koruması sağlar |
| `X-Rabisu-Signature` | `sha256=` — yalnızca ham gövdenin HMAC-SHA256'sı | Eski alıcılar için. Replay koruması **yok**, yeni entegrasyonlarda kullanmayın |
Alıcı, `|şimdi - t| <= 300` saniye olduğunu ve imzalanan olay tipini
(`X-Rabisu-Event-Type`) doğrulamalıdır.
Doğrulama (PHP, V2):
```php
$raw = file_get_contents('php://input');
$eventType = $_SERVER['HTTP_X_RABISU_EVENT_TYPE'] ?? '';
$header = $_SERVER['HTTP_X_RABISU_SIGNATURE_V2'] ?? '';
parse_str(str_replace(',', '&', $header), $parts); // t=..., v1=...
$t = (int) ($parts['t'] ?? 0);
$v1 = (string) ($parts['v1'] ?? '');
if (abs(time() - $t) > 300) {
http_response_code(401); // Zaman penceresi dışında (replay)
exit;
}
$expected = hash_hmac('sha256', $t . '.' . $eventType . '.' . $raw, $webhookSecret);
if (!hash_equals($expected, $v1)) {
http_response_code(401); // Geçersiz imza
exit;
}
// Güvenli: payload işlenebilir
$payload = json_decode($raw, true);
```
- İmzayı **her zaman** doğrulayın — yoksa sahte istekler kabul edersiniz.
- Karşılaştırmada `hash_equals` gibi **sabit zamanlı** fonksiyon kullanın.
- Gövdeyi JSON'a çevirmeden **önce** ham hali üzerinden doğrulayın (yeniden
serialize edilen JSON imzayı bozar).
## 5. Teslim ve Yanıt Sözleşmesi
Endpoint'iniz aşağıdaki HTTP kodlarını döndürmelidir:
| Yanıt | Anlamı | Rabisu davranışı |
|---|---|---|
| `2xx` | Başarıyla işlendi | Teslim tamam |
| `4xx` (401 dahil) | Kalıcı hata (imza/format) | Tekrar denenmez |
| `5xx` | Geçici hata | Üstel backoff ile tekrar denenir |
Yeniden deneme takvimi: 1dk → 5dk → 15dk → 1s → 6s → 24s. 6 denemeden sonra olay
"dead letter" olarak işaretlenir. Art arda 5 başarısızlıkta endpoint otomatik
pasifleştirilir (circuit breaker) — düzeltince `PATCH /webhooks/{id}` ile yeniden
aktifleştirin.
## 6. Yönetim Uç Noktaları
- `GET /webhooks` — kayıtlı endpoint'leri listele (pasif dahil)
- `GET /webhooks/{id}` — detay
- `PATCH /webhooks/{id}` — yeniden aktifleştir / `event_types` veya `description` güncelle
- `DELETE /webhooks/{id}` — sil (soft delete)
==============================================================================
BOLUM 2 — API REFERANSI (ENDPOINT'LER)
==============================================================================
Rabisu Sanal Sunucu Bayilik (Reseller) API'si — bayilerin programatik olarak
sanal sunucu siparisi vermesi, mevcut hizmetleri yonetmesi ve hesap bakiyesini
sorgulamasi icin REST arayuzu.
**Authentication:** HTTP Basic Auth (RFC 7617).
Username = API Key (`rsk_live_*`), Password = API Secret (`rsks_live_*`).
**Rate Limit:** Her API anahtarinin limiti AYRIDIR (ortak/global limit yok).
Limit uc grubuna (kova) gore dakika basinadir:
`default` 60 (siparis, urun, bakiye, webhook, vnc, kimlik bilgisi, lifecycle),
`services_read` 150 (`GET /services`, `GET /services/{id}`),
`services_status` 300 (`GET /services/{id}/status`),
`services_changes` 120 (power, rebuild, parola, upgrade).
Buyuk bayiye kova bazli artis Rabisu tarafindan tanimlanir; etkin degerler
`GET /auth/me` → `rate_limits`. Tum yanitlarda `X-RateLimit-*` header'lari,
asimda `429` + `Retry-After` (saniye) doner — istemci o kadar bekleyip tekrar
denemelidir.
**Idempotency:** POST/PUT/PATCH endpoint'leri `Idempotency-Key` header'ini
destekler (RFC draft-irsdl-httpbis-idempotency-header). Ayni key + ayni body
24 saat icinde tekrar gonderilirse cached response doner. Ayni key + farkli
body → 422 `idempotency_key_reuse`.
==============================================================================
[System]
==============================================================================
Saglik kontrolu, versiyon bilgisi (auth gerektirmez)
------------------------------------------------------------------------------
GET /health
Saglik kontrolu (auth gerektirmez)
------------------------------------------------------------------------------
operationId: getHealth
Kimlik dogrulama: Gerekmez
Sunucunun ayakta oldugunu dogrular. Load balancer / monitoring icin
kullanilir. Auth, rate limit ve idempotency middleware'lerine BAGLI
DEGILDIR — sinirsiz cagirilabilir.
Yanitlar:
200 — Sunucu saglikli
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (object, opsiyonel)
- status (string, opsiyonel) [Ornek: "ok"]
- version (string, opsiyonel) [Ornek: "1.0.0"]
- timestamp (string(date-time), opsiyonel) [Ornek: "2026-04-24T12:00:00Z"]
------------------------------------------------------------------------------
GET /openapi.yaml
OpenAPI 3.0 spec'inin ham YAML icerigi
------------------------------------------------------------------------------
operationId: getOpenApiSpec
Kimlik dogrulama: Gerekmez
Bu API'nin OpenAPI 3.0 spec'ini ham YAML olarak doner. Auth gerektirmez —
endpoint listesi ve schema'lar dokuman karakteri tasir, gizli degil.
Kullanim alanlari:
- Dev test paneli (`/dev/docs.html`) Swagger UI ile bunu render eder.
- Postman / Insomnia gibi araclara import.
- SDK / istemci kodu jeneratorleri (openapi-generator vb.).
**Cache:** ETag + 5 dakika `Cache-Control: public, max-age=300`. Spec
siklikla degismez; tarayici 304 Not Modified ile ucuz cevap alir.
Yanitlar:
200 — OpenAPI 3.0 spec'inin YAML icerigi
Yanit basliklari: ETag: 16 karakterli MD5 prefix; If-None-Match ile 304 dondurmek icin kullanilir. | Cache-Control
(application/yaml)
string [Ornek: "openapi: 3.0.3\ninfo:\n title: Rabisu Reseller API\n version: 1.0.0\n...\n"]
304 — Spec degismedi (If-None-Match esleslti). Body bos.
404 — docs/openapi.yaml dosyasi sunucuda bulunamadi (deploy hatasi)
(text/plain)
string [Ornek: "openapi.yaml not found"]
==============================================================================
[Auth]
==============================================================================
Kimlik dogrulama smoke test + key meta bilgisi + kredi bakiye
------------------------------------------------------------------------------
GET /auth/me
Authenticated key meta bilgisini doner
------------------------------------------------------------------------------
operationId: getAuthMe
Kimlik dogrulama: BasicAuth
HTTP Basic Auth ile gelen request'in dogrulanmis kimligini doner.
Smoke test endpoint'i — entegrasyonun calistigini hizli test etmek icin.
DONULEN ALANLAR (api_secret_hash veya benzeri sensitive alan **YOK**):
- `api_key_id`: Internal numeric ID
- `reseller_id`: WHMCS `tblclients.id` (bayinin musteri id'si)
- `name`: Key etiketi
- `permissions`: Scope listesi (`["orders:create", "*"]`)
- `client_ip`: Bu istegi yapan istemci IP'si (X-Forwarded-For trusted proxy resolver ile)
- `last_used_at`: Bu key'in son kullanim zamani (RFC 3339)
- `expires_at`: Key'in expire tarihi (NULL = sonsuz)
- `rate_limit_rpm`: Eski tek-sayi override; yalniz `default` kovayi etkiler (NULL = 60)
- `rate_limits`: Kova bazli ETKIN dakika limitleri
(`{"default":60,"services_read":150,"services_status":300,"services_changes":120}`)
- `auto_apply_credit`: POST /orders bakiye otomatik tahsilat toggle'i
(true = default; false ise siparis `pending_payment` + `notice=auto_apply_credit_disabled`
doner, bayi faturayi manuel oder). Admin CLI ile degistirilir:
`php bin/api-key-update.php --auto-apply-credit=0|1`
Yanitlar:
200 — Authenticated, kimlik bilgisi donuldu
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (AuthMeResponse, opsiyonel)
401 — Authentication basarisiz — eksik header, gecersiz API key/secret,
revoked veya expired key.
Yanit basliklari: WWW-Authenticate: RFC 7235 header — istemciye Basic Auth zorunlulugunu bildirir
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "invalid_credentials",
"message": "Gecersiz kimlik bilgileri."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
403 — IP whitelist ihlali — gelen IP key'in izin listesinde degil
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "ip_not_whitelisted",
"message": "Bu IP adresinden erisim izniniz bulunmamaktadir."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
GET /auth/balance
Bayi WHMCS kredi bakiyesi (APCu 60s cache)
------------------------------------------------------------------------------
operationId: getAuthBalance
Kimlik dogrulama: BasicAuth
Authenticated bayinin WHMCS kredi bakiyesini doner. Bakiye ONCE read-only
WHMCS DB replica'sindan (`tblclients.credit`) okunur; DB erisilemezse
WHMCS HTTP API (`GetClientsDetails`) fallback'e duser.
**Cache:** APCu 60 saniye (env: `BALANCE_CACHE_TTL_SECONDS`, 0 = bypass).
Response'ta `cached` alani cache hit durumunu bildirir.
**Currency:** Faz 1'de global `BALANCE_DEFAULT_CURRENCY` (default `TRY`).
Faz 2'de per-client currency WHMCS `tblcurrencies` join ile gelecek.
**Normalize:** WHMCS'de client bulunamazsa `credit=0.0` doner (404 vermez —
entegrasyon tarafinin defensive programlamasi icin daha guvenli default).
Yanitlar:
200 — Bakiye donuldu
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (BalanceResponse, opsiyonel)
Ornek:
{
"success": true,
"data": {
"reseller_id": 1024,
"credit": 125.5,
"currency": "TRY",
"cached": false
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
401 — Authentication basarisiz
→ bkz. sema: ErrorResponse
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
GET /auth/balance/movements
Bayi bakiye hareketleri (canli WHMCS tblcredit)
------------------------------------------------------------------------------
operationId: listAuthBalanceMovements
Kimlik dogrulama: BasicAuth
Authenticated bayinin bakiye hareketlerini kronolojik (yeni → eski) doner.
Kaynak: **canli WHMCS `tblcredit`** — WHMCS Credit Management ile birebir
(tum kredi girisleri + kullanilan/faturaya uygulanan kredi dahil).
**Onemli:** Guncel bakiye icin `GET /auth/balance` kullanin. Bu liste
dogrudan WHMCS'ten okundugu icin yerel senkron gecikmesi yoktur; WHMCS
replica gecici erisilemezse liste bos donebilir.
**`reason` degerleri:** `manual_credit` (admin kredi yuklemesi), `refund`
(faturaya bagli iade, `related_invoice_id` dolu), `apply_credit` (krediden
yapilan odeme, negatif `amount`).
**Izolasyon:** Bayi yalnizca kendi hareketlerini gorur (client id filtresi).
**`amount` isareti:** Pozitif = ekleme (kredi yuklendi/iade), Negatif = cikis
(krediden odeme). `new_balance` o hareket dahil kumulatif bakiyedir.
`currency` bayinin gercek WHMCS para birimidir (`tblclients.currency`).
**NOT:** Yerel `mod_reseller_credit_movements` tablosu artik yalniz
`credit.added` webhook'u icin beslenir; bu liste oradan OKUNMAZ.
Parametreler:
- limit (query, integer, opsiyonel) [varsayilan: 50; min 1; max 200]
Sayfa basina kayit (1-200)
- offset (query, integer, opsiyonel) [varsayilan: 0; min 0]
Atlanacak kayit sayisi (sayfalama)
Yanitlar:
200 — Hareket listesi donuldu
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (object, opsiyonel)
- items (array, opsiyonel)
Ornek:
{
"success": true,
"data": {
"items": [
{
"id": 4821,
"amount": -99,
"currency": "TRY",
"reason": "apply_credit",
"description": "Siparis #5012 / fatura #8841 icin bakiyeden otomatik odeme",
"related_invoice_id": 8841,
"related_order_id": 5012,
"new_balance": 26.5,
"occurred_at": "2026-05-29T13:05:11Z",
"created_at": "2026-05-29T13:05:11Z"
}
]
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-05-29T13:05:12Z"
}
}
401 — Authentication basarisiz
→ bkz. sema: ErrorResponse
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
==============================================================================
[Products]
==============================================================================
Urun katalogu (WHMCS urunleri + /ozl/active.json stok durumu)
------------------------------------------------------------------------------
GET /products
Urun katalogu listesi (+ stok aktiflik flag'i)
------------------------------------------------------------------------------
operationId: getProducts
Kimlik dogrulama: BasicAuth
WHMCS `tblproducts` kaydi ile `/ozl/active.json` stok durumunu birlestirir.
`hidden=1` WHMCS urunleri listeden OTOMATIK filtrelenir.
**Resellable filtresi (Rabisu Master modul):** Rabisu ana WHMCS'indeki
`mod_rabisu_master_product_meta` tablosunda `resellable=1` olan urunler
donuler. Master modul KURULU DEGILSE (tablo yok) filtre uygulanmaz —
tum gorunur urunler doner (geri uyum).
**service_type + management (Rabisu Master modul):** Rabisu admini her
urune tip (`vps`, `dedicated`, `hosting`) ve yonetim paneli secer. Tip
bayiye `service_type` olarak doner (panel adi verilmez). `management`:
`full` = API paneli yonetir (durum, guc, reinstall...); `basic` = API'de
o panelin entegrasyonu yok, urun yine satilir ve bayi musterisine yalniz
erisim bilgileri (`GET /services/{id}/credentials`: IP, kullanici, sifre,
`panel_url`) gosterilir. Tipi secilmemis urunler listelenmez ve siparis
edilemez. Bayi modulu tanimadigi tip/yonetim ikilisini import etmemelidir.
**stock_active** flag'i:
- `global_status=0` ise TUM urunler icin false
- `products` map'i verildiyse sadece `1` olan urunler true
- `products` map'i VERILMEDI ise hepsi true (fail-open)
**group_id** query parametresi (opsiyonel, positive integer): sadece
ilgili WHMCS urun grubunu listele. Gecersiz/bos deger YOK sayilir.
Parametreler:
- group_id (query, integer, opsiyonel) [min 1]
WHMCS `tblproducts.gid` filtresi
Yanitlar:
200 — Urun listesi donuldu
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (ProductListResponse, opsiyonel)
401 — Authentication basarisiz
→ bkz. sema: ErrorResponse
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
GET /products/{id}
Tek urun detayi
------------------------------------------------------------------------------
operationId: getProductById
Kimlik dogrulama: BasicAuth
Belirli bir urunun detaylarini dondurur. Urun bulunamazsa VEYA bayiye
gizliyse (hidden=1) VEYA satilamaz (Rabisu Master `resellable=0`) VEYA
hizmet tipi secilmemisse (`service_type`) ise
AYNI 404 `product_not_found` yaniti doner — bayiler arasi envanter
sizintisini onlemek icin durumlar ayrilmaz.
**config_options[].role (Rabisu Master modul):** Her config option grubu
`mod_rabisu_master_configgroup_meta`'dan gelen bir `role` alaniyla doner
(2 rol): `resource` (RAM/CPU/disk = yukseltilebilir), `fixed` (kilitli/sabit,
LOKASYON DAHIL — upgrade'de degismez). Kaydi olmayan grup veya master modul
kurulu degilse `fixed` (guvenli varsayilan) doner. Eski `location` degeri de
`fixed` (kilitli) sayilir. Bayi modulu + upgrade korumasi bu rolu tuketir.
**config_options[].options[].stock_active (Rabisu Master modul):** Her
alt-secenek (orn. tekil lokasyon / OS surumu) `mod_rabisu_master_configoption_stock`'tan
gelen bir `stock_active` bool alaniyla doner. `false` ise o alt-secenek
bayilere satista KAPALIDIR — bayi modulu katalogda gizler, sipariste secilirse
409 `config_option_out_of_stock` doner. Kaydi olmayan alt-secenek / master
modul kurulu degilse `true`. WHMCS `hidden` bayragindan BAGIMSIZ (yalniz bayi
satisini kapatir; Rabisu kendi satisina devam eder).
Parametreler:
- id (path, integer, zorunlu) [min 1]
WHMCS `tblproducts.id`
Yanitlar:
200 — Urun donuldu
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (ProductDetail, opsiyonel)
401 — Authentication basarisiz
→ bkz. sema: ErrorResponse
404 — Urun bulunamadi veya bayiye gizli
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "product_not_found",
"message": "Aradiginiz urun bulunamadi.",
"details": {
"product_id": 9999
}
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
==============================================================================
[Orders]
==============================================================================
Yeni VPS siparisi, mevcut siparis listesi/detayi
------------------------------------------------------------------------------
GET /orders
Bayinin siparis listesi (provision kayitlari)
------------------------------------------------------------------------------
operationId: listOrders
Kimlik dogrulama: BasicAuth
Parametreler:
- limit (query, integer, opsiyonel) [varsayilan: 50; min 1; max 200]
- offset (query, integer, opsiyonel) [varsayilan: 0; min 0]
Yanitlar:
200 — Siparis listesi
→ bkz. sema: OrderListResponse
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
POST /orders
Yeni siparis olustur + bakiyeden OTOMATIK odeme dene
------------------------------------------------------------------------------
operationId: createOrder
Kimlik dogrulama: BasicAuth
**3 sonuclu akis:**
1. `StockService::isAvailable(pid)` = false ise 409 `product_unavailable`.
1b. `extra.configoptions` icinde bayi satisi KAPALI bir alt-secenek (orn.
stogu biten lokasyon) secilmisse 409 `config_option_out_of_stock`
(Rabisu Master `mod_rabisu_master_configoption_stock.in_stock=0`). Bayi
modulu bunlari zaten gizler; bu sunucu tarafi ikinci savunma hattidir.
2. `period` whitelist disi ise 422 `invalid_period`.
3. WHMCS `AddOrder` -> order + unpaid invoice uretilir.
4. Bayinin `auto_apply_credit` toggle'i (GET /auth/me) kontrol edilir:
- **false** -> `ApplyCredit` ATLANIR; `status=pending_payment`,
`payment_applied=false`, `notice=auto_apply_credit_disabled`.
Bayi faturayi kendi tercihine gore (panelden/havaleden) manuel
oder. Toggle admin CLI ile acilir:
`php bin/api-key-update.php --auto-apply-credit=1`.
- **true (default)** -> WHMCS `ApplyCredit` (TEK cagri) sonuca gore:
- **success** -> `status=pending_provision`, `payment_applied=true`;
WHMCS InvoicePaid hook'u virtualizor queue cron'unu tetikler.
- **insufficient funds** -> `status=pending_payment`,
`notice=insufficient_credit`; bayi manuel odeyebilir.
- **HTTP 5xx / timeout** -> `status=payment_failed`,
RequestLogger'a `alarm` flag'i ile kayit acilir.
**ÇAĞRILMAYAN aksiyonlar:** `AcceptOrder` (WHMCS InvoicePaid hook zaten
devreye girer), `VirtualizorClient` (WHMCS cron VPS'i kurar), bakiye
pre-check, `DeleteOrder` (insufficient edge case'inde bile).
**Idempotency-Key header ZORUNLUDUR** (aksi halde 400
`missing_idempotency_key`). Ayni key+body replay icin 202 cached
response doner.
Parametreler:
- Idempotency-Key (header, string, zorunlu) [maxLength 64; Ornek: "a3f2b1e4-00d6-4c89-9fef-1a2b3c4d5e6f"]
Zorunlu replay koruma anahtari (UUIDv4 onerilir)
Istek govdesi (application/json, zorunlu):
→ bkz. sema: CreateOrderRequest
Ornek:
{
"product_id": 101,
"period": "monthly",
"extra": {
"hostname": "web01.bayi.example",
"configoption1": "centos-stream-9"
}
}
Yanitlar:
202 — Siparis DB'ye yazildi. `status` ApplyCredit sonucunu yansitir;
bayi VPS'in kurulmasini `GET /orders/{id}` ile polla.
Yanit basliklari: X-Idempotent-Replay: Cache'den donen tekrarli istekte `"true"` olur.
→ bkz. sema: CreateOrderResponse
Ornek — pending_provision_balance_sufficient: Bakiye yeterli — ApplyCredit success
{
"success": true,
"data": {
"id": 77,
"order_id": 9001,
"invoice_id": 8001,
"invoice_total": 99,
"currency": "TRY",
"status": "pending_provision",
"payment_applied": true,
"balance_after": 26,
"notice": null,
"due_date": null
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
Ornek — pending_payment_insufficient_credit: Bakiye yetersiz — fatura WHMCS'te unpaid kaliyor
{
"success": true,
"data": {
"id": 78,
"order_id": 9002,
"invoice_id": 8002,
"invoice_total": 499,
"currency": "TRY",
"status": "pending_payment",
"payment_applied": false,
"balance_after": null,
"notice": "insufficient_credit",
"due_date": null
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440001",
"timestamp": "2026-04-24T12:00:00Z"
}
}
Ornek — payment_failed_apply_credit_5xx: ApplyCredit 5xx/timeout — admin mudahale gerekli
{
"success": true,
"data": {
"id": 79,
"order_id": 9003,
"invoice_id": 8003,
"invoice_total": 199,
"currency": "TRY",
"status": "payment_failed",
"payment_applied": false,
"balance_after": null,
"notice": null,
"due_date": null
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440002",
"timestamp": "2026-04-24T12:00:00Z"
}
}
Ornek — pending_payment_auto_apply_credit_disabled: Bayinin auto_apply_credit toggle'i KAPALI — ApplyCredit hic
cagrilmadi, bayi faturayi manuel oder.
{
"success": true,
"data": {
"id": 80,
"order_id": 9004,
"invoice_id": 8004,
"invoice_total": 99,
"currency": "TRY",
"status": "pending_payment",
"payment_applied": false,
"balance_after": null,
"notice": "auto_apply_credit_disabled",
"due_date": null
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440003",
"timestamp": "2026-04-24T12:00:00Z"
}
}
400 — Idempotency-Key header eksik
Ornek:
{
"success": false,
"error": {
"code": "missing_idempotency_key",
"message": "POST /orders icin Idempotency-Key header zorunludur.",
"details": {
"header": "Idempotency-Key"
}
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
409 — Urun stokta degil (`product_unavailable`) VEYA secilen config alt-secenegi
bayi satisina kapali (`config_option_out_of_stock`).
Ornek — product_unavailable
{
"success": false,
"error": {
"code": "product_unavailable",
"message": "Secilen urun su anda siparise kapali.",
"details": {
"product_id": 101
}
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
Ornek — config_option_out_of_stock
{
"success": false,
"error": {
"code": "config_option_out_of_stock",
"message": "Secilen secenek (orn. lokasyon) su anda stokta degil. Lutfen farkli bir secenek secin.",
"details": {
"sub_option_ids": [
701
]
}
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
422 — period whitelist disi veya body validasyon hatasi
Ornek — invalid_period
{
"success": false,
"error": {
"code": "invalid_period",
"message": "Gecersiz abonelik donemi.",
"details": {
"provided": "weekly",
"allowed_values": [
"monthly",
"quarterly",
"semiannually",
"annually",
"biennially",
"triennially"
]
}
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
Ornek — validation_failed
{
"success": false,
"error": {
"code": "validation_failed",
"message": "Siparis request body gecersiz.",
"details": {
"fields": {
"product_id": "integer ve >0 olmalidir"
}
}
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
500 — WHMCS AddOrder upstream hatasi (order yaratilmadi)
------------------------------------------------------------------------------
GET /orders/{id}
Tek siparis detayi (bayi izolasyonu zorunlu)
------------------------------------------------------------------------------
operationId: getOrder
Kimlik dogrulama: BasicAuth
`unknown_state` kayitta bu cagri WHMCS `GetOrders` ile client ve 15 dakikalik
pencereye bakar. Tek eslesen siparis `pending_payment` olarak baglanir.
Pencere dolup eslesme yoksa yerel kayit `provision_failed` olur
(`notice=order_not_confirmed`). `AddOrder` tekrarlanmaz.
Parametreler:
- id (path, integer, zorunlu) [min 1]
Yanitlar:
200 — Siparis detayi
→ bkz. sema: OrderItem
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Siparis bulunamadi (diger bayinin kaydi da 404)
Ornek:
{
"success": false,
"error": {
"code": "order_not_found",
"message": "Siparis bulunamadi."
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
POST /orders/{id}/settle
Bekleyen odemeyi krediyle tahsil et (InvoicePaid)
------------------------------------------------------------------------------
operationId: settleOrder
Kimlik dogrulama: BasicAuth
Bekleyen (`pending_payment` / `payment_failed`) bir siparisin WHMCS
faturasina bayi bakiyesinden odeme dener. Bayi WHMCS'inde musteri
faturayi odeyince tetiklenir (InvoicePaid hook). Siparis aninda bakiye
yetersizdiyse, bayi sonradan bakiye yukledikten sonra burada tahsil
edilir → VPS kurulumu kuyruga girer (`pending_provision`).
**Idempotent:** Zaten odenmis (`payment_applied=true`) bir sipariste
WHMCS'e hic dokunulmaz; `already_applied=true` ile mevcut durum doner.
`Idempotency-Key` header'i opsiyoneldir; gonderilirse ayni anahtar ayni yaniti verir.
**Esli zamanli tahsil:** Ayni siparis icin baska bir tahsil suruyorsa
409 `settle_in_progress` (Retry-After 5) doner. ApplyCredit sonucu belirsiz
kalan (transport hatasi) kayit 5 dakika sonra WHMCS fatura durumuyla
cozulur: `Paid` ise odenmis sayilir, `Unpaid` ise yeniden denenir.
**Sonuc durumlari (`status`):**
- `pending_provision` — kredi uygulandi, VPS kurulumu beklemede
- `pending_payment` — bakiye hala yetersiz (`notice=insufficient_credit`),
hemen tekrar denenebilir
- `payment_failed` — WHMCS gecici hata (5xx); 5 dakika sonra tekrar dene
- `unknown_state` / `provision_failed` — tahsil yapilmaz, `already_applied=false`
**Izolasyon:** Baska bayinin siparis id'si 404 `order_not_found` doner.
Parametreler:
- id (path, integer, zorunlu) [min 1]
`mod_reseller_provisions.id` (POST /orders'in dondurdugu id)
Yanitlar:
200 — Tahsil denemesi sonucu (basari, yetersiz veya zaten odenmis)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (SettleOrderResult, opsiyonel)
Ornek — applied: Kredi uygulandi
{
"success": true,
"data": {
"id": 1024,
"order_id": 9401,
"invoice_id": 8401,
"currency": "TRY",
"status": "pending_provision",
"payment_applied": true,
"balance_after": 10,
"notice": null,
"already_applied": false
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
Ornek — alreadyPaid: Zaten odenmis (no-op)
{
"success": true,
"data": {
"id": 1024,
"order_id": 9402,
"invoice_id": 8402,
"currency": "TRY",
"status": "pending_provision",
"payment_applied": true,
"balance_after": null,
"notice": null,
"already_applied": true
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
Ornek — insufficient: Bakiye hala yetersiz
{
"success": true,
"data": {
"id": 1024,
"order_id": 9403,
"invoice_id": 8403,
"currency": "TRY",
"status": "pending_payment",
"payment_applied": false,
"balance_after": null,
"notice": "insufficient_credit",
"already_applied": false
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Siparis bulunamadi (diger bayinin kaydi da 404)
Ornek:
{
"success": false,
"error": {
"code": "order_not_found",
"message": "Siparis bulunamadi."
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
==============================================================================
[Services]
==============================================================================
VPS hizmet detayi (WHMCS + Virtualizor birlesimi), real-time metrikler ve
rebuild icin OS sablonlari. Tum endpoint'ler `ServiceOwnershipMiddleware`
arkasinda — bayi sadece kendi hizmetlerine erisebilir.
------------------------------------------------------------------------------
GET /services
Bayinin hizmetleri — toplu liste (imlec veya sayfa + arama)
------------------------------------------------------------------------------
operationId: listServices
Kimlik dogrulama: BasicAuth
Bayinin tum hizmetlerini sayfa sayfa doner. Cron durum senkronu icin
tasarlandi: 5000 hizmet = 25 istek (limit=200). Hizmet kaynagindan okur,
Virtualizor'a GITMEZ — sunucunun acik/kapali durumu icin
`GET /services/{id}/status` kullanin.
**Sayfalama:** `after` imleci (son gorulen `service_id`). Ilk istek
`after=0`; yanittaki `next_after` null olana kadar
`?after={next_after}` ile devam edin. Imlec sayfalamasi yeni hizmet
eklense de kaymaz.
**Sayfa modu (admin tablosu):** `page` veya `q` gelirse liste EN YENI
ONCE siralanir, `limit` (varsayilan 50) kadar oge ve `total` /
`total_pages` doner. `q` hostname, ana IP ve ek IP'lerde parca eslesme
yapar (`server123` → `r-server123-qwqe`); sayisalsa hizmet id'sine tam
eslesme de sayilir. `match_ids` aramaya VEYA ile eklenir — bayi kendi
tarafinda buldugu hizmetleri (or. musteri e-postasina gore) gonderir.
Bu modda `after` ve `ids` kullanilmaz.
**status:** API'nin kendi degerleri (`active`, `suspended`...); kaynak
sisteme (bugun WHMCS) ozgu metin donmez.
**Sahiplik:** Yalniz cagiran bayinin hizmetleri doner; `ids` filtresinde
baska bayinin id'si sessizce atlanir.
**Rate limit:** `services_read` kovasi (150 req/dk, `GET /services/{id}` ile ortak).
Parametreler:
- limit (query, integer, opsiyonel) [varsayilan: 100; min 1; max 200]
- after (query, integer, opsiyonel) [varsayilan: 0; min 0]
Imlec — bu service_id'den SONRAKI hizmetler
- ids (query, string, opsiyonel) [Ornek: "7777,7778"]
Virgulle ayrilmis service_id listesi (en fazla 200)
- page (query, integer, opsiyonel) [min 1]
Sayfa modu — 1'den baslar, en yeni once
- q (query, string, opsiyonel) [maxLength 100; Ornek: "server123"]
Sayfa modu — hostname / IP parca, id tam eslesme
- match_ids (query, string, opsiyonel) [Ornek: "7777,7778"]
Sayfa modu — aramaya VEYA ile eklenen service_id'ler (en fazla 500)
Yanitlar:
200 — Hizmet sayfasi
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (ServiceListResponse, opsiyonel)
Ornek:
{
"success": true,
"data": {
"items": [
{
"service_id": 7777,
"product_id": 101,
"product_name": "VPS X4",
"status": "active",
"domain": "web01.bayi.example",
"dedicated_ip": "203.0.113.42",
"billing_cycle": "Monthly",
"first_payment_amount": 99,
"recurring_amount": 99,
"next_due_date": "2026-11-24",
"reg_date": "2026-04-24"
}
],
"currency": "TRY",
"next_after": 7777
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-10-04T12:00:00Z"
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
422 — Gecersiz `ids`, `match_ids` veya `q` parametresi
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "invalid_input",
"message": "ids virgulle ayrilmis pozitif tam sayilar olmali (en fazla 200).",
"details": {
"field": "ids"
}
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-10-04T12:00:00Z"
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
503 — Hizmet kaynagi gecici olarak okunamadi — Retry-After sonra tekrar deneyin
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "service_list_unavailable",
"message": "Hizmet listesi su an okunamiyor, biraz sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-10-04T12:00:00Z"
}
}
------------------------------------------------------------------------------
GET /services/{id}
VPS hizmet detayi (WHMCS + Virtualizor birlesimi)
------------------------------------------------------------------------------
operationId: getServiceById
Kimlik dogrulama: BasicAuth
Bayi VPS hizmetinin WHMCS-side bilgileri (domain, IP, fatura periyodu,
son odeme tarihi, namespace) ile Virtualizor-side runtime metrikleri
(hostname, IP listesi, virt tipi, calisma durumu) birlesir.
**VPS_ID resolution:** Resmi Virtualizor WHMCS modulu VPS_ID'yi WHMCS
custom field'da tutar — `tblcustomfields` (fieldname=`vpsid` veya
gorunen adli `vpsid|...`, type='product', relid=PRODUCT_ID) +
`tblcustomfieldsvalues` (fieldid=customfield.id, relid=SERVICE_ID,
value=VPSID). Boru oncesi ad eslesir. Custom field tanimlanmamis veya
value bos ise `vps_id=null` doner ve `virt` payload'i null olur
(whitelabel paketler veya henuz provisionlanmamis hizmetler).
**Graceful degradation:** Virtualizor master upstream hatasi (timeout,
5xx, auth failure) durumunda 502 yerine 200 doner; `virt=null` ve
`virt_unavailable=true`. Bayi en azindan WHMCS bilgisini gorur.
**Rate limit:** `services_read` kovasi (150 req/dk, `GET /services` ile ortak).
Parametreler:
- id (path, integer, zorunlu) [min 1]
WHMCS `tblhosting.id` (service_id)
Yanitlar:
200 — Hizmet detayi donuldu (virt_unavailable=true ise virt=null olabilir)
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (ServiceDetail, opsiyonel)
Ornek:
{
"success": true,
"data": {
"service_id": 7777,
"product_id": 101,
"server_id": 12,
"vps_id": 1234,
"domain": "web01.bayi.example",
"dedicated_ip": "203.0.113.42",
"nameservers": [
"ns1.rabisu.com",
"ns2.rabisu.com"
],
"billing_cycle": "monthly",
"first_payment_amount": 99,
"recurring_amount": 99,
"next_due_date": "2026-05-24",
"domain_status": "Active",
"reg_date": "2026-04-24",
"virt": {
"hostname": "web01.bayi.example",
"ips": [
"203.0.113.42"
],
"virt_type": "kvm",
"suspended": false,
"os_name": "almalinux-8-x86_64",
"default_user": "root",
"cores": 2,
"ram_mb": 4096,
"disk_gb": 40,
"bandwidth_gb": 30720,
"bandwidth_used_gb": 12.5,
"location": "Germany"
},
"virt_unavailable": false
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Hizmet bulunamadi (tblhosting'de kayit yok)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "service_not_found",
"message": "Servis bulunamadi.",
"details": {
"service_id": 7777
}
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
DELETE /services/{id}
Servisi WHMCS modul hook'u ile terminate et (asenkron)
------------------------------------------------------------------------------
operationId: terminateService
Kimlik dogrulama: BasicAuth
WhmcsApiClient::moduleTerminate cagirilir. WHMCS provisioning queue
Virtualizor `delvs` job'unu async yurutur (~60s queue + ~30s delvs).
Bu yuzden response **HTTP 202 Accepted** doner; bayi sonra GET
/services/{id}/status ile takip eder.
**Idempotency-Key:** ZORUNLU (header yoksa `400 missing_idempotency_key`).
Destructive op + double-click korumasi. Ayni key ile 24 saat icinde
gelen tekrar istek cached response doner — 2. delvs queue'ya dusmez.
**Rate limit:** Default bucket (60 rpm).
**Body:** `{"confirm": true}` STRICT bool === true. Truthy
degerler (`1`, `"true"`, `"yes"`) reddedilir → `400 invalid_terminate_request`.
**vps_id null:** ATLA (izin verilir). Pending/whitelabel servisleri
temizleme senaryosu — WHMCS moduleTerminate vps_id'ye bakmaz,
serviceid ile calisir.
**Side effect:** `mod_reseller_provisions.status='terminated'` set
edilir + `service.terminated` event'i (`status='skipped'`) webhook
queue'ya yazilir.
Parametreler:
- id (path, integer, zorunlu) [min 1]
- Idempotency-Key (header, string(uuid), zorunlu)
ZORUNLU. Ayni key 24h cached.
Istek govdesi (application/json, zorunlu):
- confirm (boolean, zorunlu) [degerler: true]
STRICT bool === true. Truthy reddedilir.
Ornek:
{
"confirm": true
}
Yanitlar:
202 — Termination scheduled (async — WHMCS queue + Virtualizor delvs)
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (TerminateScheduledResult, opsiyonel)
Ornek:
{
"success": true,
"data": {
"status": "termination_scheduled",
"service_id": 12345,
"vps_id": 67890,
"terminated_at": "2026-04-25T06:25:00+00:00",
"estimated_completion_seconds": 120
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
400 — Invalid terminate request (`invalid_terminate_request` veya `missing_idempotency_key`)
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Servis bulunamadi
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
502 — WHMCS API upstream hatasi
------------------------------------------------------------------------------
GET /services/{id}/status
VPS anlik metrikleri (CPU/RAM/disk/net)
------------------------------------------------------------------------------
operationId: getServiceStatus
Kimlik dogrulama: BasicAuth
Virtualizor admin `act=vs&vs_status=` ile CANLI durum ve metrik (dugume o an
sorar). Bu cagri hata verir ya da beklenen yapida donmezse periyodik `act=vps_stats`
kullanilir. CPU yuzdesi enduser `act=cpu` ile gelir.
**Rate limit:** `services_status` kovasi — 300 req/dk, bayi basina ayri.
Diger kovalardan IZOLE.
**management=basic:** `409 management_unavailable` (panel API'de yok).
**Hata davranisi:** Virtualizor down ise 502 doner (graceful degradation
YOK — real-time metric icin partial yanit anlamsiz).
**Cache:** VPS basina 15 sn (sunucuda `STATUS_CACHE_TTL_SECONDS`). Ayni VPS'e
bu surede gelen ikinci istek Virtualizor'a gitmez. Power, rebuild, suspend,
unsuspend ve terminate islemleri onbellegi siler ve sonraki 180 sn onbellegi
tamamen kapatir; gecis sirasindaki ara durum (or. stop sonrasi hala calisiyor)
onbellekte kalip paneli geciktirmez.
Parametreler:
- id (path, integer, zorunlu) [min 1]
Yanitlar:
200 — Anlik metrikler
Yanit basliklari: X-RateLimit-Limit: services_status kovasi icin anahtarin etkin limiti | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (ServiceStatus, opsiyonel)
Ornek:
{
"success": true,
"data": {
"service_id": 7777,
"vps_id": 1234,
"running": true,
"cpu_percent": 12.5,
"ram_used_mb": 768,
"ram_total_mb": 2048,
"disk_used_mb": 5500,
"disk_total_mb": 40960,
"net_in_bytes": 102400,
"net_out_bytes": 51200,
"bandwidth_total_bytes": 1256550400
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Hizmet bulunamadi VEYA Virtualizor mapping'i yok
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "service_not_found",
"message": "Servis bulunamadi.",
"details": {
"service_id": 7777
}
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
502 — Virtualizor master upstream hatasi (timeout / 5xx)
→ bkz. sema: ErrorResponse
------------------------------------------------------------------------------
GET /services/{id}/credentials
Erisim bilgileri (username/password/ip/hostname/panel_url)
------------------------------------------------------------------------------
operationId: getServiceCredentials
Kimlik dogrulama: BasicAuth
Bayinin VPS'inin root erisim bilgilerini doner. `password` WHMCS
`DecryptPassword` API action'i ile cozulur (tblhosting.password sifreli
saklanir). Cozulemezse (DB'de bos, WHMCS API izni yok, transport hatasi)
`password_available=false` + bos `password` doner — bayi modulu bu durumda
mevcut sifreyi EZMEMELI.
**Kullanim:** Bayi modulu `service.created` webhook'u sonrasi bu endpoint'i
cagirip kendi WHMCS `tblhosting` kaydina (password + dedicatedip) yazar;
boylece admin/musteri panelinde + `{$service_password}` mail merge
field'inde gorunur.
**management=basic:** Panelini API yonetemeyen hizmette musteri bu
bilgilerle dogrudan baglanir; `panel_url` = WHMCS sunucusunun hostname'i
(cPanel :2083, Plesk :8443). Tam yonetimli hizmette `panel_url` null.
**GUVENLIK:** Donen `password` plaintext root sifresidir; sahiplik
(api_key → reseller → service) dogrulanir, loglarda maskelenir.
**Rate limit:** Default bucket (60 req/dk). **Idempotency:** yok (read).
Parametreler:
- id (path, integer, zorunlu) [min 1]
Yanitlar:
200 — VPS erisim bilgileri
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (object, opsiyonel)
- service_id (integer, opsiyonel) [Ornek: 7777]
- vps_id (integer, opsiyonel, null olabilir) [Ornek: 1234]
- username (string, opsiyonel) [Ornek: "root"]
- password (string, opsiyonel) [Ornek: "S3cr3t-Root-Pw"]
Plaintext root sifresi. password_available=false ise bos.
- password_available (boolean, opsiyonel) [Ornek: true]
Password WHMCS'ten cozulebildi mi.
- ip (string, opsiyonel) [Ornek: "203.0.113.42"]
- hostname (string, opsiyonel) [Ornek: "web01.bayi.example"]
- service_type (string, opsiyonel) [degerler: "vps" | "dedicated" | "hosting"; Ornek: "vps"]
- management (string, opsiyonel) [degerler: "full" | "basic"; Ornek: "full"]
- panel_url (string, opsiyonel, null olabilir)
Yalniz management=basic — musterinin giris yapacagi panel adresi.
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Kaynak bulunamadi (veya bayiye gizli). Bayiler arasi envanter/kaynak
sizintisini onlemek icin "yok" ve "erisilemez" tek yanitla doner.
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "not_found",
"message": "Aradiginiz kaynak bulunamadi."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
------------------------------------------------------------------------------
GET /services/{id}/os-templates
Bu VPS icin rebuild'e izinli OS sablonlari (servis-spesifik filtre)
------------------------------------------------------------------------------
operationId: listServiceOsTemplates
Kimlik dogrulama: BasicAuth
VPS rebuild islemi icin secim listesi. **ENH-001 (live test 2026-05-19):**
liste artik servis-spesifik — Virtualizor `act=rebuild&vpsid=X` form-load
cagrisi ile gelen `ostemplates` field'i parse edilir. Bu, Virtualizor'da
VPS'e atanmis medya group'una gore filtrelenmis listedir. Onceden global
liste (`act=ostemplates`, 255+ OS) doniyordu ve bayi yanlis OS secip
rebuild yapinca Virtualizor "The OS you submitted is invalid" hatasi
veriyordu. Artik sadece izinli OS'lar gosterilir.
**NOT:** Ilk siparis (`POST /orders`) bu endpoint'i KULLANMAZ. Provisioning
sirasinda WHMCS Virtualizor modulu kendi config options'tan OS atar. Bu
endpoint sadece rebuild flow'u icindir.
**Cache:** APCu, key `os_templates:v2:vps:{vpsId}` (per-VPS), TTL 300 saniye.
Farkli VPS'ler farkli medya grupta olabildigi icin global key COZUM DEGIL.
Ayni medya gruptaki 2 VPS bu cache'ten faydalanmaz — pratik etki sifir
(OS listesi ayda bir guncellenir).
**Cache invalidation:** Operator yeni OS image ekledikten sonra max 5dk
icinde gorunur. Acil invalidate icin admin CLI hook (Faz 4) gelecek.
**Rate limit:** Default bucket (60 req/dk).
**management=basic:** `409 management_unavailable`.
**vps_id null durumu:** 422 `service_not_provisioned` doner — provisioning
bekleyen servis icin rebuild zaten yapilamaz.
Parametreler:
- id (path, integer, zorunlu) [min 1]
Yanitlar:
200 — OS sablon listesi
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (OsTemplateList, opsiyonel)
Ornek:
{
"success": true,
"data": {
"templates": [
{
"id": 270,
"name": "CentOS Stream 9",
"virt_type": "kvm"
},
{
"id": 280,
"name": "Debian 12",
"virt_type": "kvm"
},
{
"id": 290,
"name": "Ubuntu 24.04",
"virt_type": "kvm"
}
]
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Kaynak bulunamadi (veya bayiye gizli). Bayiler arasi envanter/kaynak
sizintisini onlemek icin "yok" ve "erisilemez" tek yanitla doner.
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "not_found",
"message": "Aradiginiz kaynak bulunamadi."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
422 — VPS henuz provisioning bekleniyor (vps_id null). Rebuild yapilamayacagi
icin OS listesi de cekilemez. Bayi modulu provisioning tamamlanana kadar
"OS listesi bekleniyor" mesaji gostermeli.
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "service_not_provisioned",
"message": "Bu servis henuz olusturulmamis.",
"details": {
"service_id": 7777
}
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
POST /services/{id}/power/{action}
VPS power islemi (start/stop/restart/poweroff)
------------------------------------------------------------------------------
operationId: powerService
Kimlik dogrulama: BasicAuth
Bayi VPS'inin guc durumunu degistirir. `action` URL parametresinde
`start`, `stop`, `restart` veya `poweroff` kabul edilir; baska deger
Slim route regex'inde 404 doner.
**Idempotency-Key ZORUNLU.** Header eksikse `400 missing_idempotency_key`
doner. Power aksiyonlari double-click korumasi icin idempotent
islenmelidir; bayi modulu HER TIKLAMADA YENI bir UUID v4 uretmeli.
Ayni key + ayni body 24 saat boyunca cached response replay eder
(`X-Idempotent-Replay: true` header'i ile).
**Rate limit:** `services_changes` bucket (120 req/dk).
**vps_id null:** Servis henuz Virtualizor'a baglanmamis (`tblcustomfieldsvalues`
bos veya tanimsiz) ise `422 service_not_provisioned` doner (`management=basic`
hizmette `409 management_unavailable`) ve Virtualizor'a
HIC cagri yapilmaz.
Parametreler:
- id (path, integer, zorunlu) [min 1]
- action (path, string, zorunlu) [degerler: "start" | "stop" | "restart" | "poweroff"]
- Idempotency-Key (header, string, zorunlu) [maxLength 64]
Her tiklamada bayi modulunun urettigi YENI UUID v4 (RFC 4122).
Tekrar gonderilen ayni key cached response doner.
Yanitlar:
200 — Power aksiyonu islendi
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye) | X-Idempotent-Replay: Cache hit replay ise `true`. Yeni isleme `header gonderilmez`.
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (PowerActionResult, opsiyonel)
Ornek:
{
"success": true,
"data": {
"status": "power_action_executed",
"action": "start",
"vps_id": 12345,
"executed_at": "2026-04-25T06:01:00+00:00"
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
400 — Idempotency-Key eksik veya gecersiz power action
Ornek — missing_idempotency_key
{
"success": false,
"error": {
"code": "missing_idempotency_key",
"message": "POST /services/{id}/power/{action} icin Idempotency-Key header zorunludur.",
"details": {
"header": "Idempotency-Key",
"endpoint": "POST /services/{id}/power/{action}"
}
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
422 — VPS henuz provisionlanmamis (vps_id custom field bos)
Ornek:
{
"success": false,
"error": {
"code": "service_not_provisioned",
"message": "VPS not yet provisioned or vpsid custom field missing",
"details": {
"service_id": 12345
}
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
502 — Virtualizor upstream hatasi
------------------------------------------------------------------------------
POST /services/{id}/rebuild
VPS OS reinstall (asenkron, 202 Accepted)
------------------------------------------------------------------------------
operationId: rebuildService
Kimlik dogrulama: BasicAuth
VPS'i secilen OS template'i ile yeniden kurar. Disk uzerindeki TUM veri
silinir. Asenkron job — Virtualizor `rebuildvps` islemi 5-30 dakika
surer; API hemen 202 Accepted doner ve bayi modulu sonra
`GET /services/{id}/status` ile takip eder.
**GUVENLIK:** Body'de `confirm: true` (strict bool) ZORUNLU. `false`
veya eksikse `400 confirmation_required` doner ve Virtualizor'a HIC
cagri yapilmaz (client-side guard).
**Idempotency-Key ZORUNLU.**
**Rate limit:** `services_changes` bucket.
**vps_id null:** `422 service_not_provisioned`.
**management=basic:** `409 management_unavailable`.
Parametreler:
- id (path, integer, zorunlu) [min 1]
- Idempotency-Key (header, string, zorunlu) [maxLength 64]
Istek govdesi (application/json, zorunlu):
- os_id (integer, zorunlu) [min 1]
Hedef OS template ID. `/services/{id}/os-templates` listesinden secilir.
- confirm (boolean, zorunlu) [degerler: true]
Veri silinecegi onayi. `true` ZORUNLU; `false` veya eksikse
`400 confirmation_required` doner. Strict bool — `1`/`'true'` kabul edilmez.
- root_password (string, opsiyonel) [minLength 8; maxLength 64]
**Opsiyonel** yeni root parolasi. Bayi modulu WHMCS'ten gelen
veya kullanici UI'sinde belirledigi parolayi gonderebilir.
Gonderilmezse API guvenli (16 char, alfanumerik) bir parola
UREITR ve response'taki `data.root_password` alaninda 1 kez
doner. Bayi bu degeri kullaniciya GOSTERMELIDIR — sonradan
geri alinamaz (Virtualizor saklamaz).
**Karakter sinirlamalari:**
- Sadece ASCII yazdirilabilir (0x20-0x7E)
- `'`, `"`, `` ` ``, `\\`, `$` karakterleri YASAK
(shell/quote escape riski).
Yanitlar:
202 — Rebuild scheduled (asenkron job baslatildi)
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye) | X-Idempotent-Replay: Cache hit replay ise `true`.
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (RebuildScheduledResult, opsiyonel)
Ornek:
{
"success": true,
"data": {
"status": "rebuild_scheduled",
"os_id": 270,
"vps_id": 12345,
"estimated_duration_seconds": 600,
"root_password": "Xkj9aF2pBm7nQrV3"
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
400 — Idempotency-Key eksik / confirm=false / body validasyon hatasi
Ornek — confirmation_required
{
"success": false,
"error": {
"code": "confirmation_required",
"message": "VPS kurtarma islemi veri kaybina yol acar. Devam etmek icin `confirm: true` parametresini gondermelisiniz.",
"details": {
"vps_id": 12345,
"os_id": 270
}
}
}
Ornek — missing_idempotency_key
{
"success": false,
"error": {
"code": "missing_idempotency_key",
"message": "POST /services/{id}/rebuild icin Idempotency-Key header zorunludur."
}
}
Ornek — invalid_rebuild_request
{
"success": false,
"error": {
"code": "invalid_rebuild_request",
"message": "`os_id` must be a positive integer."
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
422 — VPS provisionlanmamis VEYA secilen os_id bu VPS'in media grubunda izinli degil.
**os_not_allowed (guvenlik):** GET /os-templates listesi media-grup-filtreli
olsa da rebuild action'i server-side dogrulanir — listede olmayan bir os_id
(forge) gonderilirse Virtualizor'a HIC gidilmez, 422 doner.
Ornek — service_not_provisioned
{
"success": false,
"error": {
"code": "service_not_provisioned",
"message": "VPS not yet provisioned or vpsid custom field missing",
"details": {
"service_id": 12345
}
}
}
Ornek — os_not_allowed
{
"success": false,
"error": {
"code": "os_not_allowed",
"message": "Secilen isletim sistemi bu sunucu icin kullanilabilir degil.",
"details": {
"service_id": 12345,
"os_id": 999
}
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
502 — Virtualizor upstream hatasi
------------------------------------------------------------------------------
POST /services/{id}/vnc-token
Kisa omurlu (60s) NoVNC baglanti bilgisi
------------------------------------------------------------------------------
operationId: vncTokenService
Kimlik dogrulama: BasicAuth
Browser tarafinda direkt `wss://{host}:{port}/{websockify_path}` URL'sine
baglanmak icin kullanilir (port = Virtualizor enduser novnc proxy portu,
ornn. 4083; HAM VNC/RFB portu DEGIL). API HIC bu websocket akisina PROXY
YAPMAZ — sadece baglanti bilgisi (host/port/password/expires_at) doner.
**Idempotency-Key KABUL ETMEZ.** Header gelse bile IGNORE edilir
(route'a IdempotencyMiddleware bagli degil). Sebep: her cagri Virtualizor'da
YENI password uretir; cached eski password 60s sonra expire olur ve
bayi "sifre gecersiz" hatasi alirdi.
**Rate limit:** Default bucket (60 req/dk) — high-frequency degil.
**vps_id null:** `422 service_not_provisioned`.
**management=basic:** `409 management_unavailable`.
**Response:**
- `expires_at`: server'in hesapladigi UTC ISO8601 (now + 60s)
- `ttl_seconds`: 60 (sabit, client kolay parse etsin)
- `password`: short-lived NoVNC parolasi (LogMasker `vnc_password` alanini maskeler)
- `host`/`port`/`websockify_path`: bayi browser'inin baglanacagi node bilgisi
Parametreler:
- id (path, integer, zorunlu) [min 1]
Yanitlar:
200 — VNC baglanti bilgisi (60s gecerli)
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (VncTokenResult, opsiyonel)
Ornek:
{
"success": true,
"data": {
"vps_id": 12345,
"host": "node01.rabisu.com",
"port": 4083,
"websockify_path": "novnc/?virttoken=12345",
"password": "Xkj9_short_lived",
"expires_at": "2026-04-25T06:01:00+00:00",
"ttl_seconds": 60
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
422 — VPS henuz provisionlanmamis
Ornek:
{
"success": false,
"error": {
"code": "service_not_provisioned",
"message": "VPS not yet provisioned or vpsid custom field missing",
"details": {
"service_id": 12345
}
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
502 — Virtualizor upstream hatasi
------------------------------------------------------------------------------
POST /services/{id}/password
VPS root parolasini degistir
------------------------------------------------------------------------------
operationId: changeServicePassword
Kimlik dogrulama: BasicAuth
Virtualizor `act=editvs` round-trip ile root parolasini degistirir
(resmi WHMCS modulu deseni: once tam config GET, sonra `editvps=1` +
`rootpass` ile POST geri).
**ONEMLI:** Parolanin VPS'e ETKIN olmasi cogu sanallastirma tipinde
guc dongusu (kapat/ac) gerektirir. Bu endpoint SADECE Virtualizor'a
yeni parolayi yazar; bayi modulu degisiklik sonrasi `poweroff` + `start`
orkestrasyonunu kendisi yapar.
**Idempotency-Key:** OPSIYONEL (ayni parolayi tekrar yazmak zararsiz;
rebuild gibi destructive degil).
**Rate limit:** services_changes bucket.
**Guvenlik:** Parola NE LOG'LANIR NE DE response'ta dondurulur.
**vps_id null:** `422 service_not_provisioned`.
**management=basic:** `409 management_unavailable`.
Parametreler:
- id (path, integer, zorunlu) [min 1]
- Idempotency-Key (header, string(uuid), opsiyonel)
Opsiyonel — gelirse 24h replay korumasi.
Istek govdesi (application/json, zorunlu):
- password (string, zorunlu) [minLength 8; maxLength 64; Ornek: "YeniGuclu_Sifre2026"]
Yeni root parolasi. Sadece yazdirilabilir ASCII (0x20-0x7E),
tirnak/backtick/backslash/dollar ($ ' " ` \) yasak.
Yanitlar:
200 — Parola degistirildi (henuz guc dongusu gerekebilir)
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Ornek:
{
"success": true,
"data": {
"status": "password_changed",
"vps_id": 12345,
"changed_at": "2026-05-31T18:00:00+00:00"
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
400 — Gecersiz parola (uzunluk/karakter) veya body
Ornek:
{
"success": false,
"error": {
"code": "invalid_change_password_request",
"message": "`password` length must be between 8 and 64 characters.",
"details": {}
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
422 — VPS henuz provisionlanmamis
Ornek:
{
"success": false,
"error": {
"code": "service_not_provisioned",
"message": "VPS not yet provisioned or vpsid custom field missing",
"details": {
"service_id": 12345
}
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
502 — Virtualizor upstream hatasi (editvs done flag yok)
------------------------------------------------------------------------------
POST /services/{id}/suspend
Servisi WHMCS modul hook'u ile suspend et
------------------------------------------------------------------------------
operationId: suspendService
Kimlik dogrulama: BasicAuth
WhmcsApiClient::moduleSuspend cagirilir. WHMCS kendi virtualizor
modulunu tetikler, queue cron Virtualizor `suspendvs` job'una
cevirir. Bizim DB'de `mod_reseller_provisions.status='suspended'`
+ `notice='Suspended via API: '` set edilir.
**Idempotency-Key:** OPSIYONEL (header gelirse middleware uygula).
Suspend zaten idempotent (zaten suspended ise WHMCS no-op).
**Rate limit:** Default bucket (60 rpm) — lifecycle high-frequency degil.
**vps_id null:** `422 service_not_provisioned` (provision olmayan
servis suspend edilmez). `management=basic` hizmette vps_id aranmaz;
WHMCS modulu uygular.
**Body:** `{"reason": "..."}` opsiyonel (1-255 char). Default:
`"Suspended via Reseller API"`. Bayinin WHMCS'inde gosterilen
reason ile senkron olabilsin.
**Side effect:** `mod_reseller_webhooks_queue` tablosuna
`service.suspended` event'i (`status='skipped'`) yazilir. Faz 2
MVP'de gonderim YOK; v2 worker bu event'leri reseller webhook
URL'lerine push eder.
Parametreler:
- id (path, integer, zorunlu) [min 1]
- Idempotency-Key (header, string(uuid), opsiyonel)
Opsiyonel — gelirse 24h replay korumasi.
Istek govdesi (application/json):
- reason (string, opsiyonel) [minLength 1; maxLength 255; Ornek: "Non-payment"]
Ornek:
{
"reason": "Non-payment"
}
Yanitlar:
200 — Servis suspend edildi
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (SuspendResult, opsiyonel)
Ornek:
{
"success": true,
"data": {
"status": "suspended",
"service_id": 12345,
"vps_id": 67890,
"reason": "Non-payment",
"suspended_at": "2026-04-25T06:23:00+00:00"
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Servis bulunamadi
422 — VPS henuz provisionlanmamis
Ornek:
{
"success": false,
"error": {
"code": "service_not_provisioned",
"message": "VPS not yet provisioned or vpsid custom field missing",
"details": {
"service_id": 12345
}
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
502 — WHMCS API upstream hatasi (whmcs_upstream_error)
------------------------------------------------------------------------------
POST /services/{id}/unsuspend
Servisin suspend'ini WHMCS modul hook'u ile geri al
------------------------------------------------------------------------------
operationId: unsuspendService
Kimlik dogrulama: BasicAuth
WhmcsApiClient::moduleUnsuspend cagirilir. Bizim DB'de
`mod_reseller_provisions.status='active'` + `notice='Unsuspended via API'`
set edilir.
**Idempotency-Key:** OPSIYONEL.
**Rate limit:** Default bucket (60 rpm).
**vps_id null:** `422 service_not_provisioned`.
**management=basic:** vps_id aranmaz; WHMCS modulu uygular.
**Body:** Yok.
**Side effect:** `service.unsuspended` event'i (`status='skipped'`)
webhook queue'ya yazilir.
Parametreler:
- id (path, integer, zorunlu) [min 1]
- Idempotency-Key (header, string(uuid), opsiyonel)
Yanitlar:
200 — Servis tekrar aktif
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (UnsuspendResult, opsiyonel)
Ornek:
{
"success": true,
"data": {
"status": "active",
"service_id": 12345,
"vps_id": 67890,
"unsuspended_at": "2026-04-25T06:24:00+00:00"
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Servis bulunamadi
422 — VPS henuz provisionlanmamis
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
502 — WHMCS API upstream hatasi
------------------------------------------------------------------------------
POST /services/{id}/upgrade
Servisin config option'larini yukselt (upgrade)
------------------------------------------------------------------------------
operationId: upgradeService
Kimlik dogrulama: BasicAuth
Servisin configurable option'larini (RAM/CPU/disk gibi `resource` rolu
gruplar) yeni degerlere yukseltir. WHMCS `UpgradeProduct` action'i
`type=configoptions` ile cagirilir; prorata farki WHMCS tarafinda
hesaplanir.
**Iki mod:**
- `quote_only=true` → WHMCS `calconly=1` ile SADECE fiyat teklifi doner
(order/invoice olusmaz, `quote=true`).
- `quote_only=false` (default) → gercek upgrade uygulanir. Fark faturasi
olusur, bayi kredisi otomatik uygulanir (OrderService ApplyCredit
deseniyle ayni: `pending_provision` / `pending_payment` /
`payment_failed`).
**Kilit bekci:** Yalniz `role=resource` gruplari (RAM/CPU/disk) upgrade ile
degistirilebilir. Diger gruplar (`fixed`, eski `location` veya metadata'si
olmayan → guvenli varsayilan kilitli) DEGISTIRILEMEZ; bu gruplardaki bir
secim degistirilirse `422 location_locked` doner. Ayni degerde birakmak
gecerlidir.
**Tam config (merge):** WHMCS'e her zaman TAM config gonderilir (servisin
mevcut tum secimleri + bayinin delta'lari). WHMCS gonderilmeyen bir config
option'i urunun default (1.) degerine cektigi icin (yanlis lokasyon +
yanlis prorata olurdu), lokasyon dahil hicbir secim resetlenmez.
**Downgrade yasak:** WHMCS negatif prorata (kredi/iade) donerse
`409 downgrade_not_allowed` doner.
**Cakisma:** Servis icin odenmemis onceki bir upgrade faturasi varsa
veya WHMCS `upgradeinprogress` bildirirse `409 upgrade_conflict`.
**Idempotency-Key:** ZORUNLU (POST — 24h replay korumasi).
**Rate limit:** `services_changes` bucket (120 rpm).
**Body:** `config_options` = `{ "": }`
(bos olamaz, int=>int). `quote_only` opsiyonel bool (default false).
Parametreler:
- id (path, integer, zorunlu) [min 1]
- Idempotency-Key (header, string(uuid), zorunlu)
ZORUNLU — 24h replay korumasi.
Istek govdesi (application/json, zorunlu):
- config_options (object, zorunlu)
WHMCS config option ID → secim/adet map'i (bos olamaz).
- quote_only (boolean, opsiyonel) [varsayilan: false]
true ise sadece fiyat teklifi (calconly), upgrade uygulanmaz.
Ornek:
{
"config_options": {
"10": 4,
"11": 2048
},
"quote_only": false
}
Yanitlar:
200 — Upgrade uygulandi veya teklif dondu (quote_only'e gore)
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (UpgradeServiceResult, opsiyonel)
Ornek — quote: Sadece teklif (quote_only=true)
{
"success": true,
"data": {
"service_id": 12345,
"quote": true,
"currency": "TRY",
"price": "TRY 45,00",
"price_amount": 45,
"days_until_renewal": 18,
"config_options": {
"10": 4,
"11": 2048
},
"order_id": null,
"invoice_id": null,
"status": null,
"payment_applied": false,
"balance_after": null,
"notice": null
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
Ornek — applied: Gercek upgrade (kredi uygulandi)
{
"success": true,
"data": {
"service_id": 12345,
"quote": false,
"currency": "TRY",
"price": "TRY 45,00",
"price_amount": 45,
"days_until_renewal": 18,
"config_options": {
"10": 4,
"11": 2048
},
"order_id": 9310,
"invoice_id": 8410,
"status": "pending_provision",
"payment_applied": true,
"balance_after": 155,
"notice": null
},
"meta": {
"trace_id": "...",
"timestamp": "..."
}
}
400 — Gecersiz upgrade istegi (invalid_upgrade_request) veya Idempotency-Key eksik
Ornek:
{
"success": false,
"error": {
"code": "invalid_upgrade_request",
"message": "config_options bos olamaz",
"details": {}
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
403 — Yetki yetersiz — bayi baska bir bayinin service_id'sine erismeye calisti
veya admin scope gerektiren endpoint icin yetersiz scope. Mesaj generic
tutulur (ServiceOwnershipMiddleware sizinti onler).
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "forbidden",
"message": "Bu kaynaga erisim yetkiniz yok."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Servis bulunamadi
409 — Downgrade yasak (downgrade_not_allowed) veya upgrade cakismasi (upgrade_conflict)
Ornek — downgrade
{
"success": false,
"error": {
"code": "downgrade_not_allowed",
"message": "Downgrade desteklenmiyor",
"details": {
"price": "TRY -12,00"
}
}
}
Ornek — conflict
{
"success": false,
"error": {
"code": "upgrade_conflict",
"message": "Servis icin odenmemis onceki upgrade faturasi mevcut",
"details": {}
}
}
422 — Kilitli config option grubu degistirilemez (location_locked)
Ornek:
{
"success": false,
"error": {
"code": "location_locked",
"message": "Bu config option grubu upgrade ile degistirilemez (lokasyon veya sabit grup)",
"details": {
"locked_group_ids": [
7
]
}
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
502 — WHMCS API upstream hatasi (whmcs_upstream_error)
==============================================================================
[Webhooks]
==============================================================================
Bayi webhook endpoint kayit yonetimi (Faz 3.W.3). Bayi, abone oldugu
event'ler gerceklestiginde (orn: service.suspended, product.price_changed)
kendi sunucusunda bir HTTP endpoint'ine imzali POST almak icin kayit yapar.
ABONELIK PATTERN'LERI (`event_types`):
- `*` → tum eventler (catch-all)
- `prefix.*` → namespace wildcard (service.*, product.*) — tek segment
- exact → tam event ismi (service.suspended)
GECERLI EVENT'LER (V1): service.created, service.suspended,
service.unsuspended, service.terminated, product.price_changed,
product.stock_changed, product.disabled, credit.added. Bilinmeyen event
tipi 422 doner.
`service.created` (Faz 3.W.4): siparis edilen VPS provisioning'i Rabisu
tarafinda tamamlanip aktif olunca gonderilir (WHMCS domainstatus 'Pending'
→ 'Active'; cron polling tespiti). Bayi bu event ile servisin hazir
oldugunu ANINDA ogrenir (GET /orders/{id} polling fallback olarak kalir).
Payload: {provision_id, service_id, order_id, product_id, vps_id,
status='active', occurred_at, schema_version}. `provision_id` = POST /orders
yanitindaki `id` (bayi korelasyon anahtari).
`credit.added` (Faz 3.B): bayiye kredi yuklendiginde (admin manuel kredi
veya fatura iadesi) gonderilir. Payload: {reseller_id, amount, currency,
reason (manual_credit|refund), description, related_invoice_id, new_balance,
occurred_at}. Bu event SADECE ilgili bayiye gider (broadcast degil).
Kaynak: WHMCS tblcredit polling (bin/credit-event-emitter.php).
NOT: apply_credit (siparis odemesi) bu event'i URETMEZ — o GET /auth/balance/movements
audit'ine OrderService tarafindan real-time yazilir.
SECRET: POST yanitindaki `secret` plain HMAC secret YALNIZCA bir kez
gosterilir. Kaybedilirse endpoint silinip yeniden olusturulmalidir.
IMZA: Dispatcher her teslimatta iki header ekler:
`X-Rabisu-Signature-V2: t=,v1=` — HMAC-SHA256(secret,
"{t}.{X-Rabisu-Event-Type}.{ham govde}"). Alici |now - t| <= 300 sn ve
imzali olay tipini dogrulamalidir (replay + tip degistirme korumasi).
`X-Rabisu-Signature: sha256=` — yalniz govde; eski alicilar icin,
replay korumasi YOK, yeni entegrasyonlarda kullanmayin.
CIRCUIT BREAKER: 5 ardisik basarisiz teslimat endpoint'i otomatik pasif
yapar (is_active=0). Bayi `PATCH {is_active: true}` ile reaktive eder.
------------------------------------------------------------------------------
GET /webhooks
Bayinin webhook kayitlarini listele
------------------------------------------------------------------------------
operationId: listWebhooks
Kimlik dogrulama: BasicAuth
Bayi'ye (api_key) ait TUM endpoint'leri doner — pasif (is_active=0,
circuit breaker ile kapanmis) olanlar dahil. Boylece bayi pasif
endpoint'i gorup PATCH ile reaktive edebilir.
Yanitlar:
200 — Webhook listesi
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (object, opsiyonel)
- items (array, zorunlu)
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
POST /webhooks
Yeni webhook endpoint kaydet
------------------------------------------------------------------------------
operationId: createWebhook
Kimlik dogrulama: BasicAuth
Bayi'nin kendi sunucusundaki bir HTTP endpoint'ini, secilen event'lere
abone ederek kaydeder. Yanitta donen `secret` plain HMAC secret YALNIZCA
bir kez gosterilir.
Hedef URL stricti: sadece `https`, kullanici bilgisi (user:pass@) yasak,
ozel/dahili IP'ler reddedilir (SSRF korumasi).
Istek govdesi (application/json, zorunlu):
→ bkz. sema: CreateWebhookRequest
Ornek:
{
"target_url": "https://hooks.bayi.com/rabisu",
"event_types": [
"service.suspended",
"product.*"
],
"description": "Uretim webhook"
}
Yanitlar:
201 — Endpoint olusturuldu (secret tek sefer doner)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (WebhookCreateResult, opsiyonel)
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
409 — Aktif webhook limiti asildi (webhook_limit_exceeded)
→ bkz. sema: ErrorResponse
422 — Validasyon hatasi (gecersiz URL / bilinmeyen event)
→ bkz. sema: ErrorResponse
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
GET /webhooks/{id}
Tek webhook detayi
------------------------------------------------------------------------------
operationId: getWebhook
Kimlik dogrulama: BasicAuth
Yanitlar:
200 — Webhook detayi
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (WebhookEndpoint, opsiyonel)
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Kayit yok veya baska bayinin (webhook_not_found)
→ bkz. sema: ErrorResponse
------------------------------------------------------------------------------
PATCH /webhooks/{id}
Webhook kismi guncelle (reaktive / event_types / aciklama)
------------------------------------------------------------------------------
operationId: updateWebhook
Kimlik dogrulama: BasicAuth
Gonderilmeyen alan degismez (partial update). `is_active: true`
gonderildiginde endpoint reaktive edilir ve `consecutive_failures`
sifirlanir (circuit breaker temiz baslangic). `event_types` gonderilirse
abonelik listesi TAMAMEN degistirilir (merge degil, replace).
Istek govdesi (application/json, zorunlu):
→ bkz. sema: UpdateWebhookRequest
Ornek:
{
"is_active": true,
"event_types": [
"product.price_changed",
"product.stock_changed"
]
}
Yanitlar:
200 — Guncellenmis webhook
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (WebhookEndpoint, opsiyonel)
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Kayit yok veya baska bayinin (webhook_not_found)
→ bkz. sema: ErrorResponse
422 — Validasyon hatasi (bos body / bilinmeyen event)
→ bkz. sema: ErrorResponse
------------------------------------------------------------------------------
DELETE /webhooks/{id}
Webhook sil (soft delete)
------------------------------------------------------------------------------
operationId: deleteWebhook
Kimlik dogrulama: BasicAuth
Soft delete — kayit silinmez, `is_active=0` yapilir (audit + circuit
breaker gecmisi korunur). Dispatcher bir daha bu endpoint'e teslimat
yapmaz.
Yanitlar:
200 — Silindi (soft)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (object, opsiyonel)
- id (integer, zorunlu) [Ornek: 12]
- deleted (boolean, zorunlu) [Ornek: true]
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Kayit yok veya baska bayinin (webhook_not_found)
→ bkz. sema: ErrorResponse
==============================================================================
[ModuleReleases]
==============================================================================
Bayi WHMCS modulu self-update. Modul en yeni surumu `GET .../latest` ile
sorar, `download_path` ile imzali zip'i indirir. Zip'i kurmadan once
`sha256` ve `signature` (zip baytlarinin ed25519 detached imzasi, base64)
modulun icindeki Rabisu public key ile dogrulanmalidir. Surum dosyalari
sunucuya elle konur; API yalniz okur.
------------------------------------------------------------------------------
GET /module-releases/{module}/latest
Modulun en yeni gecerli surumu
------------------------------------------------------------------------------
operationId: getLatestModuleRelease
Kimlik dogrulama: BasicAuth
`MODULE_RELEASES_DIR/{module}/` altindaki gecerli manifestler arasinda
`version_compare` ile en yuksek surumu doner (0.6.10 > 0.6.9). Gecersiz
manifest (bozuk JSON, surum/modul uyusmazligi, guvensiz dosya adi, eksik
zip, boyut/sha256/imza hatasi) atlanir. Gecerli surum yoksa 404
`release_not_found`.
Default rate limit kovasi (`default`, 60/dk).
Parametreler:
- module (path, string, zorunlu) [pattern ^[a-z0-9-]{1,40}$]
Modul kimligi (dizin adi), orn. `bayi-whmcs`
Yanitlar:
200 — En yeni surum ozeti
Yanit basliklari: X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (ModuleRelease, opsiyonel)
Ornek:
{
"success": true,
"data": {
"module": "bayi-whmcs",
"version": "0.6.6",
"released_at": "2026-10-04",
"notes": "- Sunucunuz hazir e-postasi\n- Webhook secret gizli alan",
"size": 123456,
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"signature": "x0Jb3F...base64 (64 bayt ed25519 imza)...Ag==",
"download_path": "/api/v1/module-releases/bayi-whmcs/0.6.6/download"
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-10-04T12:00:00Z"
}
}
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Gecerli surum yok (`release_not_found`) — modul dizini yok, manifest yok
veya manifest gecersiz. Sebep public yanitta ayrilmaz.
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "release_not_found",
"message": "Modul surumu bulunamadi.",
"details": {
"module": "bayi-whmcs",
"version": null
}
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-10-04T12:00:00Z"
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
------------------------------------------------------------------------------
GET /module-releases/{module}/{version}/download
Imzali modul zip'ini indir
------------------------------------------------------------------------------
operationId: downloadModuleRelease
Kimlik dogrulama: BasicAuth
Manifestteki zip'i binary stream olarak doner (JSON zarfi YOK). Istemci
govdenin SHA-256 ozetini `X-Content-SHA256` / `latest.sha256` ile ve
ed25519 imzasini `latest.signature` ile dogrulamadan kurulum yapmamalidir.
Surum yok veya manifest gecersiz → 404 `release_not_found` (JSON).
Default rate limit kovasi (`default`, 60/dk).
Parametreler:
- module (path, string, zorunlu) [pattern ^[a-z0-9-]{1,40}$]
Modul kimligi (dizin adi), orn. `bayi-whmcs`
- version (path, string, zorunlu) [pattern ^[0-9]+\.[0-9]+\.[0-9]+$]
Surum (`MAJOR.MINOR.PATCH`)
Yanitlar:
200 — Zip dosyasi
Yanit basliklari: Content-Length: Zip boyutu (bayt) | Content-Disposition: Indirme dosya adi | X-Content-SHA256: Zip baytlarinin SHA-256 ozeti (kucuk harf hex) | Cache-Control | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining: Mevcut window'da kalan istek sayisi | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
(application/zip)
string(binary)
401 — Kimlik dogrulama basarisiz — eksik header, hatali credential, IP whitelist
disi veya revoke edilmis key. Detay sizdirilmaz; tum durumlar tek mesaj.
Yanit basliklari: WWW-Authenticate: RFC 7617 Basic Auth challenge
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "unauthorized",
"message": "Kimlik dogrulama basarisiz."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-25T01:00:00Z"
}
}
404 — Gecerli surum yok (`release_not_found`) — modul dizini yok, manifest yok
veya manifest gecersiz. Sebep public yanitta ayrilmaz.
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "release_not_found",
"message": "Modul surumu bulunamadi.",
"details": {
"module": "bayi-whmcs",
"version": null
}
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-10-04T12:00:00Z"
}
}
429 — Dakika basina limit asildi
Yanit basliklari: Retry-After: 429 sonrasi tekrar denemeden once beklenmesi gereken saniye (RFC 7231 §7.1.3) | X-RateLimit-Limit: Bu key icin tanimli dakika basi limit | X-RateLimit-Remaining | X-RateLimit-Reset: Sonraki window basinin Unix epoch'u (UTC saniye)
→ bkz. sema: ErrorResponse
Ornek:
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Dakika basina maksimum 60 istek limiti asildi. Lutfen 12 saniye sonra tekrar deneyin."
},
"meta": {
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-04-24T12:00:00Z"
}
}
==============================================================================
[Admin]
==============================================================================
Server-to-server admin kanali (`X-Admin-Token`, BasicAuth DEGIL). Rabisu WHMCS
master modulu, bir musteri "Bayi ol" dediginde bu endpoint'lerle o musteri icin
API key uretir/listeler. Bayi API key'leri ile erisilemez.
------------------------------------------------------------------------------
GET /admin/keys
Bir bayinin aktif API key'lerini listele (secret YOK)
------------------------------------------------------------------------------
operationId: adminListKeys
Kimlik dogrulama: AdminToken
Belirtilen `reseller_id` icin aktif (is_active=1) API key'leri doner.
Secret hash veya plaintext ASLA donmez — yalnizca metadata.
Parametreler:
- reseller_id (query, integer, zorunlu)
WHMCS `tblclients.id`.
Yanitlar:
200 — Aktif key listesi
Asagidakilerin birlesimi (allOf):
→ bkz. sema: ApiResponse
- data (object, opsiyonel)
- reseller_id (integer, zorunlu) [Ornek: 42]
- count (integer, zorunlu) [Ornek: 1]
- keys (array