# Accounts and billing

> Sign in by email link, manage API keys, compare plans and quotas, check usage, upgrade through hosted checkout, and see exactly what Quire stores.

There's no dashboard and no password. You sign in with an email link, and you manage everything from the CLI, the SDKs or the API.

## Sign in

```bash
npx quirepdf-cli login you@example.com
```

1. The CLI shows a 6-digit code and waits. Quire emails you a sign-in link. The link works once and expires after 15 minutes.
2. Open the link and enter the code from your terminal, then select **Approve sign-in**.
3. The CLI collects your new API key and saves it to `~/.config/quire/config.json`. The key is never shown on a web page.

The code is never in the email, so a sign-in can only be approved by someone who can see the terminal that started it. Opening the link on its own changes nothing, so email security scanners that open links can't approve a sign-in. After five wrong codes the sign-in is cancelled.

The first sign-in creates your account on the free plan. No card is needed. If the CLI already has a working key, `login` says so and creates nothing; use `--force` to sign in again. Each new sign-in adds a key named after the device (`quirepdf-cli on your-laptop`), and your other keys keep working.

The sign-in request returns the same response whether or not an account exists for that email, so nobody can use it to find out who has an account.

### One account per mailbox

Addresses that deliver to the same mailbox share one account and one free quota. A `+tag` is ignored, and so are dots in Gmail addresses, so `you+test@gmail.com` and `y.ou@googlemail.com` sign in to the account for `you@gmail.com`. Disposable-inbox domains can't sign up (`400 disposable_email`).

Sign-in requests are limited to 10 an hour from one network and 5 an hour for one mailbox (`429 rate_limited`).

### Signing in from your own code

The CLI uses three public endpoints, which agents and scripts can use too:

```bash
curl https://api.quirepdf.dev/v1/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","device":"billing-worker"}'
```

```json
{
  "login_id": "lg_tvW6d9A9pv5Ma8YPkwlA",
  "code": "482913",
  "expires_in": 900,
  "message": "check your email for a sign-in link, then enter this code on the page it opens"
}
```

Show the `code` to the person signing in. `device` is optional and names the new key. Then poll `GET /v1/login/{login_id}` every couple of seconds:

- `202 {"status":"pending"}` until the sign-in is approved with the code;
- then `200 {"status":"ready","api_key":"qk_live_..."}`, exactly once;
- after that, or once the sign-in expires or is cancelled, `410 login_gone`.

## API keys

Keys look like `qk_live_` followed by 32 characters. Send one as `Authorization: Bearer <key>`.

```bash
npx quirepdf-cli keys                  # list
npx quirepdf-cli keys create ci        # create; the secret is printed once
npx quirepdf-cli keys revoke key_h0rRQSKOc8DB
```

The same through the API:

```bash
curl https://api.quirepdf.dev/v1/keys -H "Authorization: Bearer $QUIRE_API_KEY"
```

```json
{
  "keys": [
    { "id": "key_nb6N6M2qmGo7", "prefix": "qk_live_P7W5", "name": "quirepdf-cli on ravi-mbp", "created_at": 1791028361, "last_used_at": 1791028361 },
    { "id": "key_h0rRQSKOc8DB", "prefix": "qk_live_sS2W", "name": "ci", "created_at": 1791028361, "last_used_at": null }
  ],
  "current_key_id": "key_nb6N6M2qmGo7"
}
```

- **Create:** `POST /v1/keys` with an optional `{"name": "ci"}` (up to 60 characters) returns `201` with the key's details and `api_key`, the secret. It's shown only in this response.
- **List:** `GET /v1/keys` returns active keys with their prefix, name and timestamps (Unix seconds), never the secret. `current_key_id` is the key you called with.
- **Revoke:** `DELETE /v1/keys/{id}` returns `204`. The revoked key gets `401 unauthorized` from then on. You can't revoke your only active key (`409 last_key`); create a replacement first.

Use one key per environment or service, so you can revoke one without touching the others.

## Plans

| Plan | Renders per month | Price |
|---|---|---|
| Free | 20 | $0 |
| Starter | 2,000 | $19 a month |
| Pro | 10,000 | $49 a month |
| Scale | 50,000 | $129 a month |

Every plan includes every template, the generic layout, PNG previews, the SDKs, the CLI and the MCP server.

- **Test renders are free and unlimited.** Add `--test` (CLI), `test: true` (SDKs) or `?test=true` (API) for a watermarked render that never counts and works even after the quota runs out. Use them while you build.
- **Only successful renders count.** A request that fails with any error costs nothing.
- **Validation is free.** `POST /v1/validate` never counts.
- **One PDF or one PNG page is one render**, whatever its length, up to the 200-page limit.
- Quotas reset at the start of each calendar month, UTC.
- When the quota runs out, renders return `429 quota_exceeded` until the month ends or you upgrade.

## Usage

```bash
npx quirepdf-cli usage
```

```text
you@example.com · free plan · 2026-10
██████████████░░░░░░░░░░ 12 / 20 renders (8 left)
```

```bash
curl https://api.quirepdf.dev/v1/usage -H "Authorization: Bearer $QUIRE_API_KEY"
```

```json
{ "email": "you@example.com", "plan": "free", "period": "2026-10", "used": 24, "limit": 100, "remaining": 76 }
```

Every successful render also returns `x-quota-limit` and `x-quota-remaining` headers, so you can track usage without an extra call.

## Upgrade

```bash
npx quirepdf-cli upgrade pro
```

This opens a hosted checkout page for the plan. Through the API, `POST /v1/billing/checkout` with `{"plan": "starter"}`, `"pro"` or `"scale"` returns `{"url": "..."}` to send the user to.

Payments are handled by [Dodo Payments](https://dodopayments.com), our merchant of record. Dodo charges the right sales tax, VAT or GST for your country and issues your invoices. Quire never sees your card details.

Your plan changes as soon as payment completes, and the new limit applies immediately. Renders you've already used this month still count, so upgrading mid-month from Free to Starter with 100 used leaves you 1,900.

## Manage your subscription

```bash
npx quirepdf-cli billing
```

This opens the billing portal, where you can change plans, cancel, update your card and download invoices. The link works for 24 hours. Through the API, `GET /v1/billing/portal` returns `{"url": "..."}`.

- **Changing plans:** do it in the portal. Starting a second checkout while you have a paid plan returns `409 already_subscribed`, so you're never billed twice.
- **Cancelling:** you keep your paid plan until the end of the period you've paid for, then the account returns to the free plan.
- **Failed payments:** if a renewal payment fails, you keep your plan while the card is retried and you're emailed. The account returns to the free plan only if the subscription ends.

## What Quire stores

| Stored | Not stored |
|---|---|
| Your email address and plan | The data you send to render or validate |
| A SHA-256 hash of each API key, its 12-character prefix, name, and created and last-used times | The PDFs and PNGs Quire produces |
| The number of successful renders per month | Plaintext API keys |

Documents are rendered in memory and returned in the response. Quire doesn't write your data or the output to disk or keep a copy.

When you sign in, the new key is held so the CLI can collect it, and cleared as soon as it does. A key nobody collects is cleared after 30 minutes. Sign-in links are stored as hashes. Rate limits are counted in memory and keep no history.