Webhook API
Receive callbacks when Jetty workflows complete.
Endpoint
POST /api/mise/webhook
Overview
When you submit an async workflow — for example, analyzing Langfuse traces or running a CI quality check — Jetty sends a webhook callback when execution completes. The callback includes the full trajectory, any PR recommendations, and pass/fail status.
Authentication
Uses HMAC-SHA256 signature verification. The webhook secret is shared when you start the async workflow.
Request Headers
X-Mise-Signature: <hmac-sha256-signature>
X-Mise-Timestamp: <unix-timestamp>
X-Mise-Trajectory-Id: <trajectory-id>
Content-Type: application/json
Signature Verification
The signature is computed as:
HMAC-SHA256(secret, "{timestamp}.{payload}")
Python:
import hmac
import hashlib
signing_string = f"{timestamp}.{payload}"
signature = hmac.new(
secret.encode("utf-8"),
signing_string.encode("utf-8"),
hashlib.sha256,
).hexdigest()
Node.js:
const crypto = require('crypto');
const signingString = `${timestamp}.${payload}`;
const signature = crypto
.createHmac('sha256', secret)
.update(signingString)
.digest('hex');
Always use timing-safe comparison when verifying signatures.
Request Body
The webhook receives a trajectory payload:
{
"name": "jetty-analysis/optimize-langfuse",
"trajectory_id": "abc123",
"status": "completed" | "failed",
"storage_path": "path/to/storage",
"steps": {
"step_name": {
"status": "completed",
"outputs": {
// Step-specific outputs
"pr_title"?: string,
"pr_body"?: string,
"pr_files"?: Array<{
path: string,
content: string,
encoding?: "utf-8" | "base64"
}>,
"repository_owner"?: string,
"repository_name"?: string,
"suggested_branch"?: string,
// Alternative structure
"recommendations"?: {
title: string,
body: string,
files: Array<{path, content, encoding}>,
repository_owner?: string,
repository_name?: string,
branch?: string
}
}
}
},
"completed_steps": string[],
"total_steps": string[],
"init_params": Record<string, any>
}
Response
Success (200)
{
"success": true,
"recommendation": {
"id": "uuid",
"trajectoryId": "abc123",
"status": "pending"
}
}
Error Responses
| Code | Body | Cause |
|---|---|---|
| 400 | {"success": false, "error": "Missing required headers"} | Missing signature, timestamp, or trajectory ID headers |
| 400 | {"success": false, "error": "Invalid trajectory payload"} | Malformed or missing required fields |
| 401 | {"success": false, "error": "Invalid signature"} | HMAC verification failed |
| 500 | {"success": false, "error": "Webhook not configured"} | MISE_WEBHOOK_SECRET not set |
Configuring Webhooks
Provide the webhook URL and secret when starting an async workflow:
curl -X POST "https://flows-api.jetty.io/api/v1/run/my-collection/my-task" \
-H "Authorization: Bearer $JETTY_API_TOKEN" \
-F 'init_params={"langfuse_project_id": "proj_123"}' \
-F 'webhook_url=https://your-app.com/api/webhook' \
-F 'webhook_secret=your-webhook-secret'
Human-in-the-Loop Workflow
When webhooks deliver PR recommendations, Jetty implements a human-in-the-loop pattern:
- Receive — Webhook delivers completed analysis with PR recommendations
- Store — Recommendations stored with status
pending - Review — User views recommendations in the dashboard
- Approve/Reject — User reviews PR content and decides
- Create PR — If approved, PR is created via the GitHub PR API
This ensures human oversight before any code changes are made to your repositories.
Security Considerations
- HMAC Verification — Always use timing-safe comparison to prevent timing attacks
- Secret Management — Store
MISE_WEBHOOK_SECRETin environment variables, never commit to code - Payload Validation — Validate required fields before processing
- Error Handling — Log errors without exposing internal details to clients
See Also
- GitHub PR API — Create PRs from approved recommendations
- Chat Completions API — The core Jetty endpoint
- CI Integration Guide — Using webhooks in GitHub Actions
- Jetty Agent — The full telemetry → PR feedback loop