# Enable Email / SMS Recovery

Integrate [Candide Guardian](https://docs.candide.dev/wallet/recovery/auth-api.md) as a trusted recovery service for user Safe accounts. Candide Guardian provides secure account recovery through email and SMS verification.

> To set up the recovery module, see [Enable Recovery Module and Add Guardians](https://docs.candide.dev/wallet/plugins/how-to-add-a-guardian.md).

## What is Candide Guardian?[​](#what-is-candide-guardian "Direct link to What is Candide Guardian?")

Candide Guardian is a managed recovery service acting as a trusted guardian for user Smart Accounts. Unlike personal guardians, Candide Guardian provides:

* **Familiar Security**: Email, SMS, or both for recovery verification
* **No Key Management**: Users don't manage guardian private keys
* **Automated Signing**: Service automatically provides guardian signatures after verification
* **Grace Period Protection**: Built-in security delay before recovery execution
* **Gas Sponsorship**: Sponsored recovery transactions remove gas barriers

## Quickstart[​](#quickstart "Direct link to Quickstart")

> You can also [fork the complete code](https://github.com/candidelabs/safe-recovery-service-sdk/blob/main/examples/01-enable-email-sms-recovery/index.ts) and follow along.

### Installation[​](#installation "Direct link to Installation")

* npm
* yarn

```
npm i abstractionkit safe-recovery-service-sdk viem
```

```
yarn add abstractionkit safe-recovery-service-sdk viem
```

### Configure Environment[​](#configure-environment "Direct link to Configure Environment")

* .env

```
# Network Configuration
CHAIN_ID=11155111
BUNDLER_URL=https://api.candide.dev/public/v3/11155111
NODE_URL=https://ethereum-sepolia-rpc.publicnode.com

# Recovery Service URL
# Get access here: https://app.formbricks.com/s/brdzlw0t897cz3mxl3ausfb5
RECOVERY_SERVICE_URL= 
```

## Step 1: Initialize Service[​](#step-1-initialize-service "Direct link to Step 1: Initialize Service")

```
import { RecoveryByCustodialGuardian } from "safe-recovery-service-sdk";

const chainId = BigInt(process.env.CHAIN_ID as string);
const serviceUrl = process.env.RECOVERY_SERVICE_URL as string;

const custodialGuardianService = new RecoveryByCustodialGuardian(serviceUrl, chainId);
```

## Step 2: Create Registration Statement[​](#step-2-create-registration-statement "Direct link to Step 2: Create Registration Statement")

Generate a SIWE (Sign-In with Ethereum) statement for guardian registration. Make sure the Safe account is already deployed.

* Email Registration
* SMS Registration

```
const siweStatementToSign = await custodialGuardianService.createRegistrationToEmailRecoverySiweStatementToSign(
  smartAccount.accountAddress,
  "user@example.com"
);
```

```
const siweStatementToSign = await custodialGuardianService.createRegistrationToSmsRecoverySiweStatementToSign(
  smartAccount.accountAddress,
  "+1234567890"
);
```

## Step 3: Sign and Submit Registration[​](#step-3-sign-and-submit-registration "Direct link to Step 3: Sign and Submit Registration")

Sign the SIWE statement with the Smart Account owner key and submit registration:

* Email Registration
* SMS Registration

```
import { 
  SafeAccountV0_3_0, 
  getSafeMessageEip712Data, 
  SAFE_MESSAGE_PRIMARY_TYPE 
} from "abstractionkit";
import { TypedDataDomain } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

const ownerAccount = privateKeyToAccount(ownerPrivateKey);

const emailSafeTypedData = getSafeMessageEip712Data(
    smartAccount.accountAddress,
    chainId,
    emailRegistrationSiweMessage
);

const emailOwnerSignature = await ownerAccount.signTypedData({
    domain: emailSafeTypedData.domain as TypedDataDomain,
    types: emailSafeTypedData.types,
    primaryType: SAFE_MESSAGE_PRIMARY_TYPE,
    message: emailSafeTypedData.messageValue
});

const emailRegistrationSignature = SafeAccountV0_3_0.buildSignaturesFromSingerSignaturePairs([
    { signer: ownerAccount.address, signature: emailOwnerSignature }
]);

const emailRegistrationChallengeId = await custodialGuardianService.createRegistrationToEmailRecovery(
    smartAccount.accountAddress,
    userEmail,
    emailRegistrationSiweMessage,
    emailRegistrationSignature
);
```

```
import { 
  SafeAccountV0_3_0, 
  getSafeMessageEip712Data, 
  SAFE_MESSAGE_PRIMARY_TYPE 
} from "abstractionkit";
import { TypedDataDomain } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

const ownerAccount = privateKeyToAccount(ownerPrivateKey);

const smsSafeTypedData = getSafeMessageEip712Data(
    smartAccount.accountAddress,
    chainId,
    smsRegistrationSiweMessage
);

const smsOwnerSignature = await ownerAccount.signTypedData({
    domain: smsSafeTypedData.domain as TypedDataDomain,
    types: smsSafeTypedData.types,
    primaryType: SAFE_MESSAGE_PRIMARY_TYPE,
    message: smsSafeTypedData.messageValue
});

const smsRegistrationSignature = SafeAccountV0_3_0.buildSignaturesFromSingerSignaturePairs([
    { signer: ownerAccount.address, signature: smsOwnerSignature }
]);

// Create SMS registration
const smsRegistrationChallengeId = await custodialGuardianService.createRegistrationToSmsRecovery(
    smartAccount.accountAddress,
    userPhone,
    smsRegistrationSiweMessage,
    smsRegistrationSignature
);
```

## Step 4: Verify OTP Code[​](#step-4-verify-otp-code "Direct link to Step 4: Verify OTP Code")

Submit the OTP code received via email or SMS:

* Email Verification
* SMS Verification

```
// User enters OTP from email
const otpCode = "123456";

const verificationResponse = await custodialGuardianService.submitRegistrationChallenge(
  emailRegistrationChallengeId, // returned by createRegistrationToEmailRecovery()
  otpCode
);

const candideGuardianAddress = verificationResponse.guardianAddress;
```

```
// User enters OTP from SMS
const otpCode = "123456";

const verificationResponse = await custodialGuardianService.submitRegistrationChallenge(
  smsRegistrationChallengeId, // returned by createRegistrationToSmsRecovery()
  otpCode
);

const candideGuardianAddress = verificationResponse.guardianAddress;
```

## Step 5: Enable Recovery Module and Add Guardian[​](#step-5-enable-recovery-module-and-add-guardian "Direct link to Step 5: Enable Recovery Module and Add Guardian")

Add Candide Guardian to your Safe account. See the complete flow of adding a guardian in the [How to Add a Guardian](https://docs.candide.dev/wallet/plugins/how-to-add-a-guardian.md) guide

```
import { SocialRecoveryModule, SocialRecoveryModuleGracePeriodSelector } from "abstractionkit";

const srm = new SocialRecoveryModule(SocialRecoveryModuleGracePeriodSelector.After3Minutes);

// Create transactions to enable module
const enableModuleTx = srm.createEnableModuleMetaTransaction(
  smartAccount.accountAddress
);

// Add Candide Guardian
const addGuardianTx = srm.createAddGuardianWithThresholdMetaTransaction(
  candideGuardianAddress,
  1n // threshold
);

// Create UserOp with both transactions
let userOperation = await smartAccount.createUserOperation(
  [enableModuleTx, addGuardianTx],
  process.env.NODE_URL,
  process.env.BUNDLER_URL
);

// sponsor gas, sign, and submit userop
```

Recovery Strategy Recommendation

For maximum security, recommend Candide Guardian alongside personal guardians in a multi-guardian setup (e.g., 2 of 3: Candide Guardian + 2 personal guardians).

That's it! Your Safe account is now protected by Candide Guardian. In case of key loss, you can use it as one of the Guardians setup using your registered email/phone verification to recover your account.

## Complete Working Example[​](#complete-working-example "Direct link to Complete Working Example")

Full Working Example

examples/01-enable-email-sms-recovery/index.ts

```
loading...
```

[See full example on GitHub](https://github.com/candidelabs/safe-recovery-service-sdk/blob/main/examples/01-enable-email-sms-recovery/index.ts)

## What's Next[​](#whats-next "Direct link to What's Next")

* [Recovery Flow](https://docs.candide.dev/wallet/plugins/recover-account-candide-guardian.md): walk through the full recovery flow using your registered email/SMS to regain access to a lost account
