How to Route Stripe Webhook Events to Slack and MongoDB
Receive signed Stripe events on a Spojit Webhook trigger, branch on the event type with a Condition node, log every event to MongoDB, and post a formatted Slack alert when a payment fails.
What This Integration Does
Stripe emits a steady stream of events: charges succeed, invoices finalize, payment intents fail. Most of those events are worth keeping for later analysis, but only a few need a human to react right away. This Spojit workflow gives you both: it writes a durable record of every incoming Stripe event into a MongoDB collection so you have an audit trail you can query, and it raises a clear Slack alert the moment a payment actually fails so your finance or support team can follow up before the customer churns.
The workflow is event-driven. Stripe sends an HTTP POST to your Spojit Webhook trigger URL each time a subscribed event fires; Spojit verifies the request signature, parses the JSON body, and starts a run. A Transform node pulls out the fields you care about, a MongoDB Connector node inserts a log document, and a Condition node checks whether the event is a failed payment. Failures route to a Slack Connector node that posts a message; everything else simply ends after logging. Each Stripe delivery is one run, so re-sent or duplicated webhook deliveries each produce their own run and their own log document unless you enable dedup or guard against it in the database.
Prerequisites
- A Stripe connection in Spojit (added under Connections) if you also want to enrich events with extra Stripe lookups. The webhook itself does not require the Stripe connection, but the signing secret does.
- A signing connection for the webhook. Create one under Connections using the Custom HMAC scheme and paste your Stripe webhook signing secret (the value beginning with
whsec_). This lets Spojit verify each delivery. - A MongoDB connection with write access to the database and collection where you want to store event logs (for example a
stripe_eventscollection). - A Slack connection with permission to post messages to your target channel, and the channel ID or name you want alerts sent to.
- Access to your Stripe Dashboard to register the webhook endpoint and choose which event types to send.
Step 1: Add a Webhook trigger
Create a new workflow and add a Trigger node, then set its type to Webhook. Spojit generates a unique URL for this workflow. In the trigger configuration, attach the signing connection you created with the Custom scheme so Spojit validates the Stripe signature on every request. The trigger output is the parsed JSON body Stripe sends, available downstream as {{ input }}. A Stripe event payload looks like this:
{
"id": "evt_1NXyz...",
"type": "payment_intent.payment_failed",
"created": 1699999999,
"data": {
"object": {
"id": "pi_1NXabc...",
"amount": 4900,
"currency": "usd",
"status": "requires_payment_method",
"last_payment_error": { "message": "Your card was declined." }
}
}
}
Copy the trigger URL. You will register it in Stripe in the final step. The webhook responds 202 with an executionId as soon as it accepts the request, so Stripe sees a fast acknowledgement while your run continues in the background.
Step 2: Normalize the event with a Transform node
Add a Transform node after the trigger to reshape the raw Stripe payload into a flat, predictable record. Reading nested fields once here keeps the rest of the workflow readable. Map the fields you want to keep, for example:
{
"eventId": "{{ input.id }}",
"eventType": "{{ input.type }}",
"objectId": "{{ input.data.object.id }}",
"amount": "{{ input.data.object.amount }}",
"currency": "{{ input.data.object.currency }}",
"status": "{{ input.data.object.status }}",
"failureMessage": "{{ input.data.object.last_payment_error.message }}",
"createdAt": "{{ input.created }}"
}
Give the node an output variable such as event so later steps reference {{ event.eventType }}, {{ event.amount }}, and so on. Stripe amounts are in the smallest currency unit (cents for USD), so divide by 100 when you display them.
Step 3: Log the event to MongoDB
Add a Connector node in Direct mode, select the MongoDB connector, and choose the insert-documents tool. Set the target database and collection (for example stripe_events), then pass a single document built from your Transform output. Mapping it explicitly keeps the stored record clean:
{
"eventId": "{{ event.eventId }}",
"eventType": "{{ event.eventType }}",
"objectId": "{{ event.objectId }}",
"amount": "{{ event.amount }}",
"currency": "{{ event.currency }}",
"status": "{{ event.status }}",
"failureMessage": "{{ event.failureMessage }}",
"receivedAt": "{{ event.createdAt }}"
}
This node runs for every event type, so the collection becomes a complete history of what Stripe sent. Because it is Direct mode, the call is deterministic and costs no AI credits. If you later want to avoid duplicate rows from re-sent deliveries, you can switch to the update-documents tool with an upsert keyed on eventId instead.
Step 4: Branch on the event type with a Condition node
Add a Condition node after the MongoDB step. Configure the condition to test whether {{ event.eventType }} equals payment_intent.payment_failed (add invoice.payment_failed or charge.failed as additional matches if you also send those events). The True output handle leads to the Slack alert; leave the False output unconnected so non-failure events simply finish after being logged in Step 3.
If you prefer Spojit to scaffold this for you, open Miraxa, the intelligent layer across your automation, and ask: "Add a Condition node that checks if {{ event.eventType }} is payment_intent.payment_failed and connect the true branch to a Slack message node." Miraxa places and wires the nodes on the canvas, and you fine-tune the details in the properties panel.
Step 5: Post a formatted Slack alert on the True branch
From the Condition node's True handle, add a Connector node in Direct mode, select the Slack connector, and choose the send-message tool. Set the target channel (for example #payments-alerts or its channel ID) and compose a templated message that surfaces the key facts at a glance:
:rotating_light: *Payment failed*
Event: {{ event.eventType }}
Payment intent: {{ event.objectId }}
Amount: {{ event.amount }} {{ event.currency }}
Reason: {{ event.failureMessage }}
Because this node sits behind the Condition node, it only fires for failed payments, so the channel stays signal-rich. If you want a richer layout, the send-message tool accepts Slack block content as well as plain text.
Step 6: Register the endpoint in Stripe and enable the workflow
In your Stripe Dashboard, add a webhook endpoint pointing at the trigger URL from Step 1, and subscribe it to the event types you want to handle (at minimum payment_intent.payment_failed plus any success or invoice events you want to log). Stripe shows the signing secret for that endpoint; confirm it matches the whsec_ value in your Custom signing connection. Save the workflow, then enable it so it starts accepting live deliveries.
Tips
- Send a broad set of event types to the same workflow and let the MongoDB step log them all; the Condition node decides which ones also alert. This keeps one workflow as your single Stripe intake point.
- Stripe amounts are integers in the smallest currency unit. Use a Math connector node or a Transform expression to divide by 100 before showing a currency value in Slack.
- Index the
eventIdfield in your MongoDB collection so duplicate-delivery checks and later lookups stay fast. - To enrich an alert with more detail (for example the customer name behind a failed charge), add a Stripe Connector node using
get-customerbefore the Slack step and reference its output in the message.
Common Pitfalls
- Signature mismatch: if the secret in your Custom signing connection does not exactly match the endpoint's
whsec_value, every delivery is rejected. Each Stripe endpoint has its own secret, so re-check it if you recreate the endpoint. - Duplicate deliveries: Stripe retries until it receives a success response and can send the same event more than once. Either enable dedup on the trigger using the event-id header or upsert on
eventIdin MongoDB to avoid duplicate log rows. - Missing nested fields: not every event includes
last_payment_erroror the samedata.objectshape. Reference fields defensively in the Transform node and expect some values to be empty for non-failure events. - Alert noise: connecting Slack to the False branch (or skipping the Condition node) floods your channel with successful-payment messages. Keep Slack strictly on the True branch.
Testing
Before pointing production traffic at it, validate on a small scope. In the Stripe Dashboard use the "Send test webhook" action on your endpoint, or trigger a failed payment in Stripe test mode, and watch the run appear in your Spojit execution history. Confirm a document landed in the MongoDB collection with the expected fields, then verify the Condition node routed a failed-payment event to Slack and that a success event logged without posting. Once a handful of test events flow correctly, switch Stripe to live mode and enable the workflow.