> ## Documentation Index
> Fetch the complete documentation index at: https://docs.square1.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# API keys

> List, create, delete, reveal, and rotate your keys

An account holds up to **5** active API keys at once. All of them share the
same account quota — extra keys are for separating apps, not for buying more
capacity. See [Authentication](/authentication#keys) for what a key is.

Everything on this page is on `https://dash.square1.dev` and accepts either
the `ws-session` cookie or a Bearer API key — see
[Account API overview](/account/overview#authentication).

## List keys — `GET /api/user/me/keys`

```bash theme={null}
curl https://dash.square1.dev/api/user/me/keys \
  -H "Authorization: Bearer $SQUARE1_API_KEY"
```

```json theme={null}
{
  "success": true,
  "data": {
    "keys": [
      {
        "id": 41,
        "label": "risu",
        "key_prefix": "sq-arca-9f3c1a0b",
        "created_at": 1756400000000,
        "last_used_at": 1756486400000,
        "revealable": true,
        "legacy": false
      }
    ],
    "max": 5
  }
}
```

Active (non-revoked) keys only, oldest first. **No plaintext and no hash is
ever in this response.**

| Field                        | Notes                                                                                                                             |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `id`                         | Integer. The handle for delete and reveal                                                                                         |
| `label`                      | What you named it, or `새 키` if you didn't. Truncated to 40 characters at creation                                                 |
| `key_prefix`                 | The first **16** characters of the key — enough to recognize it, not enough to use it. May be `null` on very old rows             |
| `created_at`, `last_used_at` | **Epoch milliseconds**, not ISO strings. `last_used_at` is `null` until first use, then debounced to at most one write per minute |
| `revealable`                 | Whether the plaintext is still recoverable — see below                                                                            |
| `legacy`                     | `true` for `ws-` keys (and for rows with no stored prefix)                                                                        |

<Note>
  `revealable` is a **per-row** property, not a global feature flag. Three
  kinds of row coexist: legacy `ws-` keys (never revealable), `sq-arca-`
  keys issued before key encryption was configured (not revealable), and
  `sq-arca-` keys issued after (revealable). Read the flag per key.
</Note>

`max` is the account cap. Read it rather than hardcoding 5 — it is the same
value that produces `key_cap_reached`.

## Create a key — `POST /api/user/me/keys`

```bash theme={null}
curl -X POST https://dash.square1.dev/api/user/me/keys \
  -H "Authorization: Bearer $SQUARE1_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label": "my-bot"}'
```

```json theme={null}
{ "success": true, "data": { "apiKey": "sq-arca-..." } }
```

* `label` is optional and truncated to 40 characters. Omit it and the key is
  named `새 키`.
* **`apiKey` is the only time the plaintext appears in a create response.**
  Store it now.
* Requires an `active` account.

| HTTP | `code`             | Meaning                                                  |
| ---- | ------------------ | -------------------------------------------------------- |
| 401  | —                  | No valid cookie or key                                   |
| 403  | `account_unlinked` | Account isn't `active`; re-verify in the dashboard first |
| 400  | `key_cap_reached`  | You already hold `max` active keys — revoke one first    |

## Delete a key — `DELETE /api/user/me/keys/:id`

```bash theme={null}
curl -X DELETE https://dash.square1.dev/api/user/me/keys/41 \
  -H "Authorization: Bearer $SQUARE1_API_KEY"
```

```json theme={null}
{ "success": true }
```

`:id` must be a positive integer; anything else is `400`. A key that isn't
yours, or is already revoked, is `404` — the two are not distinguished.

<Warning>
  Revocation is immediate. Any client still presenting that key starts
  getting `401 invalid_api_key` on `/v1` right away. Nothing stops you from
  deleting the key you are currently authenticating with.
</Warning>

## Reveal a key — `GET /api/user/me/keys/:id/reveal`

Returns the **full plaintext** of a key you already created, for keys where
`revealable` is `true`.

```bash theme={null}
curl https://dash.square1.dev/api/user/me/keys/41/reveal \
  -H "Authorization: Bearer $SQUARE1_API_KEY"
```

```json theme={null}
{ "success": true, "data": { "key": "sq-arca-..." } }
```

* **Rate limit: 30 requests/minute.** See
  [Rate limits](/account/overview#rate-limits).
* Every successful reveal is written to the account's audit trail. The audit
  record holds the key **id**, never the plaintext.
* This is deliberately a separate request rather than a field on the list —
  the list is fetched on every page load and would otherwise leave plaintext
  in browser, proxy, and devtools history.

| HTTP | `code`               | Meaning                                                                                                                         |
| ---- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| 400  | —                    | `:id` is not a positive integer                                                                                                 |
| 409  | `key_not_revealable` | The plaintext is not stored for this key. Not an error condition — legacy and pre-encryption keys are permanently unrecoverable |

<Note>
  `409` also covers the case where the stored ciphertext exists but can no
  longer be decrypted. Either way the honest answer is "we don't have the
  original", which is why it is a `409` and not a `500`.
</Note>

## Rotate everything — `POST /api/user/me/rotate-key`

The panic button. Revokes **every** key on the account and issues one fresh
key in a single step.

```bash theme={null}
curl -X POST https://dash.square1.dev/api/user/me/rotate-key \
  -H "Authorization: Bearer $SQUARE1_API_KEY"
```

```json theme={null}
{ "success": true, "data": { "apiKey": "sq-arca-..." } }
```

<Warning>
  This is not "add a key". Every existing key — including the one you may
  have used to make this call — stops working immediately. Use
  `POST /api/user/me/keys` for ordinary key management.
</Warning>

* Requires an `active` account (`403 account_unlinked` otherwise).

* **60-second cooldown** between rotations. Trip it and you get a `429` with
  **no `code`** and no `Retry-After`:

  ```json theme={null}
  { "error": { "message": "잠시 후 다시 시도해 주세요", "type": "rate_limit_exceeded" } }
  ```

* The replacement key is labeled `기본`.

* The rotation is audited.

Rotating does **not** touch your browser sessions. To end those, see
[Sessions](/account/sessions).
