# Revenue from Stripe

> Connect your own Stripe account read-only and read MRR, new and churned subscriptions, revenue over time and recent payments.

**Note:**
Every plan can connect Stripe. **Free** reads the last 30 days and refreshes every six hours; **Indie** reads 90 days and **Builder** 24 months, refreshed hourly. Free and Indie include one connection per account, Builder one per project.

Nordva Launch can read your own Stripe account and report the numbers a small product needs: MRR, who started paying, who cancelled, and what was collected. The same connection lets [Contacts](/guides/contacts) show who became a customer and [Attribution](/guides/attribution) show which signup sources produce customers.

Launch only reads. It never creates, changes or refunds anything in your Stripe account, and it never sees card data.

Sell through Lemon Squeezy instead, or as well? See [Revenue from Lemon Squeezy](/guides/revenue-lemonsqueezy). A project can connect both; the numbers add up per currency.

## 1. Create a restricted key in Stripe

In Stripe, go to **Developers → API keys → Create restricted key**.

Stripe first asks how you will use the key. Choose the option for **providing the key to another website**, and enter:

| Field | Value |
|---|---|
| Name | `Nordva Launch` |
| URL | `https://launch.nordva.dev` |

Then set these five resources to **Read** and leave everything else at **None**. Do not pick Write anywhere:

| Resource | Why |
|---|---|
| Customers | Email, name, and your user id from metadata |
| Subscriptions | Status, amount, plan |
| Invoices | Paid invoices, for revenue |
| Products | Plan names |
| Events | The change feed Launch uses to stay up to date |

The key starts with `rk_live_`. A key starting with `rk_test_` connects a test-mode account, which is a good way to try this first.

**Caution:**
Never paste a secret key (`sk_live_…`). Launch rejects it and does not store it, but once a secret key has been pasted anywhere outside Stripe you should roll it.

## 2. Connect it in the dashboard

Open [**Settings → Revenue sources**](https://app.nordva.dev/revenue) in the dashboard and paste the key. Launch checks each of the five permissions before saving and, if one is missing, shows Stripe's own message naming it.

Connecting is only possible in the dashboard, behind your login. There is no API-key endpoint and no MCP tool for it, so a payment-account credential never travels through an API key or an AI conversation.

The first import starts immediately and runs in the background. After that, each connection syncs about once an hour.

### Optional: link your own user ids

If your Stripe customers carry your application's user id in metadata (for example `metadata.user_id`), enter that key name when connecting. Feedback and notifications you send with the same `user_id` then appear on the right person in [Contacts](/guides/contacts).

## 3. Read the numbers

All amounts are in the smallest currency unit (cents), reported per currency, and never converted or added together.

```bash
curl "https://api.nordva.dev/v1/revenue/summary?period_days=30" \
  -H "Authorization: Bearer nv_live_YOUR_SECRET_KEY"
```

```json
{
  "data": {
    "period_days": 30,
    "by_currency": [{
      "currency": "usd",
      "mrr_minor": 38000,
      "active_subscriptions": 21,
      "trialing_subscriptions": 2,
      "paying_customers": 20,
      "new_subscriptions": 4,
      "new_mrr_minor": 7300,
      "churned_subscriptions": 1,
      "churned_mrr_minor": 1500,
      "net_mrr_change_minor": 5800,
      "revenue_minor": 41200,
      "payments": 23
    }],
    "sync": { "status": "active", "last_synced_at": "2026-09-17T14:02:11.000Z", "livemode": true }
  }
}
```

| Endpoint | What it returns |
|---|---|
| `GET /v1/integrations/stripe` | Connection status, last sync, synced counts, last error |
| `GET /v1/revenue/summary?period_days=` | The summary above |
| `GET /v1/revenue/series?range=30d\|90d\|12m\|all` | Collected revenue per day (up to 90 days), ISO week (up to a year) or month, with empty buckets filled with zero |
| `GET /v1/revenue/events?type=payment\|cancellation&limit=` | Recent payments and cancellations with customer email and plan. For a cancellation, `amount_minor` is the monthly amount that stopped |

History reaches 30 days on Free, 90 days on Indie and 24 months on Builder. A longer `range` is shortened and the response says so with `clamped_to_plan: true`.

## What MRR means here

MRR is an operational number, not an accounting one:

- Yearly plans are divided by twelve, weekly plans multiplied by 52 ÷ 12, and quantities are included.
- A coupon counts when it is `forever`, or `repeating` and still running. A `once` coupon does not change the run rate.
- Metered and tiered prices have no fixed amount and count as zero. A subscription with paused collection counts as zero.
- `active` and `past_due` subscriptions count. Trialing subscriptions are reported separately.
- Revenue is paid invoices. Refunds are not subtracted.

## Sync status

Every response includes `sync`:

| `status` | Meaning |
|---|---|
| `backfilling` | The first import is still running. Numbers are incomplete. |
| `active` | Up to date as of `last_synced_at`. |
| `revoked` | Stripe no longer accepts the key (deleted or rolled). Numbers are as of the last sync. Disconnect and connect a new key. Launch emails the account owner when this happens. |
| `error` | The key lost a read permission. `last_error` on the status endpoint names it. |

## Disconnecting

**Disconnect** in the dashboard deletes the stored key and every synced customer, subscription and invoice. Nothing changes in Stripe. Deleting the project does the same.

## From an MCP client

Ask your assistant things like "what's my MRR and did it grow this month?" or "who cancelled this week?". It uses `get_revenue_summary`, `get_revenue_series`, `list_recent_payments` and `get_stripe_connection_status`. The assistant cannot connect Stripe and will never ask you for a key.

## Errors

| Code | Meaning |
|---|---|
| `STRIPE_NOT_CONNECTED` | Neither Stripe nor Lemon Squeezy is connected to this project. |
| `STRIPE_KEY_NOT_RESTRICTED` | The pasted key is not an `rk_` key. If it was a secret key, roll it. |
| `STRIPE_KEY_INVALID` | Stripe did not recognise the key. |
| `STRIPE_KEY_MISSING_PERMISSION` | One of the five read permissions is missing. The message names it. |
| `STRIPE_ALREADY_CONNECTED` | Disconnect the current key first. |
| `STRIPE_CONNECTION_LIMIT_REACHED` | Free and Indie include one connection per account. Builder includes one per project. |

Source: https://docs.nordva.dev/guides/revenue-stripe/
