HTTP action

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 as a named action.

Free trial · From $19/mo · No credit card required

The problem

There is always one more service. An internal admin API, a chat tool with no Slack equivalent, a staging webhook, a side project a teammate runs. You need a way to fire a templated HTTPS request to any URL when a Stripe event happens, with proper retries and signing, without standing up a relay server or paying Zapier per task. The HTTP action is that escape hatch.

Configure a URL, method, headers, and a JSONata-templated JSON body referencing the Stripe event. Enable HMAC signing so the receiver can verify the request is really from your workflow. Outbound retries on 5xx, surfaces 4xx errors immediately, respects retry-after on 429, and dedupes on the Stripe invocation id so a retry doesn't fire the request twice. JSONata covers most ad-hoc transformations (currency conversion, conditional fields, derived booleans) inside the action config, so the receiver does not need to know it is talking to Stripe.

How the HTTP action works

  1. 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. 2

    Template the body with JSONata

    Write a JSON body that references the Stripe event. JSONata supports conditionals ($contains, $boolean), arithmetic, date formatting, and array reshaping, so the payload can be shaped to whatever the receiver expects without a separate transform step.
  3. 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. 4

    Tune retries and timeouts

    By default, the action retries 5xx with exponential backoff up to 24 hours, respects 429 retry-after, and fails fast on 4xx. Override the policy per action if your receiver needs different semantics — for example, 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.

URL, headers, JSONata body, optional HMAC signature. The action you reach for when none of the six branded ones fit.

Outbound vs custom webhook handler

Outboundcustom webhook handler
Hosted relay to maintain
Signature verification + retries built-inyou build it
Idempotent on Stripe invocation idyou build it
Setup time≈4 minutesHalf a day to a week
JSONata body templating
Pricing modelFrom $19/mo flatHosting + dev time
Lives inside the Stripe Dashboard

Frequently asked questions

How is this different from Stripe's native webhook endpoints?+
Stripe's native endpoints send a fixed payload to a URL. The HTTP action lets you shape the payload per-workflow with JSONata, mix in derived fields, route to different URLs by event, and chain it with other workflow steps. It's a workflow step, not a global webhook.
Can I call non-HTTPS endpoints?+
No. HTTPS only, with TLS 1.2 or higher. We don't proxy through HTTP because we don't want signed payloads going over plaintext. If you need to hit something internal, expose it via a reverse proxy with TLS termination or use the Postgres action directly.
What's in the HMAC signature?+
An HMAC-SHA256 of the request body using a shared secret you configure in Outbound. We send it as the X-Outbound-Signature header, with the timestamp as X-Outbound-Timestamp. Same shape as Stripe's signature so you can reuse the verification code you already wrote.
How are 4xx errors handled?+
Fail fast. A 4xx means the receiver rejected the request and retrying won't help — the execution log records the status, response body, and stops. 5xx and 429 retry with exponential backoff. The retry policy is configurable per action if you want to retry 4xx for testing.
Is there a payload size limit?+
1 MB per request body. Stripe events themselves rarely exceed 100KB even with expansions, so the limit is mostly there to keep one bad workflow from accidentally posting megabytes of data into someone's API. If you legitimately need more, talk to support.