# Call the REST API with an API key

The REST API lives at `https://api.lessly.com`. It is the HTTP face of the same operations available in the Product App, the CLI and over MCP. The per-area route catalog is on the [REST API reference](/reference/openapi).

## Create a key

Keys are per-product, and made for scripts, CI and agents.

**App.** The product's **Developer** page.

**REST.**

```bash
curl -X POST https://api.lessly.com/auth/api/v1/api-keys \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"productId": "'"$PRODUCT_ID"'", "name": "ci-deploy"}'
```

The response contains the key's `id`, `productId`, `name`, `createdAt`, `lastUsedAt`, and `plaintext` — the raw `lsk_dev_…` value.

> **`plaintext` is returned exactly once.** Only a SHA-256 hash of the key is stored, so a key that was not copied at creation time cannot be recovered. Issue a new one instead.

A key cannot mint another key: a request authenticated with an API key is rejected with `403`. Creating a key requires a signed-in user.

## Use the key on a request

```bash
curl https://api.lessly.com/governance/api/v1/products \
  -H "Authorization: Bearer lsk_dev_..."
```

That endpoint — `GET /governance/api/v1/products`, the products the caller has access to — is on the [Organization API](/reference/openapi/organization) page, alongside the rest of the area.

Two credential kinds are accepted, and the platform tells them apart by their shape:

- **A session token**, obtained by signing in. Short-lived.
- **An API key**, which starts with `lsk_dev_`. Long-lived.

Requests from the Product App itself run on the browser session instead of a bearer header, and mutating calls additionally send an `X-CSRF-Token` header.

### Shape of a URL

A request path starts with the platform area it addresses, followed by a versioned route inside that area:

```text
https://api.lessly.com/<area>/api/v1/<resource>
```

The version prefix is `v1`. Breaking changes to a route arrive under a new version prefix; the previous one keeps its behaviour. Two areas appear throughout this page: `governance` for products, organizations, members and invitations, and `auth` for sign-in and API keys.

### Choosing the product

Many routes act on one product. Which one is decided by the `X-Product-Id` request header:

```text
X-Product-Id: <product-uuid>
```

An API key is minted for exactly one product, and that is a ceiling it cannot cross. A request presenting an API key together with an `X-Product-Id` naming a different product is refused with `403` and the reason `api_key_product_scope_denied`. The request is not silently rewritten to the key's own product: writing to A while the caller believes it wrote to B is worse than failing.

## Trade a key for a short-lived token

```bash
curl -X POST https://api.lessly.com/auth/api/v1/api-keys/exchange \
  -H "Authorization: Bearer lsk_dev_..."
```

Called *with* an API key, this returns a token valid for one hour, carrying the key's product. Use it where a long-lived credential should not be held by the code doing the work. Calling it with anything other than an API key answers `400`.

## List and revoke keys

```bash
curl "https://api.lessly.com/auth/api/v1/api-keys?productId=$PRODUCT_ID" \
  -H "Authorization: Bearer $SESSION_TOKEN"
```

Returns the product's active keys with `id`, `name`, `createdAt` and `lastUsedAt`. The secret value is never part of this response.

```bash
curl -X DELETE https://api.lessly.com/auth/api/v1/api-keys/$KEY_ID \
  -H "Authorization: Bearer $SESSION_TOKEN"
```

Answers `204`, and the key stops working. A key that does not exist, or that belongs to somebody else, answers `404` — the same response either way, so the call cannot be used to probe for other people's keys.

**There is no rotate call.** Rotation is two steps: create the replacement key, put it into service, then revoke the old one. Both keys are valid in between, so nothing has to go down during the swap.

## Next steps

- [Give the key to an agent](/interfaces/mcp): paste it into the MCP client as a bearer credential.
- [Sign in from a terminal](/interfaces/cli): the CLI takes a product-scoped token with `lessly auth login --token`.
- [Browse the route catalog](/reference/openapi): every area, with its endpoints and the MCP tool each is bound to.
