# Plans and API keys

Free, Pro or Enterprise — what each plan gets you, the 14-day Pro trial, how an API key signs the CLI in, what a running simulator sends, and what its errors mean.

## Plans

| | Free | Pro | Enterprise |
|---|---|---|---|
| [Simulators](https://www.rystic.ai/docs/concepts/glossary.md#simulator) | GitHub, Linear and Slack | every simulator | every simulator, plus [custom builds](mailto:founders@rystic.ai?subject=Custom%20build) |
| Credential | an API key | an API key | a license file |
| Usage | 1 seat, 10 hours a month | per seat, pooled credit, then per simulator-minute | unlimited |
| Sign-up | GitHub or Google, no card | 14 days free, card required | one week, then a contract |
| [Scenarios](https://www.rystic.ai/docs/concepts/glossary.md#scenario) | the full scenario pack, seeds | the full scenario pack, [seeds](https://www.rystic.ai/docs/concepts/glossary.md#seed), [tapes](https://www.rystic.ai/docs/concepts/glossary.md#tape), `--live` | the full scenario pack, seeds, tapes, `--live` |
| [Control plane](https://www.rystic.ai/docs/concepts/glossary.md#control-plane) | all of it, network faults and latency included | all of it: seed, scenario, clock, inspect, egress, the [state UI](https://www.rystic.ai/docs/concepts/glossary.md#state-ui), network faults and latency | all of it |
| Verified UIs | no: `rystic ui` is refused | yes: `rystic ui <simulator>` | yes: `rystic ui <simulator>` |
| Docker | `registry.rystic.ai`, the key is the credential | `registry.rystic.ai`, the key is the credential | `registry.rystic.ai`, the license is the credential |
| Network | the simulator checks in with Rystic while it runs — [what it sends](#what-a-running-simulator-sends) | the same | none; runs offline |

A new account starts on Free: sign up with GitHub or Google, no card. Free runs GitHub, Linear and Slack for 10 hours a month, with every control route, network faults and latency included; Slack's verified UI is Pro and Enterprise.

Pro is opt-in: start the trial from [pricing](https://www.rystic.ai/pricing) or the [Account](https://www.rystic.ai/account) page, through Stripe Checkout. The trial converts to paid Pro on day 14 unless you cancel it first under **Manage billing** on the Account page; canceling within the trial means no charge. An organization whose trial or subscription ends falls back to Free.

A verified UI is a client app built against the service's real UI and checked against it. `rystic ui <simulator>` runs it against a running simulator: as the image `registry.rystic.ai/<simulator>-ui:<ver>`, pulled with your `docker login`, or without Docker from the UI's archive on your local Node.js. The registry serves it only to a plan or license that includes the `verified_ui` [feature](https://www.rystic.ai/docs/concepts/glossary.md#feature), and the UI serves only a simulator granted it. It never ships inside a simulator's archive or image. Every simulator's own state UI, `/_rystic/ui`, is on every plan.

[Pricing](https://www.rystic.ai/pricing) has the prices. [Features by plan](#features-by-plan) lists what each plan includes, and the [control plane reference](https://www.rystic.ai/docs/control-plane-reference.md) lists every route with the feature it needs.

## Get an API key

1. Sign up at [rystic.ai](https://www.rystic.ai/sign-up) with GitHub or Google. A new account starts on Free; start the Pro trial from [pricing](https://www.rystic.ai/pricing) for every simulator.
2. Open [Account → API keys](https://www.rystic.ai/account/api-keys) and create one. **Personal** keys belong to you; **Team (for CI)** keys belong to the organization and suit CI and shared machines.
3. Copy it when it is shown. It starts `rys_` and is shown once; the site keeps only a hash.

Revoke a key on the same page. A simulator running on it stops within about a minute.

## Sign the CLI in

[Installation](https://www.rystic.ai/docs/installation.md#install) signs the CLI in, through the browser or with `rystic login rys_…`. [The CLI reference](https://www.rystic.ai/docs/cli-reference.md#simulators-on-an-api-key) lists where else the CLI looks for a key, such as `RYSTIC_TOKEN`.

### When the CLI refuses the key

`pull`, `update` and `run` of a simulator not installed yet ask the release endpoint for a build with the key. A refusal exits 11 and names where the key came from. So does `run` of an installed metered build that finds no key, or a malformed one (the last row):

| you saw | it means | do this |
|---|---|---|
| `release endpoint refused (…): invalid_token`, then `the API key from <where> is not one Rystic knows — make a new key at https://www.rystic.ai/account/api-keys, then …` | the key is mistyped, or not a Rystic key | create a key and `rystic login` it, or replace it where the message says (`$RYSTIC_TOKEN`, `config.json`) |
| `… refused (…): revoked`, `… was revoked — …` | the key was revoked | create a new key |
| `… refused (…): key_expired`, `This API key has expired. …` | a key pulled through [Vercel Connect](https://www.rystic.ai/docs/vercel.md) is past its 24 hours | pull another with `create_api_key`, or create a key |
| `… refused (…): org_deleted`, `… belongs to an organization that was deleted — …` | its organization is gone | create a key in another organization |
| `… refused (…): no_plan`, then a sentence saying why the organization has no plan and linking to pricing; `docker pull` returns `DENIED` with the same sentence | the organization has no plan | start the trial, or subscribe, at [pricing](https://www.rystic.ai/pricing) |
| `… refused (…): product_not_in_plan`, then `This organization's plan does not include …` | the organization's plan leaves that simulator out: Free runs only GitHub, Linear and Slack | [upgrade](https://www.rystic.ai/pricing) |
| `… refused (…): feature_not_in_plan`, then `Verified UIs are Pro and Enterprise; this organization's plan does not include them …`; `docker pull …-ui` returns `DENIED` with the same sentence | `rystic ui` or a `<simulator>-ui` pull on a plan without the `verified_ui` feature | [pricing](https://www.rystic.ai/pricing) |
| `… is the metered edition and runs on an API key`, or `the token from … is not a Rystic token` | `run` of an installed metered build found no key, or a malformed one | `rystic login rys_…`, or set `RYSTIC_TOKEN` |

Asking for a build with no license and no key exits 3 instead: `pull`, `update` and `run` of a simulator not installed yet print `no license file found — …`, ending with ``or sign in for the metered edition: run `rystic login` to sign in or sign up in the browser (with a key: `rystic login rys_…` or `--token-stdin`)``. A key not shaped `rys_…` exits 11, naming where it came from: `… is not a Rystic key (rys_ and 43 more characters): replace it`.

## What a running simulator sends

This is the section a security review reads.

On Free and Pro, the simulator itself checks in with `https://lease.rystic.ai` over HTTPS while it runs. Every call carries your API key.

| call | when | what it carries |
|---|---|---|
| register | at start, before it serves | product, version, a random id for this process, the start time |
| heartbeat | every 30 seconds | which 30-second windows since the last heartbeat it was in use |
| close | on exit | the same, plus why it stopped |

"In use" means the simulator served an API request, held a subscribed WebSocket, rendered a webhook or played a tape in that window. Control-plane calls alone do not count. So Rystic learns *that* a simulator was busy, never what it was doing.

**Never sent:** API request or response bodies, paths, headers or counts; hostnames or usernames; the simulator's data or state; scenario, seed, tape or network-profile contents; your OS and architecture; any device identifier. Rystic's server sees the connection's source address, as any HTTPS server does, and does not store it.

**Kept on your machine:** the key, and the last lease Rystic signed, which allows [one offline start](#offline-and-outages).

The CLI itself talks to Rystic when it needs a release, an image or your plan: [What leaves your machine](https://www.rystic.ai/docs/cli.md#what-leaves-your-machine) lists every call it makes and what each sends.

The Kalshi simulator fetches a replayed or live-fed market's public record from Kalshi, not from us: [The one outbound call](https://www.rystic.ai/docs/simulators/kalshi/features/tape-format.md#where-a-replayed-markets-metadata-comes-from).

## Offline and outages

1. **At start** the simulator must reach Rystic. If it can't, it keeps retrying for up to 10 minutes before giving up. `RYSTIC_REGISTER_WAIT` (a duration like `2m`, or `0` for one attempt) changes that.
2. **The simulator a machine last ran on an API key** can start once more without reaching us, within 24 hours of its last check-in, and serves for 10 minutes while it keeps retrying. A CI runner that starts fresh each time has no such allowance; the retry window is what carries it through a short outage.
3. **While running**, a lost connection leaves the simulator running for up to 2 hours.
4. **A fully offline machine** needs Enterprise.

## What stops a simulator

A simulator on Free or Pro prints why it stopped, then exits non-zero.

| you saw | it means | do this |
|---|---|---|
| a message naming the 10 hours the Free plan includes | Free's 10 hours are used up this month | wait for the next month, or [upgrade](https://www.rystic.ai/pricing) |
| `The … plan does not include dropbox-twin.` (or any simulator but GitHub, Linear and Slack) | Free runs only GitHub, Linear and Slack; a Pro organization whose trial or subscription ended runs on Free | [upgrade](https://www.rystic.ai/pricing), or pay the open invoice at [Account](https://www.rystic.ai/account) |
| `Free: 10h a month, Linear, GitHub, Slack. Upgrade at https://www.rystic.ai/pricing.` | not a stop: a Free organization sees this at start | nothing, or [upgrade](https://www.rystic.ai/pricing) |
| `Billing for this organization has lapsed, so its simulators run on a reduced plan. …` | not a stop: a Pro organization whose trial or subscription ended unpaid runs on Free | subscribe at [pricing](https://www.rystic.ai/pricing), or pay the open invoice at [Account](https://www.rystic.ai/account) |
| a message naming the spend cap | your organization's spend cap is reached | raise it at [Account → Spend limit](https://www.rystic.ai/account/spend-limit) |
| `revoked` or an invalid key | the key was revoked or mistyped | create a new key and `rystic login` it |
| `This API key has expired. …` | a key pulled through [Vercel Connect](https://www.rystic.ai/docs/vercel.md) reached its 24 hours, and the simulator stopped at its next check-in | pull another with `create_api_key` and start the simulator again |
| an error naming the clock | the machine's clock is more than 2 hours fast | fix the clock |

## Enterprise: license files

Enterprise runs on a signed license file instead of an API key, and the simulator sends nothing. [Talk to us](mailto:founders@rystic.ai?subject=Enterprise) for one. It reaches you one of two ways:

1. An **install link**: `https://www.rystic.ai/api/install?l=…`. The long `l=` value *is* the license; one pasted line installs the CLI and activates it.
2. A **license file**, named like `rystic-license-acme-2026.json`.

### Activation

```bash
rystic login ./rystic-license-acme-2026.json
```

```
license:   /Users/you/.rystic/rystic-license.json
licensee:  Acme
products:  [stripe-twin, kalshi-twin]
issued:    2026-08-19
expires:   2027-08-19 (valid)
signature: verified
```

1. `login` verifies the license and, if Docker is running, logs Docker in to `registry.rystic.ai` with it. `--no-docker` skips that half.
2. `rystic license activate <file>` is the same command minus the Docker half.
3. `rystic license` prints the active license and its claims. A license scoped to some features lists them under `features:`; one without that line grants every feature, verified UIs included.

The CLI parks the license in `$RYSTIC_HOME` and verifies it on your machine against a public key built into the CLI. Nothing phones home to check it. To point one command or container at a different license, set `RYSTIC_LICENSE` to a file path, or to the license itself, JSON or base64.

A license wins over an API key. Once a license is parked, `pull`, `update`, `status`, `list` and `doctor` use it rather than any key, so the machine installs Enterprise builds; `status` and `doctor` still report a stored key's standing. Starting a metered build with `--license` or `$RYSTIC_LICENSE` also set is refused (exit 2, `… a license was given too … — drop one`); drop the flag or unset the variable.

Rystic's release endpoint records each download (licensee, product, version, OS/arch, command, time) and nothing else. A license signed with `"metrics": "off"` records nothing; `rystic license` prints your claims.

### Expiry and renewal

Rystic renews your license before it expires, through the end of your contract. Every command that talks to the release endpoint (`rystic pull`, `rystic update`, the `rystic status` update check, and `rystic run` on a simulator not yet installed) fetches the renewal and installs it over the active license, printing `license renewed through <date>`. When Docker is logged in to the registry, the CLI logs it in again with the renewal.

A license you pass with `--license` or `$RYSTIC_LICENSE`, as CI usually does, is never rewritten: the CLI saves the renewal in `$RYSTIC_HOME` and prints its path, and you copy that file into your secret. A simulator you start without the CLI, such as a container given `RYSTIC_LICENSE`, keeps the license it was given until you update it.

`rystic license` and `rystic doctor` show the expiry date. For 7 days past it, simulators still start with a warning and `rystic pull` still fetches your renewal. After that, commands fail with `license_expired`: ask Rystic for a new license and `rystic login` it. `rystic login --force` activates a license even when it is expired.

### License errors

| you saw | it means | do this |
|---|---|---|
| `curl: … error: 403` from the install link | the link's `l=` value was cut short | paste the full link again, or use the license file |
| `no license file found …` (exit 3) | no license activated on this machine | `rystic license activate ./rystic-license-acme-2026.json` |
| `signature does not verify` | the file was altered or re-encoded in transit | re-download it exactly as sent; ask for a fresh one |
| `license_expired` | more than 7 days past its date | ask Rystic for a new license |
| `asset_not_found … carries no build for this license` | that version was never cut for you | ask Rystic, or `rystic pull -version <x.y.z>` one you have |

`rystic doctor` names the broken piece without revealing your license; [Exit codes](https://www.rystic.ai/docs/cli-reference.md#exit-codes) lists every code.

## Features by plan

A simulator's [control plane](https://www.rystic.ai/docs/control-plane.md) is split into [features](https://www.rystic.ai/docs/concepts/glossary.md#feature). Each feature unlocks a group of routes and boot inputs. Pro and Enterprise include every one; Free includes every one but `verified_ui`:

| Feature | Unlocks | Free | Pro | Enterprise |
|---|---|---|---|---|
| none | identity, the route list, reset and the simulator's docs | yes | yes | yes |
| `state_view` | reading state, the egress log, the call log and the event stream | yes | yes | yes |
| `state_set` | seeding, the clock, the RNG, credential bindings and timelines | yes | yes | yes |
| `scenarios` | listing and loading named scenarios | yes | yes | yes |
| `faults` | network latency and rejections, dropped sockets, revoked credentials, duplicate webhook deliveries and capped page sizes | yes | yes | yes |
| `ui` | the state UI and the API explorers | yes | yes | yes |
| `replay` | Kalshi's tapes and live feed | yes | yes | yes |
| `verified_ui` | the verified UI image, through `rystic ui` | no | yes | yes |

[Control plane → Features](https://www.rystic.ai/docs/control-plane.md#features) maps each feature to its routes.

A simulator on Free or Pro learns its plan from the [lease](https://www.rystic.ai/docs/concepts/glossary.md#lease) Rystic signs when it starts. The simulator renews the lease as it checks in every 30 seconds. A renewal that changes the plan applies from the next request, with no restart. `GET /_rystic/version` shows the grant under `lease.entitlements.features`: `["*"]`, every feature, on Pro, and every feature but `verified_ui` on Free.

An Enterprise build runs on a license file, holds no lease and serves every route.

---

Source: https://www.rystic.ai/docs/licensing · Markdown: https://www.rystic.ai/docs/licensing.md
