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.
Polling Webhooks Latency Depends on poll interval Instant (< 1 second) API calls Many (most return “still processing”) One (the result) Rate limits Constrained Not applicable Scalability Poor — each execution needs its own poll loop Unlimited — handles thousands of concurrent executions Complexity Simple loop Endpoint + 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
Event When What to do execution.completedAI model finished successfully Fetch result, deliver to your user execution.failedAI model failed Check error.code, retry if retryable execution.startedExecution dispatched to provider Update UI status (optional) execution.cancelledExecution cancelled Update UI status (optional)
Example: end-to-end flow
Python (Flask)
TypeScript (Express)
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:
Attempt Delay 1st retry 1 minute 2nd retry 4 minutes 3rd retry 16 minutes 4th retry 30 minutes (capped) 5th retry 30 minutes
After 5 failed attempts (or 24 hours), the delivery is marked as failed. You can manually retry failed deliveries via the API.