Skip to content

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 ​

PinKindTypesDescription
InputinputanyThe payload, see below.
OutputoutputViaducta.Plugins.Http.Types.WebHttpRequestThe signed request. Connect it to an Http client.

What is sent ​

InputBody
TextRecordValue as is, with the record's ContentType (else ContentType from config).
WebHttpRequestIts body (ByteBody, else Body) as is. Its Uri (when config Uri is empty), headers, cookies and content type are kept. Form requests are refused.
NoInputDataRefused (an error).
anything elseThe 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 ​

FieldTypeDefaultDescription
UristringTarget URL. Empty: the input WebHttpRequest Uri, else the Http client's Uri. Bind to an expression for a URL per receiver.
MethodstringPOSTHTTP method.
SignatureSchemedropdown: 0 None, 1 HmacHeader, 2 GitHub, 3 Stripe, 4 Slack, 5 StandardWebhooks, 6 SharedSecretHeader, 7 MicrosoftGraph5See below. MicrosoftGraph (7) can't be sent (error at start).
SecretpasswordThe signing secret. StandardWebhooks: whsec_ + base64 key. Bind to an expression for a secret per receiver; if empty when sending, the record fails.
SignatureHeaderstringX-SignatureHmacHeader / SharedSecretHeader: the header name.
SignaturePrefixstringHmacHeader: written before the signature (for example sha256=).
HmacAlgorithmdropdown: 0 Sha256, 1 Sha1, 2 Sha5120HmacHeader only.
SignatureEncodingdropdown: 0 Hex, 1 Base640HmacHeader only.
EventIdstringThe webhook-id. Empty: msg_ + the record id (stable across Retry).
EventTypestringEvent type for the envelope and the type header.
EventTypeHeaderstringHeader to carry the event type (for example X-Event-Type). Empty: none.
EnvelopebooleanfalseWrap as {"type", "timestamp", "data"} (Standard Webhooks style). The payload must be JSON.
ContentTypestringapplication/jsonContent type for JSON records and TextRecords without one.
OmitNullValuesbooleanfalseLeave out null properties when serializing records.
ReceiverLanguagecode generator (CSHARP, PYTHON, GO, JAVA, PHP)CSHARPDesigner 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 ​

SchemeHeaders sent
Noneno signature (warning at start)
HmacHeaderSignatureHeader: <prefix><hex or base64 HMAC(body)>
GitHubX-Hub-Signature-256: sha256=<hex>
StripeStripe-Signature: t=<ts>,v1=<hex HMAC("ts." + body)>
SlackX-Slack-Request-Timestamp, X-Slack-Signature: v0=<hex HMAC("v0:ts:" + body)>
StandardWebhookswebhook-signature: v1,<base64 HMAC("id.ts." + body)>
SharedSecretHeaderSignatureHeader: <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 ContentType empty and Method at GET (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

Viaducta documentation