Skip to main content

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

CodeBodyCause
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:

  1. Receive — Webhook delivers completed analysis with PR recommendations
  2. Store — Recommendations stored with status pending
  3. Review — User views recommendations in the dashboard
  4. Approve/Reject — User reviews PR content and decides
  5. 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_SECRET in 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