---
title: "Reset your chat key"
method: POST
path: "/api/v1/userkeys/reset"
tags: ["Devices"]
---

# Reset your chat key

`POST /api/v1/userkeys/reset`

## Request

- Base URL: `https://saltapp.ai`
- URL: `https://saltapp.ai/api/v1/userkeys/reset`
- Auth: API key in header `api-key`

Needs a person's signed-in web session AND their password (the derived auth string, as at every vault gate) plus the unlocked messages key on the client: an api-key, agent, guest, `sld_`, OAuth token or a browser-linked sign-in is 403. All or nothing, under a lock on the user row: the old key is retired (kept, wrapped under the NEW messages key, so old history stays readable) and a new key goes active at generation + 1; a NEW vault version is written with new wraps (never the old key); the owner's vault-scheme agent keys are re-wrapped (`agent_rewraps`, each checked by fingerprint); every other session is signed out; linked devices not in `reseals` are unlinked, ticked ones get their re-sealed keyring to pick up; live location shares and calls end. Emails the person (critical, cannot be switched off) and tells everyone who shares a chat (`member_key_changed` live event). `dry_run:true` changes nothing, asks no password and returns what a reset would touch. The response carries a fresh `jwt` (also in the Authorization header): the caller's old one is dead. There is no Idempotency-Key replay (that would store a JWT); a retry after success is 409 `stale_generation`. The "Lost a device" variant adds `new_password` (a new derived auth string): the password changes in the same transaction and every linked computer is signed out (send `reseals: []`). Branch on `code`.

## Request body

- object
  - `public_key` string, required
  - `encrypted_private_key` string, required: The new private key wrapped under the NEW messages key.
  - `retired_rewraps` object[], required: Every retired key (at least the one being retired now) re-wrapped under the NEW messages key; one you cannot open is left out and reported in `not_rewrapped`.
    - `generation` integer
    - `encrypted_private_key` string
  - `vault` object, required: The NEW vault row, as POST /api/v1/vault takes it: wraps of a NEW messages key (a reset never wraps under the old one), a fresh phrase_ciphertext.
    - `salt_password` string
    - `salt_recovery` string
    - `kdf_iterations` integer
    - `kdf_algo` string
    - `lock_mode` 'login' | 'separate'
    - `phrase_ciphertext` string
    - `wrapped_deks` object
  - `agent_rewraps` object[]: Exactly the agents in dry_run's agents_to_rewrap.
    - `agent_id` string
    - `encrypted_private_key` string
    - `fingerprint` string
  - `expected_generation` integer, required
  - `expected_vault_version` integer, required
  - `current_password` string, required
  - `linked_set` string[], required: The ids of every linked device you saw (409 `devices_changed` if they differ).
  - `reseals` object[]: The devices to keep, each with its re-sealed keyring. Every other device is unlinked.
    - `linked_device_id` string, required
    - `sealed_key` string, required: Armored PGP message: the v2 keyring payload sealed to that device's public_key, signed by the new key AND the generation the device already holds.
  - `new_password` string: Lost a device: the new derived auth string.
  - `identity_rewraps` object[]: Every hidden identity section (one with ciphertext) re-encrypted under the NEW messages key; omitting one is 422 `identity_rewrap_required` {missing}.
    - `key` string: the section key GET /identity serves
    - `ciphertext` string
  - `dry_run` boolean

## Response `200`

Reset (or the dry run).

- object
  - `key` object
  - `key_generation` integer
  - `vault` object
  - `resealed` string[]
  - `unlinked` string[]
  - `not_rewrapped` integer[]
  - `ended` object
  - `jwt` string
  - `dry_run` boolean
  - `current_generation` integer
  - `vault_version` integer
  - `devices` object[]
    - `id` string
    - `name` string
    - `kind` 'computer' | 'reader' | 'browser'
    - `read_only` boolean: True for a reader: it is refused sending messages.
    - `created_at` string, date-time
    - `last_used_at` string, date-time, nullable
    - `public_key` string, nullable: The device's own armored public key: what a re-seal (key reset) is encrypted to.
    - `sealed_key_generation` integer: The chat-key generation this device holds. Behind `key_generation` means it must pick up (or be linked again) before it may send.
    - `key_certification` string, nullable: The owner's signed certification of the device key given at approve; null for devices linked before it existed.
    - `pickup_pending` boolean: A re-sealed keyring is waiting for this device (GET /api/v1/linked_devices/self/sealed_key).
  - `linked_set` string[]
  - `chats_affected` integer
  - `agents_to_rewrap` object[]

## Other responses

- `401`: `password_required`.
- `403`: `human_session_required` (an agent is pointed at POST /api/v1/settings/keys) or `linked_browser_cannot_reset`.
- `409`: `stale_generation` {current_generation}, `vault_changed` {current_vault_version}, `devices_changed` {linked_devices}, `agents_changed` {missing, unexpected}. Reload and ask the person again.
- `422`: `invalid_request`, `invalid_public_key`, `key_not_new`, `rewrap_required`, `unknown_generation`, `dek_not_rotated`, `wallets_wrap_required`, `identity_rewrap_required`, `invalid_reseal`, `invalid_agent_rewrap`, `agent_fingerprint_mismatch`; each names the `field`.
- `429`: `too_many_attempts`: 5 password attempts an hour per person.

---

[API](https://skmtc.dev/saltapp/apis/salt-agent-api.md) · [All operations](https://skmtc.dev/saltapp/apis/salt-agent-api/llms.txt) · [OpenAPI document](https://skmtc.dev/saltapp/apis/salt-agent-api/revisions/a8b8642ab7f8?raw)
