Stevora

Webhooks

Configure webhook notifications

Webhooks

Webhooks let you receive HTTP callbacks when key events occur in your workflows. Stevora signs every webhook delivery with HMAC-SHA256 so you can verify authenticity.

Create a Webhook

POST /v1/webhooks

Registers a new webhook endpoint. Stevora generates an HMAC signing secret and returns it in the response. Store this secret securely -- it is only shown once.

Request Body

FieldTypeRequiredDescription
urlstringYesThe HTTPS URL to receive webhook payloads (must be a valid URL)
eventsstring[]YesArray of event types to subscribe to (at least one)

Available Events

EventTrigger
workflow.completedA workflow run finished successfully
workflow.failedA workflow run failed after exhausting retries
workflow.cancelledA workflow run was cancelled
approval.requestedAn approval step is waiting for a decision
approval.decidedAn approval decision was submitted

Example Request

curl -X POST https://api.stevora.dev/v1/webhooks \
  -H "x-api-key: stv_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/stevora",
    "events": ["workflow.completed", "workflow.failed", "approval.requested"]
  }'

Response 201 Created

{
  "success": true,
  "data": {
    "id": "wh_abc123",
    "workspaceId": "ws_xyz789",
    "url": "https://your-app.com/webhooks/stevora",
    "events": ["workflow.completed", "workflow.failed", "approval.requested"],
    "secret": "whsec_a1b2c3d4e5f6g7h8i9j0...",
    "isActive": true,
    "createdAt": "2026-04-02T12:00:00.000Z"
  }
}

The secret field is only included in the creation response. Store it securely for signature verification.

Error Responses

StatusCodeCause
400VALIDATION_ERRORInvalid URL, empty events array, or unrecognized event type
401AUTH_ERRORMissing or invalid API key

List Webhooks

GET /v1/webhooks

Returns all webhooks registered in your workspace.

Example Request

curl https://api.stevora.dev/v1/webhooks \
  -H "x-api-key: stv_your_api_key"

Response 200 OK

{
  "success": true,
  "data": [
    {
      "id": "wh_abc123",
      "workspaceId": "ws_xyz789",
      "url": "https://your-app.com/webhooks/stevora",
      "events": ["workflow.completed", "workflow.failed", "approval.requested"],
      "isActive": true,
      "createdAt": "2026-04-02T12:00:00.000Z"
    }
  ]
}

The secret is never included in list responses.

Error Responses

StatusCodeCause
401AUTH_ERRORMissing or invalid API key

Delete a Webhook

DELETE /v1/webhooks/:id

Removes a webhook. Stevora will stop sending deliveries to the URL immediately.

Path Parameters

ParameterTypeDescription
idstringThe webhook ID

Example Request

curl -X DELETE https://api.stevora.dev/v1/webhooks/wh_abc123 \
  -H "x-api-key: stv_your_api_key"

Response 200 OK

{
  "success": true,
  "data": {
    "id": "wh_abc123",
    "deleted": true
  }
}

Error Responses

StatusCodeCause
401AUTH_ERRORMissing or invalid API key
404NOT_FOUNDWebhook not found or belongs to another workspace

Webhook Payload Format

Every webhook delivery is an HTTP POST with a JSON body:

{
  "event": "workflow.completed",
  "workflowRunId": "wfrun_def456",
  "workspaceId": "ws_xyz789",
  "data": {
    "status": "COMPLETED",
    "output": { "result": "success" },
    "completedAt": "2026-04-02T12:05:00.000Z"
  },
  "timestamp": "2026-04-02T12:05:00.123Z"
}

Delivery Headers

HeaderDescription
Content-Typeapplication/json
X-Webhook-SignatureHMAC-SHA256 hex digest of the request body
X-Webhook-EventThe event type (e.g., workflow.completed)
X-Webhook-IdThe webhook registration ID

Signature Verification

Stevora signs the raw JSON body using HMAC-SHA256 with the webhook's secret. To verify a delivery:

  1. Read the raw request body (before any JSON parsing)
  2. Compute the HMAC-SHA256 of the body using your stored secret
  3. Compare the result with the X-Webhook-Signature header

Node.js Example

import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyWebhookSignature(body, signature, secret) {
  const expected = createHmac('sha256', secret)
    .update(body)
    .digest('hex');

  return timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expected, 'hex')
  );
}

// In your Express/Fastify handler:
const isValid = verifyWebhookSignature(
  rawBody,
  req.headers['x-webhook-signature'],
  process.env.STEVORA_WEBHOOK_SECRET
);

if (!isValid) {
  return res.status(401).send('Invalid signature');
}

Python Example

import hmac
import hashlib

def verify_webhook_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Retry Behavior

Stevora retries failed webhook deliveries up to 3 times with increasing backoff:

AttemptDelay
1Immediate
25 seconds
330 seconds

A delivery is considered failed if your endpoint returns a non-2xx status code, times out (10 second limit), or is unreachable.