Webhook Relay ExecModule
Overview
WebhookRelayModule sends exactly one JSON event to a webhook endpoint explicitly authorized by a bound IntegrationAccount. Version 2.0 replaces the legacy open relay contract: workflow input can no longer choose an arbitrary origin, provide authentication headers, configure plaintext HMAC secrets, follow redirects, fabricate mock success, or automatically retry an ambiguous write.
The module now provides:
- an IntegrationAccount-bound HTTPS destination;
- optional bearer authentication and HMAC-SHA256 signing from encrypted SecureFields;
- explicit
POST,PUT, orPATCHexecution; - a 512 KiB request limit and 256 KiB response-capture limit;
- 1–30 second transport bounds;
- redirects disabled across origins and schemes;
- a single network attempt for every write;
- structured, credential-safe outputs and failures.
Workflow Studio discovers this implementation as WebhookRelayModule version 2.0.0.
Usage
- Create an
IntegrationAccountfor the destination webhook. - Store the complete authorized HTTPS endpoint in
IntegrationAccount.accountId. - Optionally store a bearer token in the encrypted
apiKeySecureField. - Optionally store an HMAC-SHA256 signing secret in the encrypted
passwordSecureField. - Set the account status to
READYand bind it throughExecModuleConfig.authConfig.integrationAccount. - Configure an explicit JSON
payload, then select a relative path and method if the account endpoint is a base route. - Reconcile destination state before manually retrying any failed or timed-out delivery.
Inputs
| Input | Type | Required | Default | Constraints |
|---|---|---|---|---|
payload | object | Yes | None | Explicit JSON object, maximum 512 KiB after serialization. Plaintext values under secret-bearing field names are rejected. |
path | string | No | Empty | Relative path only, maximum 1,024 characters. Origins, traversal, query strings, fragments, and backslashes are rejected. |
method | string | No | POST | POST, PUT, or PATCH. |
idempotencyKey | string | No | None | Maximum 200 characters; no CR/LF. Forwarded as Idempotency-Key. |
correlationId | string | No | None | Maximum 200 characters; no CR/LF. Forwarded as X-Correlation-ID. |
The payload may come from the normalized module configuration or the explicit map input key payload. The module never forwards the entire workflow state implicitly.
Outputs
| Output | Type | Meaning |
|---|---|---|
status | string | success or error. |
method | string | Normalized outbound method. |
httpStatus | integer | Provider status after transport completes. |
targetHost | string | Authorized destination host; path, query, and credentials are omitted. |
response | object or string | Optional bounded, secret-redacted response. |
responseTruncated | boolean | true when capture exceeded 256 KiB. |
attempts | integer | 0 before transport or 1 after transport begins. |
error | object | Safe code, message, retryable, and ambiguous fields. |
Provider response bodies are never returned for non-2xx failures. This prevents a provider from reflecting credentials or sensitive request details through an error response.
IntegrationAccount Requirements
The bound account must be READY and use these fields:
| IntegrationAccount field | Required | Purpose |
|---|---|---|
accountId | Yes | Complete authorized HTTPS endpoint. Credentials, query strings, and fragments are rejected. |
apiKey | No | Bearer token sent only to the bound endpoint. |
password | No | HMAC-SHA256 signing secret used to compute X-Valkyr-Signature-SHA256. |
The destination origin and credentials are not accepted through workflow input. Rotate endpoint and secret material through the platform IntegrationAccount lifecycle.
Configuration
| Field | Type | Default | Notes |
|---|---|---|---|
webhookAccount | IntegrationAccount | None | Required secure relationship. |
path | text | Empty | Relative route appended to the account endpoint. |
method | select | POST | POST, PUT, or PATCH. |
payload | JSON | None | Required explicit object. |
idempotencyKey | text | None | Caller/provider correlation only; does not make every destination idempotent. |
correlationId | text | None | Optional trace identifier. |
timeoutMs | integer | 10000 | From 1000 through 30000. Applied to connect, read, write, and whole-call timeouts. |
captureResponse | boolean | true | Captures at most 256 KiB and applies secret redaction. |
Legacy fields such as url, endpoint, headers, Authorization, token, password, hmac_secret, mock, retry_enabled, and retry_count fail validation.
Operations
POST
Create or append one event at the destination. This is the default and is never retried automatically.
PUT
Replace the destination resource only when the provider defines the route as PUT. A caller-owned idempotencyKey may be forwarded, but the provider contract remains authoritative.
PATCH
Apply one partial update. A timeout or transport error is treated as ambiguous because the provider may have committed the request.
All methods send application/json, request application/json, and use the ValkyrAI-WebhookRelay/2.0 user agent. Redirect responses are returned as failures rather than followed.
Errors and Failure Modes
| Code | Cause | Automatic retry | Recovery |
|---|---|---|---|
VALIDATION_ERROR | Unsafe account endpoint, raw credential/destination field, missing or oversized payload, unsafe path/header, unsupported method, or invalid timeout. | No | Correct configuration; no request was sent. |
INTEGRATION_ACCOUNT_REQUIRED | No account is bound. | No | Bind the destination account. |
INTEGRATION_ACCOUNT_NOT_READY | Account status is not READY. | No | Repair authorization or account lifecycle state. |
WEBHOOK_HTTP_<status> | Provider returned non-2xx, including redirect status. | No | Inspect the provider under the same account and reconcile before retrying. |
NETWORK_ERROR | DNS, connect, TLS, write, read, or call timeout/transport failure. | No | Treat delivery as ambiguous; reconcile provider state before a manual retry. |
SIGNING_ERROR | The runtime could not create the HMAC signature. | No | Repair runtime cryptography support or rotate the account secret. |
EXECUTION_ERROR | An unexpected local error occurred. | No | Inspect redacted logs and configuration. |
error.ambiguous is true after transport begins, even when retryable is false. The module deliberately distinguishes “might have committed” from “safe to automatically retry.”
Example
Assume the bound IntegrationAccount contains:
accountId = https://hooks.example.com/valkyr/
apiKey = <encrypted bearer token>
password = <encrypted HMAC secret>
status = READY
Configuration and input:
{
"path": "events/orders",
"method": "POST",
"payload": {
"type": "order.ready",
"orderId": "42",
"amount": 12900,
"currency": "USD"
},
"idempotencyKey": "order-42-v1",
"correlationId": "workflow-run-7",
"timeoutMs": 10000,
"captureResponse": true
}
The request targets https://hooks.example.com/valkyr/events/orders and includes account-managed authorization and signature headers. A successful provider response produces a result like:
{
"status": "success",
"method": "POST",
"httpStatus": 202,
"targetHost": "hooks.example.com",
"response": {
"accepted": true,
"eventId": "evt_42"
},
"responseTruncated": false,
"attempts": 1
}
Notes
- Pagination: Webhook Relay sends one event and has no pagination contract.
- Limits: Request JSON is capped at 512 KiB; captured responses are capped at 256 KiB; paths at 1,024 characters; correlation and idempotency headers at 200 characters; timeout at 30 seconds.
- Idempotency: The module is not inherently idempotent. It forwards an optional provider key but never assumes the destination honors it.
- Retries: Every network write is single-attempt. Redirects are not followed. Reconcile ambiguous deliveries before any manual retry.
- API constraints: Only HTTPS account endpoints and JSON
POST,PUT, orPATCHrequests are supported. - Destructive behavior: PUT or PATCH may replace or alter remote data. Provider semantics and approval policy govern the consequence.
- Credentials: Bearer and HMAC material remain in IntegrationAccount SecureFields and are excluded from outputs, provider failures, request payloads, and logs.
- Unverified boundary: Deterministic tests cover request construction, secure account resolution, HMAC headers, destination binding, redirect/non-retry behavior, bounds, response redaction, scanner metadata, and failure normalization. A live third-party webhook is not exercised without an authorized IntegrationAccount.