Skip to content

Görevler ve webhook'lar ​

Medya üretimi (video, görsel, müzik) asenkron çalışır: bir görev (job) oluşturur ve hazır olduğunda sonucu alırsınız. Görevin tamamlandığını öğrenmenin iki yolu vardır: sorgulama (polling) ve webhook (push). Aşağıda görev yapısı ve sonucu güvenilir biçimde alma yöntemleri açıklanmaktadır.

Görev yaşam döngüsü ​

POST /media/generate  →  status: processing  ──►  done   (result_url hazır)
        │                                     └─►  failed (error, tutar iade edildi)
        └── (isteğe bağlı) callback_url → done/failed durumunda push webhook
  1. Oluşturma. Model ve parametrelerle POST /media/generate isteği gönderilir; yanıt olarak id ve sorgulama bağlantısı döner:
json
{ "id": "beefb531-…", "model": "image/nano-banana-2", "status": "processing",
  "poll": "https://nordrouter.com/media/job/beefb531-…" }
  1. Bekleme. Görev processing → done (veya failed) durumlarından geçer. Görseller saniyeler içinde, video ve müzik ise onlarca saniye ile birkaç dakika arasında tamamlanır.

  2. Sonuç. Tamamlanan görevin result_url alanı doludur (nordrouter.com üzerindeki bağlantı); dosya sınırlı bir süre saklanır (GET /media/models yanıtındaki retention_days) — kalıcı olarak saklamak için indirin.

GET /media/job/:id yanıtının (ve webhook gövdesinin — aşağıya bakın) biçimi aynıdır:

json
{ "id": "beefb531-…", "status": "done",
  "result_url": "https://nordrouter.com/media/file/beefb531-…",
  "cost_usd": 0.052, "error": null }
  • status — processing | done | failed.
  • result_url — yalnızca done durumunda doludur, aksi halde null.
  • result_url_2 — müzik modellerinde (Suno) ikinci parça, aksi halde null.
  • results — görevin tüm dosyaları: [{ "index": 1, "name": "Результат 1", "ext": "png", "url": "…" }] (3D için model ve önizleme, parça ayırma işleminde «Vokal», «Enstrümantal»… kanalları).
  • cost_usd — gerçekleşen maliyet (işlem sonucuna göre tahsil edilir; başarısız üretimler ücretlendirilmez — ayrılan tutar iade edilir).
  • error — failed durumunda hata metni, aksi halde null.

Yöntem 1. Sorgulama (polling) ​

En basit yol, status değeri done/failed olana kadar düzenli aralıklarla GET /media/job/:id isteği göndermektir:

bash
curl https://nordrouter.com/media/job/beefb531-…

2–3 saniyede bir sorgulayın. Sorgulama her zaman çalışır ve herkese açık bir adres gerektirmez — betikler, notebook'lar ve CI için uygundur.

2. Yöntem: Webhook’lar (push) ​

Sürekli sorgulama yapmamak için POST /media/generate isteğinde callback_url gönderin. Görev tamamlandığında adresinize POST isteği göndeririz:

bash
curl https://nordrouter.com/media/generate \
  -H "Authorization: Bearer sk-nr-YOUR-KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "image/nano-banana-2",
    "input": { "prompt": "…" },
    "callback_url": "https://your-server.com/webhooks/nordrouter",
    "callback_secret": "sizin-belirleyeceğiniz-herhangi-bir-dize"
  }'

callback_url kabul edilirse, oluşturma yanıtında "webhook": "registered" yer alır. callback_url, görev oluşturulurken hemen doğrulanır: uygun değilse (https değilse veya özel/yerel bir adresse) istek, nedeni belirten 400 hatası döndürür ({ "error": "callback_url reddedildi: …", "code": "invalid_callback" }); görev oluşturulmaz.

Webhook gövdesi ​

Görev done/failed durumuna ulaştığında, callback_url adresinize GET /media/job/:id ile aynı gövde ve ek olarak event alanını içeren bir POST gönderilir:

json
{ "event": "media.completed", "id": "beefb531-…", "status": "done",
  "result_url": "https://nordrouter.com/media/file/beefb531-…",
  "cost_usd": 0.052, "error": null }
  • event: media.completed (başarılı) veya media.failed (hata).

İmza doğrulama ​

callback_secret belirlediyseniz, X-NR-Signature başlığı sha256=<hmac> içerir. Bu, anahtar olarak sırrınız kullanılarak ham istek gövdesi üzerinden hesaplanan HMAC-SHA256 değeridir. İsteğin sahte olmadığını ve bizden geldiğini doğrulamak için imzayı kontrol edin:

