Skip to main content

Why webhooks for production

Webhooks are the recommended way to receive execution results in production. Instead of polling (which wastes API calls and is rate-limited), ModelRoute pushes results to your endpoint the moment they’re ready.
PollingWebhooks
LatencyDepends on poll intervalInstant (< 1 second)
API callsMany (most return “still processing”)One (the result)
Rate limitsConstrainedNot applicable
ScalabilityPoor — each execution needs its own poll loopUnlimited — handles thousands of concurrent executions
ComplexitySimple loopEndpoint + signature verification
Start with polling, graduate to webhooks. Polling is perfect when you’re building your first integration and want to see results quickly. Once you’re ready for production, set up a webhook endpoint — it takes 10 minutes and gives you a dramatically better experience at scale.

Setup

1. Register a webhook endpoint

curl -X POST https://api.modelroute.ai/v1/webhooks/endpoints \
  -H "Authorization: Bearer sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/modelroute",
    "events": ["execution.completed", "execution.failed"],
    "description": "Production webhook"
  }'
{
  "id": "wh_abc123",
  "url": "https://your-app.com/webhooks/modelroute",
  "secret": "whsec_a1b2c3d4e5f6...",
  "events": ["execution.completed", "execution.failed"],
  "status": "active"
}
Save the secret immediately — it’s only shown once. You’ll need it to verify webhook signatures.

2. Create executions as normal

No changes to your execution requests. ModelRoute automatically delivers webhook events to all active endpoints subscribed to the relevant events.

3. Receive events

When an execution completes, ModelRoute sends a POST to your endpoint:
{
  "id": "evt_abc123",
  "type": "execution.completed",
  "timestamp": "2026-03-20T10:30:08Z",
  "data": {
    "id": "exec_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "COMPLETED",
    "model": "flux-1.1-pro",
    "cost": "0.040",
    "latency_ms": 8420,
    "result_url": "https://api.modelroute.ai/v1/executions/exec_a1b2.../result",
    "created_at": "2026-03-20T10:30:00Z",
    "completed_at": "2026-03-20T10:30:08Z"
  }
}

4. Verify the signature

Always verify the HMAC-SHA256 signature before processing. See webhook verification for code samples in Python, TypeScript, Go, and bash.

5. Respond with 200

Return a 200 OK to acknowledge receipt. If your endpoint returns a non-2xx status, ModelRoute retries with exponential backoff (up to 5 attempts over 24 hours).

Webhook events

EventWhenWhat to do
execution.completedAI model finished successfullyFetch result, deliver to your user
execution.failedAI model failedCheck error.code, retry if retryable
execution.startedExecution dispatched to providerUpdate UI status (optional)
execution.cancelledExecution cancelledUpdate UI status (optional)

Example: end-to-end flow

from flask import Flask, request, jsonify
import hmac
import hashlib
import requests

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_your_secret_here"
API_KEY = "sk_your_api_key_here"

@app.route("/webhooks/modelroute", methods=["POST"])
def handle_webhook():
    # 1. Verify signature
    signature = request.headers.get("X-Signature")
    timestamp = request.headers.get("X-Signature-Timestamp")
    payload = f"{timestamp}.{request.get_data(as_text=True)}"
    expected = hmac.new(
        WEBHOOK_SECRET.encode(), payload.encode(), hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return "Invalid signature", 401

    # 2. Process the event
    event = request.json
    if event["type"] == "execution.completed":
        execution_id = event["data"]["id"]

        # 3. Fetch the full result
        result = requests.get(
            f"https://api.modelroute.ai/v1/executions/{execution_id}/result",
            headers={"Authorization": f"Bearer {API_KEY}"}
        ).json()

        # 4. Do something with the result
        print(f"Execution {execution_id} completed: {result}")

    return jsonify({"received": True}), 200

Retry policy

If your endpoint is unreachable or returns a non-2xx status:
AttemptDelay
1st retry1 minute
2nd retry4 minutes
3rd retry16 minutes
4th retry30 minutes (capped)
5th retry30 minutes
After 5 failed attempts (or 24 hours), the delivery is marked as failed. You can manually retry failed deliveries via the API.