Skip to main content

Recovery Alert Subscription Guide

Set up email and SMS notifications for recovery requests to keep Safe account owners informed of recovery attempts. This security feature provides an essential early warning system during the recovery process.

info

The Recovery Service requires access credentials. Request access here.

To set up the recovery module first, see the Enable Recovery Module and Add Guardians guide.

Overview

The Recovery Alert system provides:

  1. Instant Notifications: Immediate email and SMS alerts when recovery requests are initiated
  2. On-Chain Monitoring: Tracks on-chain recoveries across all supported networks
  3. Grace Period Warnings: Alerts during the grace period enabling owners to respond to unauthorized attempts
  4. Signature Verification: Only verified account owners can subscribe to alerts

To learn more, visit the Safe Recovery Service API section.

Prerequisites

Installation

npm i safe-recovery-service-sdk abstractionkit viem
  • safe-recovery-service-sdk provides the alert subscription functionality
  • abstractionkit provides Safe account creation and management
  • viem is used for message signing and wallet operations

Environment Setup

.env
# Network Configuration
CHAIN_ID=11155111 # Sepolia testnet chain ID
NODE_URL=https://ethereum-sepolia-rpc.publicnode.com

# Bundler and Paymaster URLs
BUNDLER_URL=https://api.candide.dev/public/v3/11155111
PAYMASTER_URL=https://api.candide.dev/public/v3/11155111

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

# Safe Owner
OWNER_PRIVATE_KEY=0x..

# Alert Configuration
USER_EMAIL=owner@example.com
USER_PHONE=+1234567890

Alert Subscription Steps

Fork the complete code to follow along.

Step 1: Initialize Alert Service

Set up the recovery service client and account credentials.

initialize-alert-service.ts
import { Alerts } from "safe-recovery-service-sdk";
import { SafeAccountV0_3_0 as SafeAccount } from "abstractionkit";
import { privateKeyToAccount } from "viem/accounts";
import * as dotenv from 'dotenv';

dotenv.config();

const recoveryServiceURL = process.env.RECOVERY_SERVICE_URL as string;
const ownerPrivateKey = process.env.OWNER_PRIVATE_KEY as `0x${string}`;
const ownerEmail = process.env.USER_EMAIL as string;
const ownerPhone = process.env.USER_PHONE as string;
const chainId = BigInt(process.env.CHAIN_ID as string);

const ownerAccount = privateKeyToAccount(ownerPrivateKey);
const smartAccount = SafeAccount.initializeNewAccount([ownerAccount.address]);
const safeAccountAddress = smartAccount.accountAddress;

const alertsService = new Alerts(recoveryServiceURL, chainId);

Step 2: Create SIWE Messages

Generate Sign-in with Ethereum (EIP-4361) messages for subscription verification using the SDK helper methods.

create-email-siwe-message.ts
const emailSiweMessage = alertsService.createEmailSubscriptionSiweStatementToSign(
safeAccountAddress,
ownerAccount.address,
ownerEmail
);

Step 3: Sign Messages and Create Subscriptions

Sign the SIWE messages and submit subscription requests for both email and SMS.

subscribe-email-alerts.ts
const emailSignature = await ownerAccount.signMessage({
message: emailSiweMessage
});

const emailSubscriptionId = await alertsService.createEmailSubscription(
safeAccountAddress,
ownerAccount.address,
ownerEmail,
emailSiweMessage,
emailSignature
);

Step 4: Activate Subscriptions

Enter the verification codes received via email and SMS to activate both subscriptions.

activate-email-subscription.ts
const emailVerificationCode = "123456";

await alertsService.activateSubscription(
emailSubscriptionId,
emailVerificationCode
);

Step 5: Verify Active Subscriptions

Check your current active alert subscriptions.

verify-subscriptions.ts
const getSubscriptionsSiweMessage = alertsService.getSubscriptionsSiweStatementToSign(
ownerAccount.address
);

const getSubscriptionsSignature = await ownerAccount.signMessage({
message: getSubscriptionsSiweMessage
});

const activeSubscriptions = await alertsService.getActiveSubscriptions(
safeAccountAddress,
ownerAccount.address,
getSubscriptionsSiweMessage,
getSubscriptionsSignature
);

Complete Working Example

Full Alert Subscription Example
examples/02-alerts-setup/index.ts
loading...

What's Next

Managing Subscriptions

Update Contact Information

To change your alert email or phone number:

  1. Unsubscribe from current alerts for that channel using the unsubscribe method with SIWE authentication
  2. Subscribe with new contact information using the subscription flow
  3. Verify the new subscription by entering the OTP code sent to your new contact

Unsubscribe from Specific Channel

To stop receiving alerts on one channel:

// Generate SIWE message for unsubscribing
const unsubscribeSiweMessage = alertsService.unsubscribeSiweStatementToSign(
ownerAccount.address
);

// Sign the SIWE message
const unsubscribeSignature = await ownerAccount.signMessage({
message: unsubscribeSiweMessage
});

// Unsubscribe from email only
const emailUnsubscribeSuccess = await alertsService.unsubscribe(
"your-email-subscription-id",
ownerAccount.address,
unsubscribeSiweMessage,
unsubscribeSignature
);

if (emailUnsubscribeSuccess) {
console.log("Successfully unsubscribed from email alerts");
// SMS subscription remains active
}

Unsubscribe from All Channels

To stop all recovery alerts:

// Generate SIWE message for unsubscribing
const unsubscribeSiweMessage = alertsService.unsubscribeSiweStatementToSign(
ownerAccount.address
);

// Sign the SIWE message
const unsubscribeSignature = await ownerAccount.signMessage({
message: unsubscribeSiweMessage
});

// Unsubscribe from all channels
const emailUnsubscribeSuccess = await alertsService.unsubscribe(
"email-subscription-id",
ownerAccount.address,
unsubscribeSiweMessage,
unsubscribeSignature
);

const smsUnsubscribeSuccess = await alertsService.unsubscribe(
"sms-subscription-id",
ownerAccount.address,
unsubscribeSiweMessage,
unsubscribeSignature
);

if (emailUnsubscribeSuccess && smsUnsubscribeSuccess) {
console.log("Successfully unsubscribed from all alerts");
}