Skip to main content

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, or PATCH execution;
  • 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

  1. Create an IntegrationAccount for the destination webhook.
  2. Store the complete authorized HTTPS endpoint in IntegrationAccount.accountId.
  3. Optionally store a bearer token in the encrypted apiKey SecureField.
  4. Optionally store an HMAC-SHA256 signing secret in the encrypted password SecureField.
  5. Set the account status to READY and bind it through ExecModuleConfig.authConfig.integrationAccount.
  6. Configure an explicit JSON payload, then select a relative path and method if the account endpoint is a base route.
  7. Reconcile destination state before manually retrying any failed or timed-out delivery.

Inputs

InputTypeRequiredDefaultConstraints
payloadobjectYesNoneExplicit JSON object, maximum 512 KiB after serialization. Plaintext values under secret-bearing field names are rejected.
pathstringNoEmptyRelative path only, maximum 1,024 characters. Origins, traversal, query strings, fragments, and backslashes are rejected.
methodstringNoPOSTPOST, PUT, or PATCH.
idempotencyKeystringNoNoneMaximum 200 characters; no CR/LF. Forwarded as Idempotency-Key.
correlationIdstringNoNoneMaximum 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

OutputTypeMeaning
statusstringsuccess or error.
methodstringNormalized outbound method.
httpStatusintegerProvider status after transport completes.
targetHoststringAuthorized destination host; path, query, and credentials are omitted.
responseobject or stringOptional bounded, secret-redacted response.
responseTruncatedbooleantrue when capture exceeded 256 KiB.
attemptsinteger0 before transport or 1 after transport begins.
errorobjectSafe 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 fieldRequiredPurpose
accountIdYesComplete authorized HTTPS endpoint. Credentials, query strings, and fragments are rejected.
apiKeyNoBearer token sent only to the bound endpoint.
passwordNoHMAC-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

FieldTypeDefaultNotes
webhookAccountIntegrationAccountNoneRequired secure relationship.
pathtextEmptyRelative route appended to the account endpoint.
methodselectPOSTPOST, PUT, or PATCH.
payloadJSONNoneRequired explicit object.
idempotencyKeytextNoneCaller/provider correlation only; does not make every destination idempotent.
correlationIdtextNoneOptional trace identifier.
timeoutMsinteger10000From 1000 through 30000. Applied to connect, read, write, and whole-call timeouts.
captureResponsebooleantrueCaptures 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

CodeCauseAutomatic retryRecovery
VALIDATION_ERRORUnsafe account endpoint, raw credential/destination field, missing or oversized payload, unsafe path/header, unsupported method, or invalid timeout.NoCorrect configuration; no request was sent.
INTEGRATION_ACCOUNT_REQUIREDNo account is bound.NoBind the destination account.
INTEGRATION_ACCOUNT_NOT_READYAccount status is not READY.NoRepair authorization or account lifecycle state.
WEBHOOK_HTTP_<status>Provider returned non-2xx, including redirect status.NoInspect the provider under the same account and reconcile before retrying.
NETWORK_ERRORDNS, connect, TLS, write, read, or call timeout/transport failure.NoTreat delivery as ambiguous; reconcile provider state before a manual retry.
SIGNING_ERRORThe runtime could not create the HMAC signature.NoRepair runtime cryptography support or rotate the account secret.
EXECUTION_ERRORAn unexpected local error occurred.NoInspect 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, or PATCH requests 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.