The queue worker posts job results to callback_url. Without a hardened receiver you get duplicate Slack alerts, double CSV imports, and race conditions when workers retry. This recipe is a minimal FastAPI service with HMAC verification and job_id deduplication.
Flow
Worker POST /hooks/mlx → verify X-MLX-Signature
→ dedupe job_id (Redis SET NX)
→ update job status in DB / notify ops
→ 200 OK (even on duplicate — idempotent)
Payload contract
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "ok",
"profile_id": "mlx-profile-uuid",
"task": "sync_orders",
"duration_ms": 8420,
"error": null
}
Workers may retry on timeout — receiver must treat duplicate job_id as success (return 200, no side effects).
HMAC sign (worker side)
import hmac
import hashlib
import json
SECRET = b"your-shared-secret"
def sign(body: dict) -> str:
raw = json.dumps(body, separators=(",", ":"), sort_keys=True).encode()
return hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
# Header: X-MLX-Signature: <hex>
FastAPI receiver
from fastapi import FastAPI, Request, HTTPException
import hmac
import hashlib
import json
import redis
app = FastAPI()
r = redis.Redis(decode_responses=True)
SECRET = b"your-shared-secret"
IDEMP_TTL = 86400 # 24h
def verify_sig(body: bytes, sig: str) -> bool:
expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig or "")
@app.post("/hooks/mlx")
async def mlx_webhook(request: Request):
raw = await request.body()
sig = request.headers.get("X-MLX-Signature", "")
if not verify_sig(raw, sig):
raise HTTPException(401, "invalid signature")
payload = json.loads(raw)
job_id = payload["job_id"]
idem_key = f"mlx:hook:{job_id}"
# Idempotent: first delivery wins
if not r.set(idem_key, "1", nx=True, ex=IDEMP_TTL):
return {"accepted": True, "duplicate": True}
status = payload.get("status")
if status == "ok":
mark_job_complete(job_id, payload)
else:
mark_job_failed(job_id, payload.get("error"))
notify_ops(payload) # Slack, PagerDuty, etc.
return {"accepted": True}
Flask variant (minimal)
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/hooks/mlx")
def mlx_hook():
raw = request.get_data()
if not verify_sig(raw, request.headers.get("X-MLX-Signature", "")):
abort(401)
payload = request.get_json()
if not r.set(f"mlx:hook:{payload['job_id']}", "1", nx=True, ex=86400):
return {"accepted": True, "duplicate": True}, 200
handle_payload(payload)
return {"accepted": True}, 200
Security checklist
- HTTPS only — never accept webhooks over plain HTTP in prod
- Rotate secret — support dual-key verify during rotation window
- Rate limit — per-IP throttle on
/hooks/mlx - Fast 200 — offload heavy work to background task; workers timeout at 10s
- Log raw payload — redact PII; keep
job_idfor audit
Observability hooks
Increment counters for webhook_received_total{status} and webhook_duplicate_total. Wire to observability guide Grafana panels alongside queue latency.
Related
Disclosure: MLX-MMO affiliated with Multilogin. Verify API endpoints against official Multilogin documentation.