> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streamkap.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Stripe Webhook

> Stream Stripe events into Streamkap by registering Stripe webhooks against a dedicated Streamkap endpoint, with optional initial and on-demand snapshots via the Stripe REST API

## Overview

The Stripe Webhook source receives events from your Stripe account at a dedicated HTTPS endpoint and routes each event to a Kafka topic based on its resource type (customers, payment intents, charges, invoices, subscriptions, products, prices, payouts, refunds, etc.).

You point Stripe at the Streamkap-generated webhook URL, register the events you care about, and Streamkap takes care of parsing, key extraction, schema inference, and routing. For initial loads or ad-hoc backfills, the source can also snapshot data directly from the Stripe REST API using your Stripe secret key.

<Info>
  This connector is in **Beta**. Behaviors and defaults may change before general availability.
</Info>

## Prerequisites

* A Stripe account with **Administrator** access (required to manage webhooks and API keys).
* Your Stripe **Secret key** (`sk_live_xxx` for production or `sk_test_xxx` for test mode), available at [dashboard.stripe.com/apikeys](https://dashboard.stripe.com/apikeys).
* A Streamkap workspace with permission to create source connectors.
* A clear list of the Stripe resources you want to capture (customers, payment intents, invoices, etc.).

## How It Works

1. **Endpoint provisioning** — When you create the source, Streamkap generates a unique HTTPS webhook URL and an API key.
2. **Stripe subscriptions** — In Stripe Workbench, you register a webhook destination pointing at the Streamkap URL with the API key passed as a query parameter, and select the events to deliver.
3. **Event-type routing** — Each incoming request carries an event in its JSON body with a `type` field (`customer.created`, `payment_intent.succeeded`, etc.). The Stripe payload router takes the **first segment** as the resource — `customer.*` events go to the `customer` topic, `payment_intent.*` events go to `payment_intent`, and so on. The connector extracts `data.object` as the Kafka record value.
4. **Key extraction** — The router pulls the resource `id` (e.g. `cus_abc123`, `pi_xyz789`) from `data.object` and uses it as the Kafka message key, enabling upsert-style consumption downstream.
5. **Signature verification (recommended)** — When you provide the Stripe signing secret (`whsec_xxx`), each payload is verified against the `Stripe-Signature` header using HMAC-SHA256. Invalid payloads are rejected and (if configured) routed to the DLQ.
6. **Optional fan-out** — Stripe's list objects (line items, refunds, subscription items) can be fanned out into their own topics.
7. **Optional snapshot** — For initial loads or ad-hoc backfills, the source queries the Stripe REST API with cursor pagination and emits records alongside the live webhook stream.

## Streamkap Setup

### 1. Create the Source

1. Navigate to [Sources](https://app.streamkap.com/connectors/add?tab=Sources) and choose **Stripe**.
2. Give the source a memorable **Name** (for example, `stripe-prod`).

### 2. Connection Settings (Auth tab)

| Field              | Required               | Description                                                                                                                                                                                                               |
| ------------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Webhook URL**    | Auto                   | Read-only. Endpoint that Stripe posts events to. Generated on save. Append `?api_key=<API_KEY>` when registering the webhook destination in Stripe.                                                                       |
| **API Key**        | Auto                   | Read-only and encrypted. Sent by Stripe as the `api_key` query parameter to authenticate each request. Generated on save.                                                                                                 |
| **Signing Secret** | Strongly recommended   | Stripe webhook signing secret (`whsec_xxx`) from the webhook destination in Stripe Workbench. When set, every payload is verified against the `Stripe-Signature` header. Leave empty to skip verification (testing only). |
| **Stripe API Key** | Required for snapshots | Stripe Secret key (`sk_live_xxx` or `sk_test_xxx`) from [dashboard.stripe.com/apikeys](https://dashboard.stripe.com/apikeys). Used by Streamkap to call the Stripe REST API during snapshots. Encrypted at rest.          |

### 3. Schema (Stripe Resources)

In the **Schema** tab, choose the resources you want to capture. The default is `customer,payment_intent,charge,invoice`.

A resource is the **first segment** of the Stripe event `type` field — for example `customer.created`, `customer.updated`, and `customer.subscription.created` all map to the `customer` resource. Each resource becomes a Kafka topic of the same name.

The dropdown is pre-loaded with the resources most commonly used in billing / SaaS CDC scenarios:

| Category                    | Resources                                                                                                     |
| --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Customers and subscriptions | `customer`, `subscription_schedule`, `setup_intent`, `payment_method`, `mandate`                              |
| Payments and charges        | `payment_intent`, `charge`, `refund`, `payout`, `transfer`, `topup`, `application_fee`, `balance_transaction` |
| Billing                     | `invoice`, `invoiceitem`, `credit_note`, `coupon`, `promotion_code`, `plan`, `quote`, `tax_rate`              |
| Checkout and storefront     | `product`, `price`, `checkout`                                                                                |
| Other                       | `review`, `source`                                                                                            |

Any fan-out topics you configure (see [Fan-out](#fan-out)) are added automatically.

<Info>
  **Advanced — other Stripe resources.** Stripe has [many more event types](https://stripe.com/docs/api/events/types) than listed above (Issuing, Financial Connections, Identity, Treasury, Climate, Terminal, etc.). The Schema field accepts any resource name — just type the first-segment of the Stripe event type and Streamkap will route it.
</Info>

<Info>
  **Snapshot support is narrower** than webhook routing. Only `customer`, `charge`, `payment_intent`, `invoice`, `subscription`, `product`, `price`, `payout`, `refund`, `payment_method`, `balance_transaction`, `coupon`, `plan`, and `setup_intent` can be snapshotted via the Stripe REST API. All other resources stream via webhooks only.
</Info>

### 4. Settings

| Field                                      | Default         | Description                                                                                                                                                                                                                                                       |
| ------------------------------------------ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Unselected Resource Behavior**           | `DEFAULT_TOPIC` | What to do when an event arrives for a resource not in your Schema list. `DEFAULT_TOPIC`, `SKIP`, or `FAIL`.                                                                                                                                                      |
| **Default Topic for Unselected Resources** | `unknown`       | Topic used when behavior is `DEFAULT_TOPIC`. Only shown when the option above is set to `DEFAULT_TOPIC`.                                                                                                                                                          |
| **Include Event Metadata**                 | `true`          | Keep Stripe event metadata (`_event_id`, `_event_type`, `_event_created`, `_api_version`, `_livemode`, `_previous_attributes`) on the output record. Disable for upsert / state-table mode; keep enabled for audit-log mode and for deduplication on `_event_id`. |
| **Fan-out Fields** *(advanced)*            | *(empty)*       | Comma-separated list of nested lists to fan out into their own topics. Allowed values: `invoice.lines`, `charge.refunds`, `subscription.items`. See [Fan-out](#fan-out).                                                                                          |
| **Enable Dead Letter Queue** *(advanced)*  | `true`          | Failed records are written to a DLQ topic instead of crashing the connector.                                                                                                                                                                                      |

### 5. Save and Copy the Endpoint

Save the source. Copy the **Webhook URL** and **API Key** — you will need them in the next section to configure Stripe.

## Stripe Setup

Stripe does not support custom headers on webhook deliveries. Pass the Streamkap API key in the destination URL as a query parameter:

```
https://<webhook-url>?api_key=<API_KEY>
```

### 1. Get Your API Keys

1. Go to [dashboard.stripe.com/apikeys](https://dashboard.stripe.com/apikeys).
2. Copy your **Secret key** (`sk_test_xxx` for test mode, `sk_live_xxx` for production).
3. Use the toggle at the top of the dashboard to switch between test and live mode.
4. Paste the Secret key into the Streamkap source's **Stripe API Key** field in the Auth tab — this lets Streamkap call the Stripe REST API when you run a snapshot.

### 2. Register the Webhook in Workbench (recommended)

> **Note**: Stripe replaced the old "Developers → Webhooks" page with **Workbench**. New accounts use Workbench by default.

1. Go to [dashboard.stripe.com](https://dashboard.stripe.com) and open **Developers → Workbench** (or [dashboard.stripe.com/webhooks](https://dashboard.stripe.com/webhooks)).
2. Click **Add destination** (or **Create new destination**).
3. Enter your endpoint URL: `https://<webhook-url>?api_key=<API_KEY>` using the values copied from Streamkap.
4. **Important — choose the "Snapshot" payload format, NOT "Thin" payload.** Streamkap requires the full resource object in `data.object`. Thin-payload events (events whose `object` is `v2.core.event`) are not supported and should be left unselected.
5. Select the events you want to deliver. Recommended starting set:

| Category            | Events                                                                                                     |
| ------------------- | ---------------------------------------------------------------------------------------------------------- |
| Customers           | `customer.created`, `customer.updated`, `customer.deleted`                                                 |
| Subscriptions       | `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted`          |
| Payment intents     | `payment_intent.succeeded`, `payment_intent.payment_failed`, `payment_intent.canceled`                     |
| Charges             | `charge.succeeded`, `charge.refunded`, `charge.captured`, `charge.failed`                                  |
| Invoices            | `invoice.created`, `invoice.finalized`, `invoice.paid`, `invoice.payment_failed`                           |
| Products and prices | `product.created`, `product.updated`, `product.deleted`, `price.created`, `price.updated`, `price.deleted` |
| Payouts             | `payout.created`, `payout.paid`, `payout.failed`                                                           |
| Refunds             | `refund.created`, `refund.updated`                                                                         |
| Checkout            | `checkout.session.completed`, `checkout.session.expired`                                                   |

6. Click **Create**.
7. Open the destination's detail page and copy the **Signing secret** (`whsec_xxx`). Paste it into Streamkap's **Signing Secret** field in the Auth tab.

### 2 (Alternative). Register the Webhook via the Stripe API

For scripted setup, use curl with your Secret key:

```bash theme={null}
curl https://api.stripe.com/v1/webhook_endpoints \
  -u sk_live_YOUR_SECRET_KEY: \
  -d url="https://<webhook-url>?api_key=<API_KEY>" \
  -d "enabled_events[]"="customer.created" \
  -d "enabled_events[]"="customer.updated" \
  -d "enabled_events[]"="customer.deleted" \
  -d "enabled_events[]"="customer.subscription.created" \
  -d "enabled_events[]"="customer.subscription.updated" \
  -d "enabled_events[]"="customer.subscription.deleted" \
  -d "enabled_events[]"="payment_intent.succeeded" \
  -d "enabled_events[]"="payment_intent.payment_failed" \
  -d "enabled_events[]"="charge.succeeded" \
  -d "enabled_events[]"="charge.refunded" \
  -d "enabled_events[]"="invoice.paid" \
  -d "enabled_events[]"="invoice.finalized" \
  -d "enabled_events[]"="product.created" \
  -d "enabled_events[]"="product.updated" \
  -d "enabled_events[]"="price.created" \
  -d "enabled_events[]"="price.updated"
```

The response includes a `secret` field (`whsec_xxx`) — copy it into Streamkap's **Signing Secret** field.

Or subscribe to all snapshot-payload events:

```bash theme={null}
curl https://api.stripe.com/v1/webhook_endpoints \
  -u sk_live_YOUR_SECRET_KEY: \
  -d url="https://<webhook-url>?api_key=<API_KEY>" \
  -d "enabled_events[]"="*"
```

### Local Testing with the Stripe CLI

```bash theme={null}
stripe listen --forward-to "https://<webhook-url>?api_key=<API_KEY>"
```

The CLI prints a `whsec_xxx` signing secret on startup — paste it into Streamkap's **Signing Secret** field. Trigger test events with:

```bash theme={null}
stripe trigger customer.created
stripe trigger payment_intent.succeeded
```

## Event Routing Reference

The payload router inspects the event `type` field on each incoming request and routes by the first segment.

| Stripe Event Type                                                                      | Kafka Topic         | Default Key                              |
| -------------------------------------------------------------------------------------- | ------------------- | ---------------------------------------- |
| `customer.created`, `customer.updated`, `customer.deleted`                             | `customer`          | `{ id: cus_xxx }`                        |
| `customer.subscription.*`                                                              | `customer`          | `{ id: sub_xxx }`                        |
| `payment_intent.succeeded`, `payment_intent.payment_failed`, `payment_intent.canceled` | `payment_intent`    | `{ id: pi_xxx }`                         |
| `charge.succeeded`, `charge.refunded`, `charge.captured`, `charge.failed`              | `charge`            | `{ id: ch_xxx }`                         |
| `charge.dispute.*`                                                                     | `charge`            | `{ id: dp_xxx }`                         |
| `invoice.created`, `invoice.finalized`, `invoice.paid`, `invoice.payment_failed`       | `invoice`           | `{ id: in_xxx }`                         |
| `product.*`, `price.*`                                                                 | `product` / `price` | `{ id: prod_xxx }` / `{ id: price_xxx }` |
| `payout.*`, `refund.*`                                                                 | `payout` / `refund` | `{ id: po_xxx }` / `{ id: re_xxx }`      |
| `checkout.session.completed`, `checkout.session.expired`                               | `checkout`          | `{ id: cs_xxx }`                         |

Events whose action ends in `.deleted` set `__deleted: true` on the output record so downstream sinks can issue tombstones. `__op` is emitted as a Kafka header (`c` create, `u` update, `d` delete, `r` snapshot).

Each record additionally carries `__changeType` (`CREATE`, `UPDATE`, `DELETE`, `SUCCEEDED`, `FAILED`, `REFUNDED`, `PAID`, `FINALIZED`, `SNAPSHOT`, …) in the value.

## Fan-out

Stripe resources contain nested list objects (`{object: "list", data: [...]}`) for line items, refunds, and subscription items. Most warehouse destinations can't store these cleanly as a single column. Fan-out emits one record per list element to a dedicated topic so each child becomes its own row.

Set **Fan-out Fields** to a comma-separated list from the allowed set:

```
invoice.lines, charge.refunds, subscription.items
```

| Fan-out entry        | Generated topic      | Key shape                                 |
| -------------------- | -------------------- | ----------------------------------------- |
| `invoice.lines`      | `invoice_lines`      | `{ id: invoice_id, item_id: line_id }`    |
| `charge.refunds`     | `charge_refunds`     | `{ id: charge_id, item_id: refund_id }`   |
| `subscription.items` | `subscription_items` | `{ id: subscription_id, item_id: si_id }` |

Fan-out topics are added to the Schema list automatically — you do not need to register them manually.

Each fan-out record contains the list element's fields plus a `_ctx_event_id` for correlation back to the parent Stripe event.

<Warning>
  Fan-out topics do **not** emit tombstone records when list items are removed (for example when a line item is removed from an invoice). To handle deletions, configure the downstream sink for delete-and-reinsert on each parent event, or treat each parent record as the source of truth and full-replace the child rows.
</Warning>

## Signature Verification

Stripe signs every webhook with HMAC-SHA256. Streamkap parses the `Stripe-Signature` header (`t=timestamp,v1=hex_signature`) and verifies the payload against the signing secret you provide.

To enable verification, paste your destination's signing secret (`whsec_xxx`) into the Auth tab's **Signing Secret** field. The connector checks all `v1` signatures sent during secret rotation, so you can rotate without downtime.

Leave the field empty only when testing — anyone who knows the connector URL could otherwise submit fake events.

## Snapshot

Once your source is live, you can run a snapshot from the Streamkap UI to load historical data for selected resources. Webhook streaming continues to run in parallel — snapshot is a one-time backfill, not an alternative to live events.

### Snapshottable Resources

Only these resources can be snapshotted via the Stripe REST API:

| Resource              | API Endpoint               |
| --------------------- | -------------------------- |
| `customer`            | `/v1/customers`            |
| `charge`              | `/v1/charges`              |
| `payment_intent`      | `/v1/payment_intents`      |
| `invoice`             | `/v1/invoices`             |
| `subscription`        | `/v1/subscriptions`        |
| `product`             | `/v1/products`             |
| `price`               | `/v1/prices`               |
| `payout`              | `/v1/payouts`              |
| `refund`              | `/v1/refunds`              |
| `payment_method`      | `/v1/payment_methods`      |
| `balance_transaction` | `/v1/balance_transactions` |
| `coupon`              | `/v1/coupons`              |
| `plan`                | `/v1/plans`                |
| `setup_intent`        | `/v1/setup_intents`        |

All other resources you select in the Schema tab (`checkout`, `quote`, `credit_note`, `transfer`, etc.) **cannot be snapshotted** — they only receive live events via webhooks once you register them in Stripe. This is a limitation of the Stripe REST API, which exposes historical-fetch endpoints only for the resources above.

## Troubleshooting

| Issue                                  | Check                                                                                                                                                                                    |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401 Unauthorized` on webhook delivery | Verify the URL registered in Stripe includes `?api_key=<API_KEY>` with the value from the Streamkap source. Stripe does not pass custom headers, so the key must be in the query string. |
| Webhook not firing                     | In Stripe Workbench, open the destination and check delivery attempts. Stripe retries with exponential backoff up to 3 days in live mode (3 attempts in test mode).                      |
| `Callback URL is not allowed`          | The callback URL must be HTTPS in live mode. For local testing, use the Stripe CLI's `stripe listen --forward-to` command.                                                               |
| Snapshot does not start                | Confirm **Stripe API Key** is set in the Auth tab. The key needs read access for the resources you want to snapshot.                                                                     |
| `Access denied` on snapshot            | Verify the Stripe API key is in the correct mode (live vs test) for the data you expect, and that it has not been revoked.                                                               |
| Events arrive in the `unknown` topic   | The resource segment in the Stripe event `type` is not in your Schema list. Add it, or set **Unselected Resource Behavior** to `SKIP`.                                                   |
| Signature verification failed          | Verify the **Signing Secret** matches the `whsec_xxx` shown on the destination's detail page in Stripe Workbench. Re-copy it if you regenerated it.                                      |
| Thin-payload events rejected           | Streamkap only supports Snapshot (v1) payloads. Events with `"object": "v2.core.event"` in their body are thin and won't be processed. Deselect thin-only events in Workbench.           |
| Retry storms                           | Stripe retries up to 3 days in live mode. If the connector was down, expect a burst when it recovers. Check the destination's delivery attempts in Workbench.                            |

## Limitations

* The Stripe Webhook source is currently **Beta**.
* Stripe expects a `2xx` response promptly. The connector responds immediately and processes asynchronously; failures are captured in the DLQ when enabled.
* Snapshot supports only the 14 resources listed above. Other resources (`checkout`, `quote`, `credit_note`, `transfer`, etc.) are streamed via webhooks only.
* Only Stripe's **Snapshot (v1)** payload format is supported. Thin payloads (`v2.core.event`) are not processed.
* Stripe does not guarantee event ordering. Use the `_event_created` timestamp for ordering and `_event_id` for deduplication.
* Maximum payload size is 50 MB and maximum header size is 64 KB.
* The connector runs as a single task; horizontal scaling requires multiple source instances.

## See Also

* [Webhook Source](/webhook) — generic webhook source for any HTTP-capable producer.
* [Zendesk Webhook](/webhook-zendesk) — Zendesk-specific webhook source.
* [Salesforce CDC](/webhook-salesforce) — Salesforce CDC + Apex-trigger webhook source.
* [Shopify Webhook](/webhook-shopify) — Shopify-specific webhook source.
