# Connect Stripe

The restricted key HeyCMO needs, what each permission is for, and the webhook it sets up for you.

HeyCMO is an invitation-only, $0 operational Alpha for Stripe and Resend recovery emails. A person approves every message before it is sent. The guides also describe implementation capabilities outside the Alpha. Their presence here does not make them available in the pilot. [Start with the Alpha checklist](https://docs.heycmo.ai/getting-started/). Help: [alpha@heycmo.ai](mailto:alpha@heycmo.ai).

Setup → **Connect Stripe** and **Email sending**, or Settings → **Connections** → **Stripe and email sending** (the same forms). One paste per provider: HeyCMO checks the key with the provider, stores it encrypted (envelope encryption; AWS KMS once the operator turns it on), creates this workspace's webhook through the provider's API and stores the signing secret the provider returns. You never copy a tenant id or build a webhook by hand.

## Create a restricted key

Stripe → Developers → API keys → *Create restricted key*:

| Permission | Why |
| --- | --- |
| Invoices: **Read** | required — the dunning loop senses failed payments |
| Webhook Endpoints: **Write** | lets HeyCMO create its recovered-revenue webhook for you |
| Subscriptions: **Read** | optional, but without it the dunning loop cannot see that a subscription is paused, cancelled or set to cancel, so it may still email about that subscription's invoice; also the billing loops (churn, trial, expansion) |
| Customers: **Read** | optional — the billing loops (churn, trial, expansion); Customers: Read also lets HeyCMO email a customer whose card expires before a renewal (pre-dunning) |
| PaymentIntents: **Read**, Charges: **Read** | optional — lets a recovery email say why the payment failed (an expired card, a 3D Secure check, insufficient funds) and pick its button; without them the email uses the general copy and the invoice link |
| Customer portal: **Write** | optional — the recovery email's "Update card in 1 click" opens Stripe's card-update page directly (with Subscriptions: Read, so HeyCMO can check the subscription is charged to the customer's default card, the one that page changes); without it the button opens the invoice, where a new card pays |

Everything else: None. Paste the `rk_live_…` key → **Connect Stripe**. A key Stripe rejects, or one that cannot read invoices, is refused and nothing is stored. (`API`: `POST /v1/connect/stripe` with `{"restrictedKey":"rk_live_…"}`.)

Connecting again is safe: the existing webhook is reused, not duplicated. If Stripe ever reports failed signature checks on the HeyCMO endpoint, tick *Recreate the webhook* and connect again.

## Webhooks — usually nothing to do

Connecting Stripe and your email provider created both webhooks: the Stripe one feeds recovered-revenue attribution (`invoice.paid`, `invoice.payment_succeeded`), drafts the first recovery email as soon as a payment fails (`invoice.payment_failed`), the card-expiry email before a renewal (`invoice.upcoming`, and `customer.source.expiring` for a saved card that expires at the end of the month), sees new trials and subscriptions for the conversions you send back to your ad platforms (`customer.subscription.created`), and withdraws a waiting recovery draft when the invoice is paid, the subscription is paused or cancelled, or the invoice is written off (`invoice.marked_uncollectible`, `customer.subscription.paused`, `customer.subscription.updated`, `customer.subscription.deleted`); your email provider's one records delivery events and suppresses bounced and complained recipients. If you connected Stripe before an event was added to this list, HeyCMO adds it to the webhook it created in your Stripe account within a day (connecting again with the same key does it at once). Until Stripe has sent one failed payment, the first email goes with the next daily run, and the loop's run note says so. Setup's **Connect Stripe** and **Email sending** steps turn green only once their webhooks are set up, and Setup → **Advanced / manual setup** shows your workspace id and both endpoint URLs.

Only if a connect answered **"One manual step left"** — a Stripe key without Webhook Endpoints: Write, or a sending-only Resend key — the form shows the exact endpoint URL and events with copy buttons. Add that endpoint in the provider (Stripe → Developers → Webhooks → Add endpoint; Resend → Webhooks), then store the signing secret (`whsec_…`) it gives you in Setup → **Advanced / manual setup** or Settings → **Advanced** → **Integration secrets** → *Stripe webhook signing secret* / *Resend event webhook signing secret* (Postmark and SendGrid have their own fields there for a webhook added by hand). The URL is also on `GET /v1/connect` (`webhookUrl`), and your workspace id is on Settings → **Team & API keys**, with a copy button.

Without the email provider's webhook, dunning still runs but bounces and complaints cannot produce suppression. Without the Stripe one, recoveries are not attributed.
