How to Trigger a Manual Cross-System Customer Offboarding Flow

Build a Spojit workflow where you start offboarding on demand with a Manual trigger, gate the irreversible actions behind a Human approval, then fan out in parallel to cancel the customer's Stripe subscriptions, archive their record to MongoDB, and post a Slack notice.

What This Integration Does

When a customer churns or requests account closure, several systems have to be touched at once: billing has to stop, a record has to be preserved for compliance and analytics, and the team has to be told. Doing this by hand is slow and easy to get wrong, and skipping a step (an uncancelled subscription, an unlogged closure) costs money or breaks an audit trail. This workflow turns the whole sequence into a single deliberate action: an operator passes the customer's identifiers, one approver signs off, and Spojit executes the rest consistently every time.

The run model is operator-initiated and gated. A Manual trigger starts the run with a small payload you supply (the customer's Stripe id, email, and an optional reason). A Human approval node pauses the workflow so a named approver confirms the offboarding in the Approvals inbox before anything destructive happens. Once approved, a Parallel node fans out into three concurrent branches: a Connector node cancels the Stripe subscriptions, a Connector node inserts an archive document into MongoDB, and a Connector node posts to Slack. Because the run is manual and gated, it never fires on its own; re-running it for the same customer is safe to the extent that cancellation and archival are idempotent (cancelling an already-cancelled subscription is a no-op, and you can de-duplicate the archive on customer id).

Prerequisites

  • A Stripe connection with permission to read and cancel subscriptions. See the Stripe connector article for setup.
  • A MongoDB connection pointed at the database and collection you will use as the offboarding archive (for example a collection named offboarded_customers). See the MongoDB connector article.
  • A Slack connection with access to the channel you want to notify. See the Slack connector article.
  • At least one approver set up in your workspace (a User, Role, or Team) to fill the approval slot. Review Using Human Approval Nodes first.
  • The Stripe customer id (cus_...) you intend to offboard, ready to paste into the Manual trigger when you run it.

Step 1: Start with a Manual trigger

Create a new workflow and add a Trigger node set to type Manual. A Manual trigger runs when you press the Run button, and its output is exactly the request body you pass in. Define a small, predictable shape so downstream nodes can reference it. When you run the workflow you will supply:

{
  "stripeCustomerId": "cus_QabC123XyZ",
  "email": "jordan@example.com",
  "reason": "Requested account closure"
}

Downstream nodes read these as {{ trigger.stripeCustomerId }}, {{ trigger.email }}, and {{ trigger.reason }}. Keeping the payload minimal makes the workflow reusable for any customer.

Step 2: List the customer's active subscriptions

Add a Connector node in Direct mode using the Stripe connector and the list-subscriptions tool. Direct mode is right here because this is a single, deterministic call with no AI cost. Map the inputs so it only returns the subscriptions that matter:

  • customer set to {{ trigger.stripeCustomerId }}
  • status set to active so you do not try to cancel already-canceled records

Set the node's output variable so later steps can read it, for example subscriptions. The returned list gives you each subscription id to cancel in Step 5. If the list comes back empty, there is nothing to cancel and the cancellation branch will simply do no work.

Step 3: Gate the offboarding with a Human approval

Add a Human node immediately after the subscription lookup and before any destructive action. This is the safety gate: the workflow pauses here until an approver responds in the Approvals inbox at /approvals. Configure it:

  • Label: Confirm customer offboarding
  • Message: a clear summary using upstream variables, for example Offboard {{ trigger.email }} ({{ trigger.stripeCustomerId }}). Reason: {{ trigger.reason }}.
  • Urgency: Normal (raise to High for time-sensitive closures)
  • Timeout (minutes): optional; leave blank for no deadline, or set a value after which the request is treated as a reject
  • Approval slots: add one slot and place your approver atom in it (a User, Role, or Team). Approval completes only when every slot is satisfied.

If the request is approved the node outputs { approved: true, approvalId, outcome: "APPROVED" } and the run continues. If it is rejected or times out, the workflow halts; there is no downstream "on reject" branch, which is exactly what you want for an irreversible action.

Step 4: Fan out with a Parallel node

After the approval, add a Parallel node so the three independent actions run concurrently rather than one after another. Create three branches: Cancel billing, Archive record, and Notify team. Each branch holds one Connector node. Running them in parallel keeps the total run time close to the slowest single branch rather than the sum of all three. The branches do not depend on each other, so order does not matter.

Step 5: Cancel the Stripe subscriptions

In the Cancel billing branch, add a Connector node using the Stripe connector. Stripe's subscription list is read with list-subscriptions (from Step 2), and cancellation is performed with the raw-api-request tool, which lets you call any Stripe endpoint directly. Configure it to delete the subscription:

  • method: DELETE
  • path: /subscriptions/{{ subscriptions[0].id }}

If a customer can hold more than one active subscription, wrap this Connector node in a Loop node set to ForEach over {{ subscriptions }} and reference the current item's id in the path, for example /subscriptions/{{ subscription.id }}. Cancelling a subscription that is already canceled is a harmless no-op, which keeps re-runs safe. Use Agent mode here only if you want the intelligent layer to decide between immediate cancellation and cancel-at-period-end based on the reason; otherwise keep Direct mode for predictability.

Step 6: Archive the record to MongoDB

In the Archive record branch, add a Connector node in Direct mode using the MongoDB connector and the insert-documents tool. Point it at your archive database and collection (for example offboarded_customers) and pass a single document that captures who was offboarded, when, and why:

{
  "documents": [
    {
      "stripeCustomerId": "{{ trigger.stripeCustomerId }}",
      "email": "{{ trigger.email }}",
      "reason": "{{ trigger.reason }}",
      "approvalId": "{{ approval.approvalId }}",
      "offboardedAt": "{{ now }}"
    }
  ]
}

Storing the approvalId alongside the record gives you a defensible audit trail tying the closure to the person who approved it. To avoid duplicate archive rows on a re-run, you can instead use the update-documents tool with an upsert keyed on stripeCustomerId.

Step 7: Notify the team in Slack

In the Notify team branch, add a Connector node in Direct mode using the Slack connector and the send-message tool. Set the channel to the channel id or name your operations team watches, and compose a text message from the trigger and approval data:

Customer offboarded: {{ trigger.email }} ({{ trigger.stripeCustomerId }})
Reason: {{ trigger.reason }}
Approved by approval {{ approval.approvalId }}. Billing cancelled and record archived.

If you would rather resolve the channel from a person, you can precede this with a lookup-user-by-email call, but for a team channel the direct send-message call is simplest. Save the workflow; on save Spojit converts the canvas into the executable definition. You can ask Miraxa, the intelligent layer across your automation, to wire any missing connection, for example "Connect the Cancel billing branch's Stripe node to the Parallel node."

Tips

  • Keep the destructive Stripe and MongoDB calls in Direct mode so each run behaves identically and spends no AI credits; reserve Agent mode for the rare case where you need judgment about how to cancel.
  • Use the Human node's Email approvers option so the first ten slot members are emailed the request, shortening the time the run sits paused.
  • Put the cancellation, archive, and Slack steps in the Parallel node rather than a sequence so a slow Slack post does not delay the billing cancellation.
  • If a customer may have several subscriptions, loop the cancellation over {{ subscriptions }} rather than hard-coding subscriptions[0], so none are missed.

Common Pitfalls

  • Placing the Human node after the cancellation makes the approval meaningless: always gate before any destructive action so a reject genuinely stops billing changes.
  • A rejected or timed-out approval halts the workflow with no downstream branch, so the archive and Slack steps will not run. That is intended; do not try to add "on reject" logic, which is not supported.
  • Referencing subscriptions[0].id when the list is empty leaves nothing to cancel; guard with a Condition node, or rely on the empty-list no-op, but verify in the execution log.
  • The Slack channel must be one your Slack connection can post to; a private channel the connection has not joined will fail the branch even though billing and archival succeeded.
  • Without an upsert key on the MongoDB write, re-running the workflow for the same customer creates duplicate archive documents.

Testing

Validate on a throwaway customer before trusting it on real accounts. Create a test Stripe customer with a single subscription, then press Run and pass that customer's cus_... id in the Manual trigger payload. Approve the request in the Approvals inbox and watch the execution log: confirm list-subscriptions returned the expected subscription, that the raw-api-request DELETE marked it canceled in your Stripe dashboard, that a document landed in your MongoDB archive collection, and that the Slack message arrived. Then run it once more and reject the approval to confirm the workflow halts before any of the three branches execute. Only after both paths behave correctly should you use it on a live customer.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.