Appearance
Webhook sender
Turns any record into a signed webhook request. Connect its Output to an Http client, which does the actual sending (auth, TLS, client certificates, timeouts). The signature formats are the same ones the Webhook receiver checks, so a Viaducta flow can send to another one.
Pins
| Pin | Kind | Types | Description |
|---|---|---|---|
| Input | input | any | The payload, see below. |
| Output | output | Viaducta.Plugins.Http.Types.WebHttpRequest | The signed request. Connect it to an Http client. |
What is sent
| Input | Body |
|---|---|
TextRecord | Value as is, with the record's ContentType (else ContentType from config). |
WebHttpRequest | Its body (ByteBody, else Body) as is. Its Uri (when config Uri is empty), headers, cookies and content type are kept. Form requests are refused. |
NoInputData | Refused (an error). |
| anything else | The record as JSON (compact, without the internal recordId), content type ContentType from config. |
The bytes go in ByteBody (Body is empty), so the Http client sends exactly what was signed.
Configuration
| Field | Type | Default | Description |
|---|---|---|---|
Uri | string | Target URL. Empty: the input WebHttpRequest Uri, else the Http client's Uri. Bind to an expression for a URL per receiver. | |
Method | string | POST | HTTP method. |
SignatureScheme | dropdown: 0 None, 1 HmacHeader, 2 GitHub, 3 Stripe, 4 Slack, 5 StandardWebhooks, 6 SharedSecretHeader, 7 MicrosoftGraph | 5 | See below. MicrosoftGraph (7) can't be sent (error at start). |
Secret | password | The signing secret. StandardWebhooks: whsec_ + base64 key. Bind to an expression for a secret per receiver; if empty when sending, the record fails. | |
SignatureHeader | string | X-Signature | HmacHeader / SharedSecretHeader: the header name. |
SignaturePrefix | string | HmacHeader: written before the signature (for example sha256=). | |
HmacAlgorithm | dropdown: 0 Sha256, 1 Sha1, 2 Sha512 | 0 | HmacHeader only. |
SignatureEncoding | dropdown: 0 Hex, 1 Base64 | 0 | HmacHeader only. |
EventId | string | The webhook-id. Empty: msg_ + the record id (stable across Retry). | |
EventType | string | Event type for the envelope and the type header. | |
EventTypeHeader | string | Header to carry the event type (for example X-Event-Type). Empty: none. | |
Envelope | boolean | false | Wrap as {"type", "timestamp", "data"} (Standard Webhooks style). The payload must be JSON. |
ContentType | string | application/json | Content type for JSON records and TextRecords without one. |
OmitNullValues | boolean | false | Leave out null properties when serializing records. |
ReceiverLanguage | code generator (CSHARP, PYTHON, GO, JAVA, PHP) | CSHARP | Designer only: the button opens generated receiving code for an app that gets these webhooks, see "Receiver code" below. Not used at run time. |
Signature schemes
| Scheme | Headers sent |
|---|---|
| None | no signature (warning at start) |
| HmacHeader | SignatureHeader: <prefix><hex or base64 HMAC(body)> |
| GitHub | X-Hub-Signature-256: sha256=<hex> |
| Stripe | Stripe-Signature: t=<ts>,v1=<hex HMAC("ts." + body)> |
| Slack | X-Slack-Request-Timestamp, X-Slack-Signature: v0=<hex HMAC("v0:ts:" + body)> |
| StandardWebhooks | webhook-signature: v1,<base64 HMAC("id.ts." + body)> |
| SharedSecretHeader | SignatureHeader: <secret> |
Every scheme also sends webhook-id and webhook-timestamp (unix seconds), so receivers can drop repeated deliveries. The Webhook metadata gets EventId, EventType and Scheme.
Retries
Put a Retry component in front: Retry → Webhook sender → Http client. When the Http client fails, Retry sends the original record again through the sender, which signs it again with a fresh timestamp (receivers reject old timestamps) and the same webhook-id (from the record id, or EventId). Connect Retry's MaxRetries pin to a log or database for deliveries that never succeeded.
Receiver code
The ReceiverLanguage button opens code for the app that receives these webhooks (Download saves it): C# (.NET 8+, ASP.NET Core minimal API, no packages), Python (3.10+, standard library only), Go (1.24+, standard library only, an http.Handler) Java (17+, JDK HttpServer, Jackson 2.15+ for the payload) or PHP (8.1+, no packages, a plain script or php -S). It contains the payload types (the type connected to Input; a TextRecord gives text, a WebHttpRequest the raw body, anything else JSON), a <Payload>WebhookVerifier that checks the signature the way SignatureScheme signs it (constant time, 300 seconds tolerance for the timestamp of Stripe, Slack and StandardWebhooks, a secondary secret while rotating), unwraps the envelope and returns the event id, type, timestamp and payload, and an endpoint on the path of Uri: app.Map<Payload>Webhook(...) in C#, serve(...) (http.server) in Python with a FastAPI example, verifier.Handler(...) in Go, serve(port, verifier, handler) (JDK HttpServer) in Java with a Spring Boot controller as a comment, <Payload>WebhookEndpoint::handle(...) in PHP with a Laravel route as a comment. The same menu gives an OpenAPI 3.1 document (YAML or JSON) of what the sender sends (webhooks), with the signature in x-viaducta-signature. The endpoint answers 401 for a failed check, 400 for a body that is not the expected JSON, 500 when the app's handler fails (a Retry in front of the sender sends the event again with the same webhook-id) and 200 otherwise. The secret is read from the VIADUCTA_WEBHOOK_SECRET (and VIADUCTA_WEBHOOK_SECRET_SECONDARY) environment variable or the constructor, never written into the code. Settings bound to expressions are noted in the file header.
Notes
- On the Http client leave
ContentTypeempty andMethodatGET(the default), otherwise they override what the sender set. - Headers set on the Http client are added too; they are not part of the signature.
- One webhook per record; batches are not collected.
- Retry keeps pending retries in memory only.
Example
Database query → Retry → Webhook sender (StandardWebhooks, Uri = expression on the subscriber URL, Secret = expression on the subscriber secret, EventType = order.created) → Http client