Jobs और webhooks
Media generation asynchronous है: job ID लेकर poll करें या callback_url दें। Webhook signature source की पुष्टि करता है।
- Status queued और processing से completed या failed होता है।
- Polling सरल है; webhook के लिए public HTTPS receiver चाहिए।
- Duplicate delivery स्वीकारें, signature जाँचें और job ID से idempotent processing करें।
पूरे उदाहरण
मूल गाइड के सभी चलने योग्य उदाहरण नीचे सुरक्षित रखे गए हैं। केवल अपने टूल और ऑपरेटिंग सिस्टम से संबंधित ब्लॉक चलाएँ।
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 |
पहले न्यूनतम text request से कनेक्शन जाँचें, फिर streaming, tool calls, images और दूसरी advanced सुविधाएँ जोड़ें। API key को public code या browser frontend में कभी न रखें।