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

# Stripe

> Read MRR, balance, and recent charges from your Stripe account into a block.

The Stripe connector pulls live numbers from your Stripe account on a refresh cadence (cron) or in real time (webhook push). Bind a block to a Stripe query, declare a projection, and the platform keeps `widget.data` fresh.

<Note>
  Stripe data sources are a **Builder beta** feature. Free workspaces can read this page, but connector creation and refresh tools return `PRO_REQUIRED`.
</Note>

## Add a Stripe data source

<Steps>
  <Step title="Go to the wizard">
    `/settings/data-sources/new/stripe` (or click "New data source" from `/settings/data-sources` and pick Stripe).
  </Step>

  <Step title="Paste a restricted API key">
    Read-only is sufficient — the connector never writes back to Stripe. Generate a restricted key from your Stripe dashboard scoped to `Read` on Subscriptions, Charges, and Balance.

    The plaintext is envelope-encrypted with your workspace's DEK before it's written to Convex. We never log or display it.
  </Step>

  <Step title="Optionally paste a webhook signing secret">
    Skipping this puts the source in **cron-mode** — the platform polls Stripe on a cadence you pick (`5`, `15`, `60`, `360`, or `1440` minutes).

    Providing a signing secret enables **push-mode** — Stripe POSTs subscription / charge / balance events to a Convex webhook endpoint and the platform refreshes within seconds. The cadence dropdown still controls a safety-net background poll.
  </Step>

  <Step title="Save">
    The wizard validates the key against Stripe's API and saves on success. You're sent back to the data-sources list with the new source visible (label, type, cadence, status).
  </Step>
</Steps>

## Query catalog

Three first-class queries. See `apps/web/convex/lib/connectors/stripeQueries.ts` for the implementations.

<AccordionGroup>
  <Accordion title="stripe.mrr">
    Sums the `unit_amount` of every active subscription, paginated across the full set.

    Result:

    ```ts theme={null}
    {
      amount_cents: number;
      currency: string;
    }
    ```

    Use for an MRR metric widget.
  </Accordion>

  <Accordion title="stripe.balance">
    Calls `/v1/balance` and returns the available + pending breakdown.

    Result:

    ```ts theme={null}
    {
      available_cents: number;
      pending_cents: number;
      currency: string;
    }
    ```

    Use for a "what's my Stripe balance" widget.
  </Accordion>

  <Accordion title="stripe.recentCharges">
    Lists the most recent N charges (default 10, max 100).

    Result:

    ```ts theme={null}
    {
      charges: Array<{
        id: string;
        amount_cents: number;
        currency: string;
        status: string;
        created_at_unix: number;
        customer_email: string | null;
      }>;
    }
    ```

    Use for a recent-activity list widget.
  </Accordion>
</AccordionGroup>

## Bind a block

In the block's `chatblocks.json`:

```json theme={null}
{
  "binding": {
    "dataSourceId": "<stripe-data-source-id>",
    "queryConfig": {
      "type": "stripe.mrr",
      "params": {}
    },
    "projection": {
      "fields": {
        "value": { "source": "amount_cents", "format": "currency" },
        "label": { "literal": "MRR" }
      }
    }
  },
  "widget": {
    "template": "metric",
    "data": { "value": "$0", "label": "MRR" }
  }
}
```

The aggregator runs `stripe.mrr` on the configured cadence, applies the projection to map `amount_cents` → a formatted currency string, writes the result into `widget.data`, and pushes to iOS.

Projection format helpers (`currency`, `number`, `percent`, `percent-with-sign`, `datetime`) live in `packages/shared/src/projection.ts`. They're pure functions — same input → same output, no side effects.

## Refresh modes

<Tabs>
  <Tab title="Cron-mode">
    Default. The platform polls Stripe at `cadenceMinutes` (5 / 15 / 60 / 360 / 1440). A 1-minute Convex cron scans active data sources and dispatches refreshes for any whose `lastRefreshAt + cadenceMinutes < now`.

    Per-binding failures don't poison the source — one failing query won't stop the others from refreshing. A source-level auth failure (revoked API key, etc.) sets `lastErrorMessage` on the source and surfaces in the data-sources list.
  </Tab>

  <Tab title="Push-mode">
    When you provide a webhook signing secret, Stripe POSTs events to:

    ```
    https://<convex-deployment>.convex.cloud/webhooks/stripe/<dataSourceId>
    ```

    The handler verifies the Stripe signature (5-minute replay window via the official SDK), dedupes by `event.id` in `webhookEvents`, and schedules a refresh.

    Push-mode and cron-mode coexist — the cron is a safety net if a webhook delivery is missed.
  </Tab>
</Tabs>

## Credential storage and audit

The credential blob is JSON: `{ apiKey, webhookSecret? }`. Encrypted via the workspace DEK (envelope encryption with a master KEK in Convex env). Every decryption — every refresh — writes an `auditLog` row stamped `actor: "system:scheduledRunner"` or `actor: "system:stripeWebhook"` so you can see exactly when the key was used.

See [Audit log](/concepts/audit-log) for the full event surface.

## What's next

<CardGroup cols={2}>
  <Card title="Postgres" icon="database" href="/connecting-data/postgres">
    Point at your own Postgres and write SQL queries with platform-side safety wrapping.
  </Card>

  <Card title="Webhook" icon="webhook" href="/connecting-data/webhook">
    Receive arbitrary JSON from your own systems with HMAC-signed delivery.
  </Card>
</CardGroup>
