# Dashboard guide

Every screen of the Keyrail dashboard, what it is for, what you can do there and what each setting means.

The dashboard at `https://keyrail.tech/app` is where you set up your products, connect your payment provider and look after your licenses. Everything it does goes through the [vendor API](/docs/vendor-api/), so anything you do here can also be scripted.

**Signing in.** There is no password. On the sign-in page, enter your work email and click **Email me a sign-in link**: you receive a magic link that works once and expires after 15 minutes (**Resend link** sends a new one). The session then lasts 30 days on that browser. New accounts start from **Create an account** on the same page.

**Navigation.** The sidebar lists the screens in three groups: **Overview**; **Products**, **Plans**, **Licenses**, **Customers**; **Integrations**, **API keys**, **Events**, **Account & usage**; then **Help**: **Documentation**, **What’s new** and **Contact support**. Every screen has a **Learn more** link under its title that opens the matching section of this guide (the API keys screen links to the vendor API reference). At the bottom, a meter shows your active licenses against your Keyrail plan, with the theme switch and **Sign out**. On a phone, the sidebar opens from the menu button.

When an account has several products, screens that work on one product at a time (Plans, the onboarding checklist) show a product selector.

## Overview

The home screen. It has three parts:

- **Get started**, the onboarding checklist, with six steps: **Create a product**, **Copy your product ID and public key**, **Create a plan**, **Connect Paddle or Lemon Squeezy**, **Create a test license** and **Integrate the SDK**. Each step opens in place with the form or the values it needs, so you can go from zero to a working activation without leaving the page. The payment step can be skipped with **Skip: I'll create licenses through the API**; the copy and SDK steps have **Mark as done**. Once every step is done, **Hide** removes the checklist.
- **Recent activity**: the last events of your account (webhooks, license changes, activations), with a link to **All events**.
- **Usage**: active licenses and products against your plan limits, and **Shortcuts** to find a license, the payment integrations, the API keys, the [going-live checklist](/docs/going-live/) and [troubleshooting](/docs/troubleshooting/).

The checklist's progress for the copy and SDK steps is remembered in your browser.

## Products

A product is a piece of software you sell. It owns its signing keys, its plans and its licenses. The list shows each product's name, **Product ID** (with a copy button), **Key prefix**, grace and offline periods. **New product** opens the creation form; your Keyrail plan limits how many products you can have (see [Account](#account)).

