Skip to the content.

Why qhook?

qhook is a lightweight event-to-action engine. It receives events (webhooks, SNS, API calls) and executes HTTP actions reliably — from a single call to a multi-step pipeline with error routing and rollback. Single binary, no Redis, no Kubernetes.


The Problem

When an event arrives — a GitHub push, a Stripe payment, a monitoring alert, a tenant signup — you need to reliably call one or more HTTP endpoints in response.

Current approach Limitation
Custom scripts No retry, no error routing, no monitoring
CI/CD tools (GitHub Actions, etc.) Weak at orchestrating external HTTP services with retry and rollback
Kubernetes-native tools (Argo Events, etc.) Requires Kubernetes
Managed workflow services (Step Functions, etc.) Vendor lock-in, per-transition costs
Heavyweight orchestrators (Conductor, etc.) JVM + DB + Elasticsearch, complex setup

qhook fills the gap: lightweight, reliable event-to-action execution without infrastructure overhead.


How It Works

Event source               qhook                           Your services

  Webhook (verified) ---->|                              |
  SNS message ----------->| Route + execute              |
  API call --------------->|                              |
                           |  Simple (handler):           |
                           |    event → POST /billing --->|  one HTTP call
                           |    (retry, DLQ, fan-out)     |
                           |                              |
                           |  Multi-step (workflow):      |
                           |    event → build → deploy -->|  chained HTTP calls
                           |              ↓ fail          |  with error routing
                           |          rollback → alert -->|

Handlers execute a single HTTP action per event — with retry, DLQ, filtering, transformation, and fan-out to multiple services.

Workflows chain multiple HTTP actions — with branching, parallelism, wait, callback, and error routing (catch → rollback). Each step can send custom headers for authentication.

Both are defined in the same YAML config. Both share the same retry, DLQ, and monitoring infrastructure.


Use Cases

Simple: Webhook Fan-out

Receive Stripe webhooks, verify the signature, and deliver to multiple services with independent retry.

sources:
  stripe:
    type: webhook
    verify: stripe
    secret: ${STRIPE_WEBHOOK_SECRET}

handlers:
  billing:
    source: stripe
    events: [invoice.paid]
    url: http://billing:3000/webhook
    idempotency_key: "$.id"
    retry: { max: 8 }
  analytics:
    source: stripe
    events: ["*"]
    url: http://analytics:3000/ingest

Multi-step: Tenant Provisioning

API event triggers infra creation → DB setup → service configuration, with rollback on failure. Input parameters are validated, and each step can authenticate to external APIs.

sources:
  platform:
    type: event

workflows:
  tenant-provision:
    source: platform
    events: [tenant.create]
    timeout: 300
    params:
      - name: tenant_id
        type: string
      - name: region
        type: string
      - name: plan
        type: string
        required: false
    steps:
      - name: create-infra
        url: https://api.terraform.io/v2/runs
        headers:
          Authorization: "Bearer ${TF_API_TOKEN}"
        result_path: "$.infra"
        retry: { max: 3, errors: [5xx, timeout] }
        catch:
          - errors: [all]
            goto: notify-failure

      - name: wait-infra
        type: callback
        url: https://api.terraform.io/v2/notification-configs
        headers:
          Authorization: "Bearer ${TF_API_TOKEN}"
        callback_timeout: 600

      - name: create-db
        url: http://db-service:3000/tenant
        result_path: "$.db"
        catch:
          - errors: [all]
            goto: rollback-infra

      - name: configure-services
        url: http://integration:3000/setup
        catch:
          - errors: [all]
            goto: rollback-db

      - name: activate
        url: http://platform:3000/tenant/activate
        end: true

      # --- rollback (reverse order) ---
      - name: rollback-db
        url: http://db-service:3000/tenant/delete
        on_failure: continue
        goto: rollback-infra

      - name: rollback-infra
        url: https://api.terraform.io/v2/runs
        headers:
          Authorization: "Bearer ${TF_API_TOKEN}"
        on_failure: continue
        goto: notify-failure

      - name: notify-failure
        url: http://slack:3000/notify
        end: true

Multi-step: Deploy with Post-deploy Checks

GitHub push triggers build → deploy → smoke test → notify. Failure routes to rollback. Works as a post-deploy pipeline triggered by CI/CD or ArgoCD.

sources:
  github:
    type: webhook
    verify: github
    secret: ${GITHUB_WEBHOOK_SECRET}

workflows:
  deploy:
    source: github
    events: [push]
    timeout: 600
    steps:
      - name: build
        url: http://ci:3000/build
        retry: { max: 2, errors: [5xx, timeout] }
        result_path: "$.build"

      - name: deploy-staging
        url: http://deployer:3000/deploy
        input: |
          {"env": "staging", "image": ""}
        catch:
          - errors: [all]
            goto: rollback

      - name: smoke-test
        url: http://tester:3000/smoke
        catch:
          - errors: [all]
            goto: rollback

      - name: deploy-prod
        url: http://deployer:3000/deploy
        input: |
          {"env": "production", "image": ""}
        end: true

      - name: rollback
        url: http://deployer:3000/rollback
        end: true

Multi-step: Alert Remediation

