Webhooks
When an async inference request finishes, Meshive can POST the result to your server instead of making you poll. Every delivery is signed so you can verify it came from Meshive.
Two ways to receive events
Section titled “Two ways to receive events”There is one delivery engine with two ways to register where events go:
| Method | Where you set it | Best for |
|---|---|---|
| Inline callback | webhook_url field on a single async request (API or Playground) | “Tell me when this job is done.” |
| Subscription endpoint | Serverless › Webhooks in the console — register a URL and pick event types | ”Send all matching events for this workspace here.” |
Both methods use the same signature, payload schema, and retry behavior. If an inline webhook_url and a subscription endpoint resolve to the same URL for the same event, you receive the event once (deduplicated).
Inline callback (per request)
Section titled “Inline callback (per request)”Add a webhook_url to any async image or video request:
curl "https://<your-deployment>.inference.meshive.ai/v1/images/generations?async=true" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $MESHIVE_API_KEY" \ -d '{ "model": "<model-id>", "prompt": "A sunset over the ocean", "size": "1024x1024", "webhook_url": "https://your-server.com/webhooks/meshive" }'import httpx
resp = httpx.post( "https://<your-deployment>.inference.meshive.ai/v1/images/generations", params={"async": "true"}, headers={"Authorization": f"Bearer {MESHIVE_API_KEY}"}, json={ "model": "<model-id>", "prompt": "A sunset over the ocean", "size": "1024x1024", "webhook_url": "https://your-server.com/webhooks/meshive", },)# → 202 {"id": "req_...", "status": "queued"}. Your endpoint is called on completion.The URL must be HTTPS and resolve to a public address — see Security. Invalid URLs are rejected at submit time with HTTP 422.
Subscription endpoint (console)
Section titled “Subscription endpoint (console)”-
Open Serverless › Webhooks in your workspace console.
-
Click Add endpoint, paste your HTTPS URL, and select which events to receive (default: all).
-
Use Test to send a sample event and confirm your server responds with a
2xx.
Subscription endpoints receive every matching event for the workspace, so you don’t have to set webhook_url on each request. You can enable/disable them and inspect deliveries (status, attempts, and the exact body sent) under Deliveries.
Event types
Section titled “Event types”| Type | When |
|---|---|
request.completed | An async request finished successfully. Result is in output_object_key / result_payload. |
request.failed | The request failed. Inspect error_code / error_message. |
request.canceled | The request was canceled (by you or by the sweeper). |
Terminal states only — there are no intermediate-progress events. To observe progress, poll GET /v1/requests/{id}.
Payload
Section titled “Payload”Every delivery has a JSON body with this envelope:
{ "id": "evt_3f9c1a2b4d5e6f70", "type": "request.completed", "created_at": 1780000000, "data": { "id": "req_abc123", "status": "succeeded", "modality": "image", "namespace_name": "my-workspace", "registration_id": 42, "created_at": 1779999970, "completed_at": 1779999998, "output_object_key": "serverless/requests/req_abc123/result_0.png", "output_bucket": "meshive-r2", "result_payload": { "images": 1 }, "error_code": null, "error_message": null }}id— the event id (evt_…). All deliveries of one event share it — use it as your idempotency key (see below).data.id— the request id (req_…).- A large
result_payloadis omitted (replaced with{"_truncated": true}); fetch the full result viaGET /v1/requests/{id}.
Headers on each delivery:
| Header | Meaning |
|---|---|
X-Meshive-Signature | t=<unix>,v1=<hmac-sha256> — verify this (generic endpoints only). |
X-Meshive-Event | The event type. |
X-Meshive-Delivery-Id | Identifies one delivery to one destination (stable across its retries). |
User-Agent | Meshive-Webhook/1.0 |
Verifying the signature
Section titled “Verifying the signature”For generic endpoints, verify X-Meshive-Signature against the raw request body using your signing secret (Serverless › Webhooks → Signing secret). The signed payload is "{t}." + raw_body.
import hmac, hashlib, osfrom flask import Flask, request
SECRET = os.environ["MESHIVE_WEBHOOK_SECRET"]app = Flask(__name__)
@app.post("/webhooks/meshive")def handler(): header = request.headers.get("X-Meshive-Signature", "") try: parts = dict(p.split("=", 1) for p in header.split(",")) payload = f'{parts["t"]}.'.encode() + request.get_data() expected = hmac.new(SECRET.encode(), payload, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, parts.get("v1", "")): return "bad signature", 401 except (ValueError, KeyError): # malformed / missing signature header → 401 (not 500) return "bad signature", 401 # optional but recommended: reject stale timestamps (e.g. > 5 minutes old) event = request.get_json() print(event["type"], event["data"]["id"], event["data"]["status"]) return "", 200import crypto from "crypto";import express from "express";
const SECRET = process.env.MESHIVE_WEBHOOK_SECRET;const app = express();
app.post("/webhooks/meshive", express.raw({ type: "application/json" }), (req, res) => { const header = req.header("X-Meshive-Signature") || ""; const parts = Object.fromEntries(header.split(",").map(p => p.split("="))); const expected = crypto.createHmac("sha256", SECRET) .update((parts.t || "") + "." + req.body).digest("hex"); // timingSafeEqual throws on length mismatch (missing/short v1) → guard so a bad // header returns 401 instead of crashing the handler. let ok = false; try { ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 || "")); } catch {} if (!ok) { return res.status(401).send("bad signature"); } // optional but recommended: reject stale timestamps (e.g. > 5 minutes old) const event = JSON.parse(req.body.toString()); console.log(event.type, event.data.id, event.data.status); res.sendStatus(200);});Delivery, retries, and idempotency
Section titled “Delivery, retries, and idempotency”- At-least-once. Each delivery is attempted up to 3 times with backoff (2s, 4s, 8s) on any non-
2xxresponse or network error. An event can also legitimately arrive at your server more than once (e.g. inline callback + subscription endpoint with slightly different URLs). Dedupe on the eventid(evt_…) — every delivery of the same event carries the same one. - Respond fast. Each attempt times out after 5 seconds — return
2xxquickly and do heavy work asynchronously, or the delivery counts as failed and is retried. - Auto-disable. A subscription endpoint that fails many times in a row (currently 20 consecutive deliveries) is automatically disabled, with the reason shown in the console. Re-enabling it resets the failure counter. Test sends never count toward auto-disable.
- Email fallback. If an inline callback can’t be delivered after all retries, Meshive sends you a notification email once.
Test deliveries & the Deliveries log
Section titled “Test deliveries & the Deliveries log”Test sends a sample event (X-Meshive-Event: webhook.test, body marked "_meshive_test": true) and shows the response code immediately. Tests are recorded in Deliveries like real events, so you can inspect exactly what was sent.
The Deliveries log shows the most recent deliveries (up to 200), filterable by endpoint or request id — each with event type, target URL, platform, delivered status, HTTP status code, attempt count, error string, and a link to the request.
Rolling the signing secret
Section titled “Rolling the signing secret”The signing secret is one per workspace, shared by all endpoints. Rolling it (Serverless › Webhooks → Signing secret → Roll) invalidates the old secret immediately — deploy the new secret to all of your receivers first, then roll.
Security
Section titled “Security”- HTTPS only.
http://and other schemes are rejected. - No private targets. URLs that resolve to private, loopback, link-local, or cloud-metadata addresses are rejected (SSRF protection), both when you save and again right before each delivery. For local development, use a tunnel (e.g. ngrok) to get a public HTTPS URL.
- No redirects. Meshive does not follow redirects when delivering.