#
Receiving Emails
Learn how to receive emails in real-time and sync historical messages.
#
Real-Time Inbox
Watch for new emails as they arrive:
await for (final email in client.watchInbox()) {
print('From: ${email.from}');
print('Subject: ${email.subject}');
print('Body: ${email.body}');
}
#
Historical Sync
Fetch and store all historical emails:
// Sync all emails from relays to local DB
await client.sync();
// Then read from local DB
final emails = await client.getEmails();
#
Combined Approach
Typical usage pattern:
// 1. Initial sync
await client.sync();
// 2. Get cached emails
final emails = await client.getEmails();
displayEmails(emails);
// 3. Watch for new emails
client.watchInbox().listen((email) {
displayNewEmail(email);
});
#
Email Model
class Email {
final String id; // Unique event ID
final String from; // Sender address
final String to; // Recipient address
final String subject; // Email subject
final String body; // Plain text body
final DateTime date; // Email date
final String senderPubkey; // Nostr pubkey of sender
final String rawContent; // Original RFC 2822 content
}
#
Query Emails
#
Get All Emails
final emails = await client.getEmails();
#
With Pagination
final emails = await client.getEmails(
limit: 20,
offset: 0,
);
#
Get Single Email
final email = await client.getEmail('event-id-here');
if (email != null) {
print(email.body);
}
#
Delete Emails
await client.delete('event-id-here');
#
How It Works
#
Subscription Flow
sequenceDiagram
participant Client
participant Relays
participant Store
Client->>Relays: Subscribe to Kind 1059
loop On each event
Relays->>Client: Gift-wrapped event
Client->>Client: Check if processed
Client->>Client: Unwrap NIP-59
Client->>Client: Parse RFC 2822
Client->>Store: Save email
Client->>Store: Mark processed
Client-->>App: Yield email
end
#
Event Processing
- Subscribe to kind 1059 (gift wrap) events tagged with your pubkey
- Check if event was already processed
- Unwrap NIP-59 encryption to get inner event
- Verify inner event is kind 1301 (email)
- Parse RFC 2822 content into Email model
- Store in local database
- Yield to stream listener
#
Deduplication
The SDK automatically handles duplicates:
// Events are tracked by ID
if (await _store.isProcessed(event.id)) {
continue; // Skip already processed
}
// After processing
await _store.markProcessed(event.id);
#
Error Handling
Malformed events are silently skipped:
try {
final unwrapped = await _unwrapGiftWrap(event);
if (unwrapped == null) continue;
if (unwrapped.kind != 1301) continue;
// ...
} catch (e) {
continue; // Skip malformed events
}
#
Flutter Integration
class InboxController extends GetxController {
final NostrMailClient _client;
final emails = <Email>[].obs;
StreamSubscription? _subscription;
@override
void onInit() {
super.onInit();
_loadEmails();
_watchInbox();
}
Future<void> _loadEmails() async {
await _client.sync();
emails.value = await _client.getEmails();
}
void _watchInbox() {
_subscription = _client.watchInbox().listen((email) {
emails.insert(0, email);
});
}
@override
void onClose() {
_subscription?.cancel();
super.onClose();
}
}