GuideApril 9, 20268 min read

Building a Self-Healing Agent with Kite and Stripe

A practical walkthrough: wire up a Stripe webhook to an autonomous agent that automatically retries failed payments, notifies customers, and escalates to a human if three attempts fail.

Autonomous agents are most useful when they react to real-world events without human intervention. In this guide, we'll build a self-healing billing agent that listens to Stripe payment failures via Kite and automatically takes corrective action.

What We're Building

An agent that:

1. Receives payment_intent.payment_failed events from Stripe via Kite. 2. Automatically retries the payment after a short delay. 3. Sends a friendly customer notification on the first failure. 4. Escalates to a human support queue after three consecutive failures.

Prerequisites

  • A Kite account (free tier works)
  • A Stripe account with a webhook endpoint configured
  • Node.js 20+ or Python 3.11+

Step 1 — Install and Authenticate Kite

bash
curl -sSL https://getkite.sh/install | sh
kite auth login

After login, grab your endpoint token from the dashboard. You'll use it to scope which events your agent receives.

Step 2 — Configure the Stripe Webhook

In your Stripe dashboard, create a webhook endpoint pointing at your Kite relay URL:

https://relay.getkite.sh/hooks/<your-endpoint-token>

Subscribe to the payment_intent.payment_failed event type. Kite will automatically verify the Stripe-Signature header using your signing secret — set it in your Kite dashboard under Endpoint → Sources → Stripe.

Step 3 — Start the Listener

bash
kite listen stripe --forward http://localhost:3001/webhook

This opens a WebSocket to the relay and forwards verified Stripe events to your local server.

Step 4 — Write the Agent Logic

Here's a minimal Node.js handler:

typescript
import express from "express";

const app = express();
app.use(express.json());

const failureCount = new Map<string, number>();

app.post("/webhook", async (req, res) => {
  const event = req.body; // CloudEvent envelope

  if (event.type !== "com.stripe.payment_intent.payment_failed") {
    return res.sendStatus(200);
  }

  const { id: paymentIntentId, customer } = event.data;
  const failures = (failureCount.get(paymentIntentId) ?? 0) + 1;
  failureCount.set(paymentIntentId, failures);

  if (failures === 1) {
    await notifyCustomer(customer, "We had trouble charging your card. We'll retry shortly.");
    await scheduleRetry(paymentIntentId, delayMs: 30 * 60 * 1000); // 30 min
  } else if (failures === 2) {
    await notifyCustomer(customer, "Second payment attempt failed. Please update your payment method.");
    await scheduleRetry(paymentIntentId, delayMs: 24 * 60 * 60 * 1000); // 24 hours
  } else {
    await escalateToSupport({ paymentIntentId, customer, failures });
    failureCount.delete(paymentIntentId);
  }

  res.sendStatus(200);
});

app.listen(3001);

Step 5 — Run the Agent

Start your local server, then in a separate terminal:

bash
kite listen stripe --forward http://localhost:3001/webhook

Trigger a test payment failure from the Stripe dashboard. Within milliseconds, your agent receives the CloudEvent, increments the failure count, and takes action — no polling, no webhook servers exposed to the internet.

Taking It Further

  • Persistent state: Replace the in-memory Map with a database so the agent survives restarts.
  • OpenClaw integration: Use kite listen --sink openclaw to feed events directly into an OpenClaw agent loop.
  • Multi-source: Add kite listen github in the same terminal session to handle both Stripe and GitHub events in one stream.

This pattern — a lightweight Kite listener feeding a local agent — is how we recommend building reactive, event-driven agents. No infrastructure, no polling, just clean real-time events.