Skip to content

Generic Webhook Notifications

The generic webhook channel POSTs a JSON body to any HTTPS URL you control. Use it to drive custom automation: auto-rollback on deploy failure, create Jira tickets on incidents, update Grafana annotations, write to a data warehouse, or fan out to a tool Reoclo doesn’t natively support.

  • An HTTPS endpoint that accepts POST requests with Content-Type: application/json
  • (Optional but recommended) A shared secret you’ll use to verify Reoclo signed each request

The receiver just needs to:

  1. Accept POST requests at a fixed URL
  2. Read the JSON body
  3. (Optional) Verify the X-Reoclo-Signature header
  4. Return any 2xx response (200, 201, 202, 204 all work)

Treat non-2xx responses as failures — Reoclo will retry (1s, 2s, 4s backoff) before dropping the delivery.

  1. Open Settings → Notification Channels.

  2. Click Add ChannelGeneric Webhook.

  3. Give it a name (e.g., data-warehouse).

  4. Fill in the form:

    FieldRequiredDescription
    URLYeshttps://... — HTTP isn’t supported
    HeadersNoCustom request headers as a JSON object (e.g., {"Authorization": "Bearer xyz"})
    HMAC AlgorithmNosha256 or sha1 — leave blank to disable signing
    SecretNoRequired if HMAC algorithm is set; used to compute the signature
  5. Click Send Test — Reoclo POSTs a test payload and the channel cannot save until your endpoint returns 2xx.

  6. Pick which events this channel receives.

  7. Save.

For anything with special characters (URLs with query strings, headers with :), use --from-file:

kind: generic_webhook
name: data-warehouse
config:
url: https://ingest.example.com/reoclo
headers:
Authorization: Bearer ${env:DW_TOKEN}
X-Tenant: yourcompany
hmac_alg: sha256
secret: ${env:DW_HMAC_SECRET}
events:
deploy_succeeded: true
deploy_failed: true
monitoring_uptime_down: true
monitoring_uptime_recovered: true
incident_created: true
incident_resolved: true
Terminal window
DW_TOKEN=... DW_HMAC_SECRET=... \
reoclo channels create generic_webhook --from-file dw-webhook.yaml

Every request is a JSON POST:

POST /reoclo HTTP/1.1
Host: ingest.example.com
Content-Type: application/json
Authorization: Bearer xyz
X-Reoclo-Signature: sha256=8f3c...
{
"subject": "Deployment succeeded",
"body": "Deployment of MyApp to prod-1 succeeded.",
"payload": {
"tenant_id": "550e8400-e29b-41d4-a716-446655440000",
"event": "deploy_succeeded",
"deployment_id": "dep-abc123",
"application_id": "app-xyz789",
"application_name": "MyApp",
"timestamp": "2026-04-10T15:30:45.123456+00:00"
}
}

The payload object’s exact shape depends on the event type — it includes the relevant resource IDs and context for each event.

If you set an HMAC algorithm and secret on the channel, Reoclo signs the raw request body with HMAC and sends the hex digest in X-Reoclo-Signature as <alg>=<hex>:

X-Reoclo-Signature: sha256=8f3c2a91...

Compute the same HMAC over the raw body using your shared secret and compare with a constant-time comparison.

import hashlib
import hmac
def verify(raw_body: bytes, secret: str, signature_header: str, algo: str = "sha256") -> bool:
digest = hashlib.sha256 if algo == "sha256" else hashlib.sha1
expected = f"{algo}=" + hmac.new(secret.encode(), raw_body, digest).hexdigest()
return hmac.compare_digest(expected, signature_header)
const crypto = require('crypto');
function verify(rawBody, secret, signatureHeader, algo = 'sha256') {
const expected = `${algo}=` + crypto.createHmac(algo, secret).update(rawBody).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
package webhook
import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/hex"
"hash"
)
func Verify(rawBody []byte, secret, signatureHeader, algo string) bool {
var h hash.Hash
if algo == "sha1" {
h = hmac.New(sha1.New, []byte(secret))
} else {
h = hmac.New(sha256.New, []byte(secret))
}
h.Write(rawBody)
expected := algo + "=" + hex.EncodeToString(h.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signatureHeader))
}

Use the headers config to inject bearer tokens, tenant identifiers, or routing hints:

config:
url: https://api.example.com/hooks
headers:
Authorization: Bearer ${env:API_TOKEN}
X-Source: reoclo
X-Env: production

Header names are case-insensitive; the receiver sees them as sent. Don’t override Content-Type — Reoclo always sends application/json and your receiver will misparse anything else.

SymptomCauseFix
Channel won’t save: connection refusedReceiver isn’t running or wrong URLCheck the URL is reachable from the public internet (Reoclo runs from our hosted control plane)
403 Forbidden from receiverAuth header missing or wrongConfirm the bearer token / auth scheme your receiver expects, set it in the channel headers
Signature verification failsReceiver hashed re-serialized JSON, not the raw bodyHash the raw bytes of the request body before any parsing
All deliveries 200 but receiver gets nothingReceiver is responding before processingProcess synchronously, or write to a queue and return 200 only after enqueue succeeds
Webhook hits rate limitToo many events flowing through one endpointFilter events on the channel to only what you need, or split into multiple channels

Auto-rollback — receiver subscribes to deploy_failed and calls your CI to re-run the previous green deploy.

Audit logging — fan out all events to a SIEM or data warehouse with no event filter set.

Custom paging — subscribe to monitoring_uptime_down and incident_created and hit your own pager (Twilio SMS, custom on-call).

Status page automation — subscribe to incident_created, incident_updated, incident_resolved and update Atlassian Statuspage or your own.

Ticket creation — subscribe to monitoring_ssl_expiring/monitoring_domain_expiring and create Jira/Linear tickets for renewal.

  • Status Pages — Reoclo’s built-in status page if you don’t want to build a custom integration
  • Alert Rules — wire monitor thresholds into your custom webhook