# Going-live checklist

Everything to check before real customers pay: from sandbox and test mode to a live integration, a tested purchase and your customer emails.

Work through this list once, before your first real sale. Each item links to the dashboard screen or the guide that covers it. The boxes are remembered in this browser, so you can come back to it.

## Product and signing key

- [ ] The product exists, with its final **Product name** (customers see it in key emails and the portal) and **License key prefix**. See [Products](/docs/dashboard/#products).
- [ ] **Grace period** and **Offline period** are chosen on purpose: a shorter offline period means refunds and revocations reach apps sooner, a longer one tolerates customers who work offline. See [how changes reach installed apps](/docs/concepts/#how-changes-reach-installed-apps).
- [ ] The account owner exported the private signing keys once (**Export private keys** on the product page) and stored the file in a vault. See [signing without Keyrail](/docs/offline-certificates/#signing-without-keyrail).

## Plans

- [ ] One plan per thing you sell, with its final **Code** (it cannot change: it is in certificates and price maps) and **Kind**. See [Plans](/docs/dashboard/#plans).
- [ ] **Machines per seat**, **Entitlements**, **Max major version** (perpetual plans) and any **Offline days** override are set.

## Payment provider in live mode

- [ ] A **live** integration exists for the product: **Integrations → Connect provider**, **Environment: Live**. Keep the sandbox integration for tests: one integration per environment. See [Integrations](/docs/dashboard/#integrations).
- [ ] Paddle: a notification destination in your **live** Paddle account points to the live integration’s **Webhook URL**, with every event listed in the [Paddle guide](/docs/paddle/#2-add-a-notification-destination-in-paddle). Lemon Squeezy: the webhook is created outside test mode, with the [listed events](/docs/lemon-squeezy/#2-add-the-webhook-in-lemon-squeezy).
- [ ] The live destination’s secret is saved in the live integration (**Save secret**); the badge no longer says **Secret missing**.
- [ ] Paddle, optional: a live Paddle API key with `customer.read` permission is saved, so licenses are created without waiting for `customer.created`.
- [ ] The **Price map** of the live integration lists every **live** price or variant you sell (monthly, yearly, each currency, upgrades). Paddle sandbox and live price IDs are different: copy them from the live account.

## API keys

- [ ] Your backend uses a secret key created for production (**API keys → New secret key**), stored in its secret manager, never in an app or a repository. See [API keys](/docs/dashboard/#api-keys).
- [ ] Keys used during development that you no longer need are revoked. A key has full access to the account.

## Your app

- [ ] The release build embeds the right **Product ID** and the public keys as a map (`kid` → key), and uses the production API base (no local test URL left over).
- [ ] The app version passed to the SDK is the real one, so **Max major version** applies.
- [ ] Only a valid state unlocks paid features, and every other reason is handled with a clear message, including `activation_limit` (link to the customer portal) and `version_not_covered`. See the [integration checklist](/docs/security/#integration-checklist) and [troubleshooting](/docs/troubleshooting/).
- [ ] The app offers a way to deactivate the machine, or points to the customer portal, so customers can move their license.

## Test the whole flow

Run it once in sandbox or test mode, then once in live mode with a real card and a refund.

- [ ] **Purchase**: a checkout creates a license in **Licenses**, and `webhook.received` shows *processed* in **Events**.
- [ ] **Email**: the customer receives the key, from the expected sender, in the expected language.
- [ ] **Activation**: the key activates the app; the machine appears in **Machines** on the license page.
- [ ] **Offline**: with the network off, the app still starts.
- [ ] **Renewal** (subscriptions): after a renewal, **Paid until** moves forward.
- [ ] **Refund**: a full refund sets the license to `refunded`, and the app loses access at its next refresh (`license_refunded`).
- [ ] **Free a machine**: **Free this machine** in the dashboard, or the customer portal, lets the key activate on another computer.

## Customer emails and portal

- [ ] **Account & usage › Customer e-mails**: **Sender name**, **Sender address**, **Reply-to address** and **Default language** are set. See [custom sender and languages](/docs/custom-sender/).
- [ ] Your sending domain shows **verified**, so key emails come from your own address. See [verify your domain](/docs/custom-sender/#verify-your-domain).
- [ ] **Customer portal linked from the e-mails** is the one you want: **Hosted portal**, **My own portal** or **No portal link**. See [customer portal](/docs/customer-portal/).
- [ ] Your app’s “Manage license” menu and your website link to that portal.

## Your Keyrail plan

- [ ] **Account & usage › Usage**: your expected number of active licenses and products fits your plan. Limits are soft and never block your customers, but only a plan with room for it lets you create another product. See [pricing](/pricing/).
- [ ] The person who should receive limit warnings and billing emails is the account owner.
