Stripe webhook to any URL with JSONata
Fire a signed, JSONata-templated HTTPS request to any endpoint when a Stripe event happens. The escape hatch for every integration we don't ship natively.
Free trial · From $19/mo · No credit card required
The problem
There's always one more service. Your internal admin API. A Slack-incompatible chat tool. A staging webhook. A friend's side project. You need a way to send a stripe webhook to any url with a templated body and proper retries, without standing up a relay server or paying Zapier per task. The HTTP action is that escape hatch. Configure a URL, headers, method, and a JSONata-templated JSON body referencing the Stripe event. Add an HMAC-signed signature header so the receiver can verify it's really from your workflow.
Outbound retries on 5xx, dedupes on Stripe's invocation id, surfaces 4xx errors immediately, and respects retry-after on 429. It's the action you reach for when none of the other six fit. JSONata is powerful enough that most ad-hoc transformations — currency conversion, conditional fields, derived booleans — happen inside the action config, not in glue code on the other end.
How stripe webhook to any url works
- 1
Set URL, method, and headers
Pick the HTTPS endpoint, the method (POST, PUT, PATCH), and any static headers (Authorization, Content-Type). Header values support JSONata if you need them dynamic — useful for per-customer auth tokens. - 2
Template the body with JSONata
Write a JSON body referencing the Stripe event. JSONata gives you conditionals ($contains, $boolean), arithmetic, date formatting, and the ability to shape the payload to whatever the receiver expects — no transform service needed. - 3
Enable HMAC signing (recommended)
Outbound can sign every request with an HMAC over the body using a shared secret. Receivers can verify the signature, dedupe on the X-Outbound-Invocation-Id header, and trust the source — same pattern as Stripe's own webhooks. - 4
Tune retries and timeouts
Default: retry on 5xx with exponential backoff up to 24 hours, respect 429 retry-after, fail fast on 4xx. Override per-action if your receiver needs different semantics (e.g., one-shot fire-and-forget for non-critical sends).
trigger:
event: customer.subscription.updated
steps:
- action: outbound.http
config:
method: POST
url: https://admin.acme.com/internal/subscription-changed
headers:
Authorization: "Bearer {{ secrets.ADMIN_TOKEN }}"
Content-Type: application/json
sign_with: hmac-sha256
body:
customer_id: "{{customer.id}}"
new_plan: "{{items.data[0].price.nickname}}"
seats: "{{items.data[0].quantity}}"
renews_at: "{{$fromMillis(current_period_end * 1000)}}"
is_upgrade: "{{items.data[0].price.unit_amount > previous_attributes.items.data[0].price.unit_amount}}"Example workflow configuration
Screenshot of the HTTP action config in the Stripe Workflow builder. Shows a URL input, a method dropdown set to POST, a headers grid with one static header and one JSONata-templated header, a JSON body editor with templated values highlighted, an HMAC sign toggle enabled with a secret name reference, and a retries section with a max-retries field.
Outbound vs custom webhook handler
| Outbound | custom webhook handler | |
|---|---|---|
| Hosted relay to maintain | — | |
| Signature verification + retries built-in | you build it | |
| Idempotent on Stripe invocation id | you build it | |
| Setup time | ≈4 minutes | Half a day to a week |
| JSONata body templating | — | |
| Pricing model | From $19/mo flat | Hosting + dev time |
| Lives inside the Stripe Dashboard | — |
Frequently asked questions
How is this different from Stripe's native webhook endpoints?+
Can I call non-HTTPS endpoints?+
What's in the HMAC signature?+
How are 4xx errors handled?+
Is there a payload size limit?+
Related integrations
Write Stripe events to Postgres
When the target is your database, the Postgres action skips the HTTP layer entirely.
Send Stripe events to Discord
If the destination is Discord, the branded action handles rate limits and rich embeds for you.
Trigger Stripe from any HTTPS POST
The mirror image, coming with Inbound: external services POST in, a workflow fires.