| Field | Meaning |
|---|---|
| **Product name** | Shown to your customers in key emails and the customer portal. |
| **License key prefix** | 2 to 8 uppercase letters or digits, `KR` by default: keys look like `KR-7K2M9-Q4X1C-8D3F6-H0J5N`. |
| **Grace period (days)** | `graceDays`, 0 to 365, default 14: how long a subscription license stays valid after a missed renewal. [Details](/docs/concepts/#paid-period-and-grace-period). |
| **Offline period (days)** | `offlineDays`, 1 to 365, default 30: how long an app keeps working without reaching Keyrail. A plan can override it. [Details](/docs/concepts/#how-changes-reach-installed-apps). |

Creating a product generates its first signing key, `k1`. Click a product to open its page:

- **Signing keys**: the **Product ID** and the **Active public key** to copy into your app (both are public), and the list of keys with their status (pending, active, retired).
  - **Rotate key** starts a two-phase [key rotation](/docs/offline-certificates/#key-rotation): it creates the next key as *pending* (it signs nothing yet); once your app update embedding it is out, **Activate k2** (or the next key ID) makes it sign every new certificate and retires the old one. Activation cannot be undone.
  - **Export private keys** (account owner only) downloads every signing key of the product with its private part, so that you can [sign certificates without Keyrail](/docs/offline-certificates/#signing-without-keyrail). Whoever holds the file can issue licenses: keep it in a vault. Each export is written to the audit log.
- **Settings**: the same fields as above, saved with **Save changes**.
- **Plans** of the product, with a link to **Manage plans**.
- **Integrate the SDK**: ready-to-paste snippets with your product ID and public keys.
- **View licenses** lists the licenses of this product.

## Plans

A plan is a license template inside a product: it says what a license grants. **New plan** opens the form; **Edit** changes an existing plan (everything but its code and kind).

| Field | Meaning |
|---|---|
| **Name** | Display name, e.g. "Pro (yearly)". |
| **Code** | Short slug (lowercase letters, digits, `-` and `_`, up to 32 characters). It is written in certificates (`plan`) and used in price maps, so it **cannot change** once the plan exists. |
| **Kind** | **Subscription** (valid while paid, plus the grace period), **Perpetual** (one-time purchase, optionally capped to a major version) or **Trial** (free for a fixed number of days). Fixed once created. |
| **Machines per seat** | `maxActivations`: the activation limit for one seat. A license's limit is this value × its seats. |
| **Trial length (days)** | Trials only: `trialDays`. |
| **Max major version** | Optional `maxMajor`: `2` covers every 2.x release but not 3.0, which reports `version_not_covered`. Empty: every version. |
| **Offline days** | Optional override of the product's offline period for this plan (1 to 365). Empty: the product's value. |
| **Entitlements** | Optional comma-separated feature flags (`export, ocr`), carried in the signed certificate for your app to check. |

More in [concepts: plans](/docs/concepts/#plans).

## Licenses

Every license, whether it came from a payment provider, the API or this dashboard. Search by **email, name or full license key** (keys are stored hashed, so a partial key cannot match), filter by status and product, then click a row to open the license. **New license** issues one by hand (a test key, a gift, a reseller sale): choose the plan, the **Customer email**, an optional **Customer name** and the **Seats**; for a subscription, **Paid until** (the grace period is added on top). Tick **E-mail the license key to the customer** to send it; the key is shown right after creation either way.

The license page shows:

- **Machines**: each activated machine with its name, platform, app version, fingerprint, activation date and last check-in, out of the license's limit. **Free this machine** deactivates one so the customer can use the key on another computer.
- **History**: the license's audit events (webhooks, status changes, activations, emails), with a link to **All events**.
- **Details**: customer, product, plan and entitlements, seats, machine limit, versions covered, **Paid until**, **Valid until**, the language of its emails and the license ID.

Actions, depending on the status:

| Button | Effect |
|---|---|
| **Send key again** | Emails the key again to the customer, from your sender identity and in the license's language. |
| **Extend** | Moves **Paid until** forward by 30 days, 1 year or to a date. Offered for subscription and trial licenses that are not suspended, refunded or revoked. |
| **Edit** | Changes the **Seats**, a **Machine limit override** (empty: plan's machines per seat × seats) and the **Max major version** of this license, for example after an upgrade purchase. |
| **Suspend** / **Reinstate** | Suspension is reversible. Apps refuse the license at their next online check; certificates already on a machine keep working until they expire. **Reinstate** also lifts a refund. |
| **Revoke** | Final: the key stops working for good (`license_revoked`). You type `revoke` to confirm. |

Statuses are explained in [concepts](/docs/concepts/#license-statuses), and what happens on installed apps in [how changes reach installed apps](/docs/concepts/#how-changes-reach-installed-apps).

## Customers

Your end users, matched by email across purchases: name, email, number of licenses, **Customer since** and customer ID. Search by email or name; click an email to see that customer's licenses. Customers are created with their first license (from a webhook, the API or **New license**); there is nothing to create by hand here.

## Integrations

Connections to your payment providers: each one links a product to Paddle Billing or Lemon Squeezy, in one environment. **Connect provider** opens the form: choose **Paddle Billing** or **Lemon Squeezy**, the product (when you have several), the **Environment** (**Live**, or **Sandbox (test payments)**: use a separate integration for test mode), then **Connect**.

Each integration then shows:

| Setting | Meaning |
|---|---|
| **Webhook URL** | The address to paste into your provider (`https://api.keyrail.tech/v1/webhooks/{provider}/int_…`). **How to set it up in Paddle** (or Lemon Squeezy) lists the steps and events. |
| **Webhook secret** | The provider's signing secret: the Paddle notification destination's secret key, or the signing secret you chose in Lemon Squeezy. **Save secret** stores it; it is write-only and never shown again. Until it is set, the badge says **Secret missing** and every webhook is refused with `401`. |
| **Environment** | **Live** or **Sandbox (test payments)**, shown as a badge. |
| **Paddle API key** | Paddle only, optional: a key with the read-only `customer.read` permission lets Keyrail fetch a buyer's email right away instead of waiting for `customer.created`. Write-only; **Remove** deletes it. |
| **Price map** | One row per Paddle price ID (`pri_…`) or Lemon Squeezy variant ID, mapped to one of the product's plans. **Add price** adds a row, **Save price map** saves. A purchase of a price that is not in the map creates no license: it is logged in [Events](#events) as ignored. |

**Disconnect** deletes the integration: webhooks to its URL are refused and new purchases no longer create licenses; existing licenses keep working. Step-by-step guides: [Paddle Billing](/docs/paddle/), [Lemon Squeezy](/docs/lemon-squeezy/).

## API keys

Secret keys (`kr_sk_…`) for the [vendor API](/docs/vendor-api/), used from your backend or scripts. **Never ship one in an app**: apps use the client API, which needs no secret.

**New secret key** asks for a **Name** (for example "Backend (production)") and shows the key **once**: copy it into your secret manager, tick **I have stored this key somewhere safe**, then **Done**. The list shows each key's name, prefix, creation date and **Last used**. A key has full access to your account (there are no per-key scopes), so create one per system and revoke the ones you no longer use: **Revoke** makes every request with that key fail with `401` at once.

## Events

The audit log of your account, newest first: webhooks received (with their result: processed or ignored, and why), license creations and status changes, activations and deactivations, key emails, and settings changes (products, plans, signing keys, integrations, API keys, sending domain, plan changes). Opened from a license (**All events**), the list is filtered to that license; **Clear** removes the filter.

This is the first place to look when something did not happen: a payment that created no license shows up as an ignored webhook with its reason, for example `unknown price`. See [troubleshooting](/docs/troubleshooting/#no-license-after-a-payment). The same data is available with [`GET /v1/events`](/docs/vendor-api/#events).

## Account

The **Account & usage** screen holds your Keyrail plan and the settings of the emails and pages your customers see. Its sections:

- **Usage**: active licenses and products against your plan's limits. Limits are soft: your customers' licenses never stop working because of them; near or over the limit, a warning appears and the owner is emailed.
- **Plan and billing**: the Keyrail plans, monthly or yearly, with the buttons to subscribe, upgrade or switch, and **Manage billing** (payment method, invoices, cancellation) once you are subscribed. Payments are handled by Paddle. See [pricing](/pricing/).
- **Account**: name, account ID, the signed-in user and role, the link of your hosted customer portal to share with customers, and **Export data** (account owner): a JSON file with your products, plans, customers, licenses with their keys, machines, integrations without secrets and recent events.
- **Customer e-mails**: **Sender name**, **Sender address**, **Reply-to address**, **Default language**, and which **Customer portal linked from the e-mails**: **Hosted portal**, **My own portal** (with its address) or **No portal link**. See [custom sender and languages](/docs/custom-sender/).
- **Customer portal**: how the hosted portal looks for your customers (name shown, colour, logo, optional billing portal link) and, once your sending domain is verified, an address on your own domain. See [customer portal](/docs/customer-portal/).
- **Sending domain**: **Add domain**, the DNS records to publish, then **Verify now**. See [verify your domain](/docs/custom-sender/#verify-your-domain).
- **“Lost your key?” on your website**: lets your customers get their key again by email from a form on your own website, with the list of websites allowed to call it.
- **“My license” page on your website**: the address of a page of your website where customers sign in with an email link, see their licenses and free a computer; the code to paste there, a live preview, and **Link key e-mails to this page**. See [license page on your website](/docs/license-page/).
- **Danger zone** (account owner): **Close account…**. Closing revokes API keys and sessions at once and refuses new activations; activated machines keep validating for 30 days, during which the emailed link reopens the account. Export your data first.