python
import hmac, hashlib

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header)  # sabit zamanlı karşılaştırma
javascript
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}

JSON ayrıştırmasından önce, HMAC’i geldiği hâliyle tam olarak aynı baytlar üzerinden hesaplayın. Aksi takdirde imza eşleşmez.

10 satırda hazır webhook alıcısı ​

Webhook’u alan, imzayı doğrulayan ve 2xx yanıtı döndüren çalışan bir sunucu. Sır, callback_secret için gönderdiğiniz değerle aynı olmalıdır.

Node.js / Express:

javascript
import express from "express";
import crypto from "node:crypto";

const SECRET = "sizin-belirleyeceğiniz-herhangi-bir-dize";           // = callback_secret değeriniz
const app = express();

app.post("/webhooks/nordrouter",
  express.raw({ type: "application/json" }),   // ham gövde — imza için zorunlu
  (req, res) => {
    const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
    const got = req.headers["x-nr-signature"] || "";
    if (expected.length !== got.length || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got)))
      return res.sendStatus(401);             // imza eşleşmedi — istek bizden değil
    const job = JSON.parse(req.body);
    if (job.event === "media.completed") console.log("tamamlandı:", job.result_url, "$" + job.cost_usd);
    res.sendStatus(200);                       // 2xx yanıtı verin, aksi hâlde tekrar gönderilir
  });

app.listen(3000);

Python / FastAPI:

python
import hmac, hashlib
from fastapi import FastAPI, Request, Response

SECRET = b"sizin-belirleyeceğiniz-herhangi-bir-dize"                  # = callback_secret değeriniz
app = FastAPI()

@app.post("/webhooks/nordrouter")
async def hook(request: Request):
    raw = await request.body()                 # ham baytlar — imza için zorunlu
    expected = "sha256=" + hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, request.headers.get("X-NR-Signature", "")):
        return Response(status_code=401)       # imza eşleşmedi — istek bizden değil
    job = await request.json()
    if job["event"] == "media.completed":
        print("tamamlandı:", job["result_url"], "$", job["cost_usd"])
    return Response(status_code=200)           # 2xx yanıtı verin, aksi hâlde tekrar gönderilir

Yerel ortamda test

callback_url herkese açık bir https adresi olmalıdır. Dizüstü bilgisayarda geliştirme yapmak için bir tünel açın; örneğin ngrok http 3000 komutunu çalıştırın ve oluşturulan https adresini callback_url olarak gönderin.

Teslimat ve güvenilirlik ​

  • En fazla 4 deneme; bekleme süresi giderek artar (≈3 → 6 → 9 sn), her denemenin zaman aşımı 15 sn’dir. Aksi hâlde tekrar gönderileceğinden 2xx yanıtı verin.
  • Yönlendirmeler izlenmez. İsteği doğrudan callback_url adresinde yanıtlayın.
  • Teslimat best-effort esasına dayanır: 4 denemenin tamamı başarısız olursa (sunucunuz çalışmıyorsa) webhook daha sonra yeniden gönderilmez. Bu nedenle:

Webhook’lar sorgulamanın yerini tutmaz

Webhook, garantili teslimat değil, daha hızlı bildirim sağlar. Yedek olarak GET /media/job/:id kullanmaya devam edin: push bildirimi makul bir süre içinde gelmezse durumu kendiniz sorgulayın. İdempotensi sizin sorumluluğunuzdadır: aynı görev hem webhook ile gelebilir hem de sorgulama sırasında görülebilir; id değerini esas alın.

callback_url gereksinimleri ​

  • Yalnızca https:// adresleri kabul edilir.
  • Yalnızca herkese açık adresler kabul edilir. Yerel ve özel adresler (localhost, *.local, 10.x, 172.16–31.x, 192.168.x, 127.x, 169.254.x, 100.64–127.x, IPv6 loopback/ULA/link-local) reddedilir. Bu, SSRF saldırılarına karşı bir korumadır. Adres, her gönderimden önce de yeniden doğrulanır (DNS rebinding koruması).

Hangisini seçmeli? ​

SorgulamaWebhook
Herkese açık https adresigerekmezgerekir
Bildirimin ulaşma gecikmesisorgulama aralığına kadarneredeyse anında
Garantikontrol sizdebest-effort + yedek olarak sorgulama
Uygun olduğu yerlerbetikler, CI, dizüstü bilgisayarsunucular, üretim arka uçları

Öneri: Arka uçta webhook’u yedek olarak sorgulamayla birlikte kullanın; betiklerde ve dizüstü bilgisayarda yalnızca sorgulama yapın.