Skip to main content

Webhook Ingest ExecModule

Overview

WebhookIngestModule converts one bounded JSON webhook payload into a deterministic event proposal for downstream workflow steps. Version 2 replaces the legacy implementation that generated random Contact and EngagementEvent IDs without persisting either record. It also stops returning the complete raw payload and makes the absence of persistence explicit.

The canonical module identifier is:

com.valkyrlabs.workflow.modules.marketing.WebhookIngestModule

This module is a local normalization boundary. It does not expose an HTTP endpoint, verify a provider signature, create a Contact, persist an EngagementEvent, or grant access to business data.

Usage

  1. Receive and authenticate the provider webhook through a trusted ingress layer.
  2. Pass the exact JSON body as payload; pass the provider delivery ID as idempotencyKey when available.
  3. Configure stable sourceName and eventType identifiers.
  4. Map only the fields needed by later steps through contactMapping.
  5. Require status: success, store or deduplicate by eventKey, and route eventProposal to a separately approved ACL-aware persistence step if a durable business object is required.

Inputs

InputRequiredDescription
payloadYesOne non-null JSON document, capped at 256 KiB of UTF-8.
idempotencyKeyNoCaller-owned provider delivery identifier, 1–128 bounded safe characters.
occurredAtNoRFC 3339 event time supplied by the trusted receiver.

payload remains in memory only for parsing and hashing. It is not copied into eventProposal, error, or logs.

Outputs

OutputDescription
statussuccess or error.
eventKeywebhook_ plus a deterministic SHA-256 digest derived from the source, event type, and provider idempotency key or payload digest.
payloadSha256SHA-256 digest of the accepted payload bytes.
sourceNameValidated source-system identifier.
eventTypeValidated normalized event type.
extractedFieldsOnly fields explicitly selected through contactMapping.
eventProposalNormalized proposal containing the event key, type, source, payload digest, optional occurrence time, and extracted data.
persistencePerformedAlways false.
errorSafe object with code, message, and retryable: false.

The module never returns contactId or engagementEventId because it does not create those records.

IntegrationAccount Requirements

None. WebhookIngestModule performs no network call and must not receive provider tokens, signing secrets, passwords, or other credentials.

Authenticate and rate-limit the inbound request before the workflow executes. If signature verification is needed, bind the secret to the ingress component's IntegrationAccount; do not place it in this module's payload or mapping.

Configuration

ConfigurationRequiredConstraint
sourceNameYesStable identifier using letters, digits, ., _, :, or -; maximum 128 characters.
eventTypeYesStable identifier using the same character set; maximum 64 characters.
contactMappingNoObject containing at most 32 output-field to simple JSONPath mappings. Default: {}.

Supported paths begin with $ and use field or numeric array segments, such as $.form.email or $.items[0].contact.email. Recursive descent, filters, scripts, wildcards, quoted bracket properties, and negative indexes are rejected. Each extracted value is capped at 16 KiB.

Legacy createContact: true is rejected. A merge strategy cannot authorize persistence, and no random fallback ID is generated.

Operations

The module has one operation: normalize one payload.

  1. Validate configuration and the payload byte limit.
  2. Parse exactly one non-null JSON document.
  3. Validate the optional idempotency key and event time.
  4. Resolve the bounded mapping allowlist.
  5. Hash the payload and derive eventKey.
  6. Return a side-effect-free proposal.

The operation is deterministic for the same configuration and inputs. When idempotencyKey is omitted, identical payload bytes produce the same key for the same source and event type.

Errors and Failure Modes

CodeMeaningRecovery
INVALID_JSONThe payload is not valid JSON.Send one valid JSON document. Parser context and payload bytes are not returned.
VALIDATION_ERRORA required identifier, payload bound, timestamp, mapping, or legacy persistence request is invalid.Correct the named input. Use a separate generated ACL-aware service for persistence.
EXECUTION_ERRORAn unexpected local normalization failure occurred.Inspect sanitized application diagnostics; retry only after correcting the runtime issue.

All errors are non-retryable at this module boundary. The trusted ingress layer owns provider acknowledgement and delivery retry policy.

Example

Configuration:

{
"sourceName": "webflow-main",
"eventType": "form_submit",
"contactMapping": {
"email": "$.form.email",
"firstName": "$.form.first_name"
}
}

Input:

{
"payload": "{\"form\":{\"email\":\"ada@example.com\",\"first_name\":\"Ada\"},\"provider_metadata\":{\"secret\":\"not-selected\"}}",
"idempotencyKey": "evt_provider_123",
"occurredAt": "2026-08-09T12:30:00Z"
}

Expected shape:

{
"status": "success",
"eventKey": "webhook_<sha256>",
"payloadSha256": "<sha256>",
"sourceName": "webflow-main",
"eventType": "form_submit",
"extractedFields": {
"email": "ada@example.com",
"firstName": "Ada"
},
"eventProposal": {
"eventKey": "webhook_<sha256>",
"type": "form_submit",
"channel": "webhook",
"sourceSystem": "webflow-main",
"payloadSha256": "<sha256>",
"occurredAt": "2026-08-09T12:30Z",
"extractedData": {
"email": "ada@example.com",
"firstName": "Ada"
}
},
"persistencePerformed": false
}

The unselected provider_metadata value is absent from every output.

Notes

  • Pagination does not apply; one execution accepts one JSON document.
  • The payload limit is 256 KiB, mappings are capped at 32, and each selected value is capped at 16 KiB.
  • The deterministic key supports deduplication but does not prove provider authenticity. Verify signatures and replay windows before invoking the workflow.
  • The module has no network access, provider API dependency, retry loop, destructive behavior, or persistence side effect.
  • persistencePerformed: false is a contract, not a transient status. Use generated owner/ACL-enforcing services for Contact or EngagementEvent writes.
  • The focused tests validate bounds, mapping behavior, deterministic keys, raw-payload non-disclosure, failure cleanup, and metadata discovery. Provider ingress, signature verification, rate limiting, and durable persistence remain external boundaries.