PagerDuty alert triggers severity-based action with verification. Signature-verified webhook, authenticated API calls for escalation.

sources:
  pagerduty:
    type: webhook
    verify: pagerduty
    secret: ${PAGERDUTY_WEBHOOK_SECRET}

workflows:
  remediate:
    source: pagerduty
    events: [incident.triggered]
    timeout: 300
    steps:
      - name: triage
        type: choice
        choices:
          - condition: "$.severity == critical"
            goto: restart
          - condition: "$.severity == warning"
            goto: scale-up
        default: notify

      - name: restart
        url: http://orchestrator:3000/restart
        goto: verify

      - name: scale-up
        url: http://orchestrator:3000/scale
        goto: verify

      - name: verify
        type: wait
        seconds: 30
        goto: health-check

      - name: health-check
        url: http://orchestrator:3000/health
        catch:
          - errors: [all]
            goto: escalate
        end: true

      - name: escalate
        url: https://api.pagerduty.com/incidents
        headers:
          Authorization: "Token token=${PAGERDUTY_API_KEY}"
          Content-Type: "application/json"
        end: true

      - name: notify
        url: http://slack:3000/notify
        end: true

Multi-step: Terraform Post-apply

Terraform Cloud run completion triggers application-layer configuration and verification.

sources:
  terraform:
    type: webhook
    verify: terraform
    secret: ${TF_NOTIFICATION_SECRET}

workflows:
  post-apply:
    source: terraform
    events: [run:completed]
    timeout: 300
    steps:
      - name: update-config
        url: http://config-service:3000/apply
        retry: { max: 3, errors: [5xx, timeout] }
        catch:
          - errors: [all]
            goto: rollback-config

      - name: health-check
        url: http://health-service:3000/check
        catch:
          - errors: [all]
            goto: rollback-config

      - name: notify-success
        url: http://slack:3000/notify
        input: |
          {"text": "Terraform apply succeeded, config updated."}
        end: true

      - name: rollback-config
        url: http://config-service:3000/rollback
        on_failure: continue
        goto: notify-failure

      - name: notify-failure
        url: http://slack:3000/notify
        input: |
          {"text": "Terraform post-apply failed. Manual intervention required."}
        end: true

Supported Webhook Providers

Provider Verification Header
GitHub HMAC-SHA256 X-Hub-Signature-256
Stripe HMAC-SHA256 + timestamp Stripe-Signature
Shopify HMAC-SHA256 (base64) X-Shopify-Hmac-SHA256
PagerDuty HMAC-SHA256 X-PagerDuty-Signature
Grafana HMAC-SHA256 X-Grafana-Alerting-Signature
Terraform Cloud HMAC-SHA512 X-TFE-Notification-Signature
GitLab Token comparison X-Gitlab-Token
AWS SNS X.509 certificate (in body)
Custom HMAC HMAC-SHA256 X-Webhook-Signature

How qhook Compares

vs Custom Scripts / Cron

  Scripts qhook
Retry on failure DIY (if at all) Built-in (exponential backoff)
Error routing DIY catch → rollback steps
Dead Letter Queue None Built-in
Monitoring DIY Prometheus metrics, alerts
Webhook verification DIY Built-in (9 providers)

vs CI/CD Tools (GitHub Actions, etc.)

  CI/CD tools qhook
Trigger Git events, schedules Webhooks (verified), SNS, API
HTTP orchestration Weak (shell scripts with curl) Native (YAML-defined HTTP chains)
Retry with error routing Limited Built-in (catch → rollback)
Auth to external APIs Per-step secrets headers field with env var expansion
DLQ None Built-in

vs Kubernetes-native Tools

  K8s tools (Argo Events, etc.) qhook
Requires Kubernetes Yes No
Webhook verification Limited Built-in (9 providers)
Setup K8s cluster + CRDs Single binary
Post-deploy hooks K8s Jobs only Any HTTP service

vs Managed Workflow Services (Step Functions, etc.)

  Managed services qhook
Infrastructure Vendor-specific Any cloud or on-prem
Pricing Per-transition / per-run Free (self-hosted)
Webhook verification Not included Built-in
Local dev Emulators (partial) SQLite
Vendor lock-in Yes None

vs Heavyweight Orchestrators (Conductor, etc.)

  Heavy orchestrators qhook
Runtime JVM + DB + Elasticsearch Single Rust binary
Setup time 30+ minutes 2 minutes
Definition JSON DSL or Code SDK YAML
Web UI Yes No (CLI only)
Operational burden High Low

Where qhook Fits

qhook is purpose-built for one pattern: event arrives → execute HTTP actions reliably.

It is not a replacement for:

It is the right choice when:


Summary

Capability qhook
Deployment Single binary, zero external deps
Database SQLite (dev) / Postgres (prod)
Input Webhooks (9 providers verified), SNS, internal API
Actions HTTP with custom headers for authentication
Reliability Retry (exponential backoff), error routing, DLQ
Workflows Sequential, choice, parallel, map, wait, callback
Input validation Workflow params with type checking
Monitoring Prometheus metrics, health checks, Slack/Discord alerts
Config Single YAML file, env var expansion, validation CLI