Skip to content

Recovery

Same auth as the rest of the wallet lane (publishable key + end-user JWT). Conceptually:

  1. Initiate — Sigil emails the user a 6-digit OTP.
  2. Verify — Sigil retrieves both shares (provider from KMS, recovery from your webhook or local KMS), returns them to the iframe.
  3. Complete — the iframe re-shares with a fresh polynomial and PUTs the new shares back.

POST /v1/wallets/me/recovery/initiate

No body. Returns the recovery process id — keep it for the next call.

{ "data": { "recovery_process_id": "rec_xxx" }, "error": null }

If the JWT has no email claim (rare but possible in OIDC flows), returns 424 no_email.

POST /v1/wallets/me/recovery/{recovery_process_id}/verify

{ "code": "238041" }

Returns:

{
"data": {
"wallet_id": "",
"provider_share": "base64",
"recovery_share": "base64",
"polynomial_version": 1
},
"error": null
}

The iframe reconstructs the private key from these two shares (threshold = 2). The new device share, new provider share, and new recovery share are then derived from a fresh polynomial.

POST /v1/wallets/me/recovery/{recovery_process_id}/complete

{
"new_provider_share": "base64",
"new_provider_share_index": 2,
"new_recovery_share": "base64",
"new_recovery_share_index": 3
}

Sigil persists the new provider share under a fresh DEK + KEK wrap, dispatches the new recovery share to your webhook (or stores it locally if you’re in Sigil-managed mode), marks the old shares rotated, and finalises the recovery process. Returns 204.

Errors

error.codeStatusMeaning
expired410The recovery process is older than 15 minutes.
process_state409You’re calling complete before verify, or vice versa.
wallet_not_found404The user has no wallet to recover.