New to Kestra?
Use blueprints to kickstart your first workflows.
Orchestrate JSON payload validation and reshaping with Kestra and JSONata. Check an inbound webhook for required fields, flatten nested line items, and forward only valid orders.
Inbound webhooks rarely arrive in the shape a downstream system wants, and forwarding a payload without checking it first is how a missing customer ID or an empty line-item array turns into a broken record somewhere else. This blueprint uses io.kestra.plugin.transform.jsonata.TransformValue to do both jobs in one pass: it checks the incoming order JSON for the fields the downstream order service actually requires, and reshapes the nested structure into a flat record with computed totals, all with a single JSONata expression instead of a custom script.
order_webhook (io.kestra.plugin.core.trigger.Webhook) exposes a signed endpoint that receives the raw order JSON as {{ trigger.body }}.validate_and_reshape (io.kestra.plugin.transform.jsonata.TransformValue) runs a single JSONata expression against the payload. It builds a missing_fields list by checking order.id, order.customer.id, order.currency, and a non-empty order.line_items array, then returns valid (true only when nothing is missing), the missing_fields list, and a flattened order object with order_id, customer_id, customer_email, currency, item_count, a computed total_amount ($sum of qty * unit_price across every line item, rounded with $round), and a reshaped line_items array with a per-line line_total.reject_invalid_payload (io.kestra.plugin.core.flow.If) checks {{ outputs.validate_and_reshape.value | jq('.valid') | first }}. When it's false, the nested fail_on_missing_fields (io.kestra.plugin.core.execution.Fail) stops the flow with the specific missing fields in the error message, so forward_to_order_service never runs for a bad payload.forward_to_order_service (io.kestra.plugin.core.http.Request) only executes after validation passes, POSTing the flattened order object to the downstream order service.notify_success confirms the order ID, item count, and total in Slack; the errors block posts a separate alert if validation rejects the payload or the downstream call fails.A webhook receiver with no validation step either crashes downstream on the first malformed payload, or you end up writing and maintaining a custom script just to check a few required fields and flatten some nested arrays. Kestra's TransformValue task runs that logic as a single, versioned, testable expression right in the flow, and the If/Fail combination makes rejection an explicit, alertable step instead of a silent downstream failure.
ORDER_WEBHOOK_KEY: the key segment for this flow's webhook URL. Treat it as a secret; anyone with it can trigger the flow.ORDER_SERVICE_URL: base URL of the downstream order-processing API.SLACK_WEBHOOK_URL: Slack incoming webhook URL./api/v1/{tenant}/executions/webhook/{namespace}/jsonata-webhook-payload-reshape/{ORDER_WEBHOOK_KEY}.{"order": {"id": "...", "customer": {"id": "...", "email": "..."}, "currency": "USD", "line_items": [{"sku": "...", "qty": 1, "unit_price": 9.99}]}} and confirm it forwards successfully.$missing array as the downstream service's contract grows, without touching any other task.forward_to_order_service for a database insert, a queue publish, or a call to another Kestra flow via Subflow.io.kestra.plugin.transform.jsonata.TransformItems instead when the webhook delivers a batch of orders as a JSON array rather than one at a time.reject_invalid_payload failures to a dead-letter queue or a review namespace so rejected payloads aren't just logged and dropped.