Recovery
Same auth as the rest of the wallet lane (publishable key + end-user JWT). Conceptually:
- Initiate — Sigil emails the user a 6-digit OTP.
- Verify — Sigil retrieves both shares (provider from KMS, recovery from your webhook or local KMS), returns them to the iframe.
- 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.code | Status | Meaning |
|---|---|---|
expired | 410 | The recovery process is older than 15 minutes. |
process_state | 409 | You’re calling complete before verify, or vice versa. |
wallet_not_found | 404 | The user has no wallet to recover. |