Job و webhook
ساخت رسانه غیرهمزمان است: job ID میگیرید و وضعیت را poll میکنید یا callback_url میدهید. امضای webhook منبع را تأیید میکند.
- وضعیت از queued و processing به completed یا failed میرسد.
- Polling ساده است؛ webhook به گیرندهٔ عمومی HTTPS نیاز دارد.
- ارسال تکراری را بپذیرید، امضا را بررسی و job ID را idempotent پردازش کنید.
نمونههای کامل
همهٔ نمونههای قابل اجرای راهنمای اصلی در ادامه حفظ شدهاند. فقط بلوک متناسب با ابزار و سیستمعامل خود را اجرا کنید.
POST /media/generate → status: processing ──► done (result_url ready)
│ └─► failed (error, funds refunded)
└── (optional) callback_url → push webhook on done/failedjson
{ "id": "beefb531-…", "model": "image/nano-banana-2", "status": "processing",
"poll": "https://nordrouter.com/media/job/beefb531-…" }json
{ "id": "beefb531-…", "status": "done",
"result_url": "https://nordrouter.com/media/file/beefb531-…",
"cost_usd": 0.052, "error": null }bash
curl https://nordrouter.com/media/job/beefb531-…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": "any-string-of-yours"
}'json
{ "event": "media.completed", "id": "beefb531-…", "status": "done",
"result_url": "https://nordrouter.com/media/file/beefb531-…",
"cost_usd": 0.052, "error": null }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) # constant-time comparejavascript
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));
}javascript
import express from "express";
import crypto from "node:crypto";
const SECRET = "any-string-of-yours"; // = your callback_secret
const app = express();
app.post("/webhooks/nordrouter",
express.raw({ type: "application/json" }), // raw body — required for the signature
(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); // signature mismatch — not from us
const job = JSON.parse(req.body);
if (job.event === "media.completed") console.log("done:", job.result_url, "$" + job.cost_usd);
res.sendStatus(200); // reply 2xx or we retry
});
app.listen(3000);python
import hmac, hashlib
from fastapi import FastAPI, Request, Response
SECRET = b"any-string-of-yours" # = your callback_secret
app = FastAPI()
@app.post("/webhooks/nordrouter")
async def hook(request: Request):
raw = await request.body() # raw bytes — required for the signature
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) # signature mismatch — not from us
job = await request.json()
if job["event"] == "media.completed":
print("done:", job["result_url"], "$", job["cost_usd"])
return Response(status_code=200) # reply 2xx or we retryمستندات رسمی و پیوندها
| Polling | Webhook | |
|---|---|---|
Public https address | not needed | required |
| Latency | up to poll interval | near-instant |
| Guarantee | you control it | best-effort + polling backup |
| Best for | scripts, CI, laptop | servers, production backends |
ابتدا اتصال را با یک درخواست متنی کوچک بررسی کنید؛ سپس streaming، فراخوانی ابزار، تصویر و قابلیتهای پیشرفته را اضافه کنید. کلید API را در کد عمومی یا frontend مرورگر قرار ندهید.