vybekiit 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/bin.js +1697 -562
  2. package/dist/global-skills/add-ai/SKILL.md +1 -1
  3. package/dist/global-skills/add-analytics/SKILL.md +1 -1
  4. package/dist/global-skills/add-blog/SKILL.md +1 -1
  5. package/dist/global-skills/add-crud/SKILL.md +1 -1
  6. package/dist/global-skills/add-files/SKILL.md +1 -1
  7. package/dist/global-skills/add-images/SKILL.md +1 -1
  8. package/dist/global-skills/add-language/SKILL.md +1 -1
  9. package/dist/global-skills/add-notifications/SKILL.md +1 -1
  10. package/dist/global-skills/add-realtime/SKILL.md +1 -1
  11. package/dist/global-skills/add-route/SKILL.md +1 -1
  12. package/dist/global-skills/add-search/SKILL.md +1 -1
  13. package/dist/global-skills/add-signin/SKILL.md +1 -1
  14. package/dist/global-skills/add-teams/SKILL.md +1 -1
  15. package/dist/global-skills/add-upload/SKILL.md +1 -1
  16. package/dist/global-skills/aws-serverless/SKILL.md +44 -44
  17. package/dist/global-skills/aws-serverless/assets/powertools-handler.py +1 -2
  18. package/dist/global-skills/aws-serverless/references/api-gateway.md +470 -50
  19. package/dist/global-skills/aws-serverless/references/architecture.md +186 -47
  20. package/dist/global-skills/aws-serverless/references/concurrency.md +158 -44
  21. package/dist/global-skills/aws-serverless/references/deployment.md +1 -1
  22. package/dist/global-skills/aws-serverless/references/event-sources.md +391 -72
  23. package/dist/global-skills/aws-serverless/references/lambda.md +428 -69
  24. package/dist/global-skills/aws-serverless/references/orchestration.md +384 -65
  25. package/dist/global-skills/aws-serverless/references/production.md +415 -78
  26. package/dist/global-skills/aws-serverless/references/troubleshooting.md +626 -79
  27. package/dist/global-skills/back-up-my-code/SKILL.md +1 -1
  28. package/dist/global-skills/buy-domain/SKILL.md +1 -1
  29. package/dist/global-skills/check-safety/SKILL.md +1 -1
  30. package/dist/global-skills/configure-capabilities/SKILL.md +1 -1
  31. package/dist/global-skills/connect-account/SKILL.md +1 -1
  32. package/dist/global-skills/connect-account-backend/SKILL.md +1 -1
  33. package/dist/global-skills/design-my-data/SKILL.md +1 -1
  34. package/dist/global-skills/doctor/SKILL.md +1 -1
  35. package/dist/global-skills/feedback/SKILL.md +63 -0
  36. package/dist/global-skills/go-live/SKILL.md +1 -1
  37. package/dist/global-skills/harden/SKILL.md +1 -1
  38. package/dist/global-skills/instrument-feature-flags/references/usage.md +0 -35
  39. package/dist/global-skills/instrument-product-analytics/SKILL.md +1 -1
  40. package/dist/global-skills/instrument-product-analytics/references/android.md +0 -36
  41. package/dist/global-skills/instrument-product-analytics/references/configuration.md +0 -1
  42. package/dist/global-skills/instrument-product-analytics/references/flutter.md +0 -37
  43. package/dist/global-skills/instrument-product-analytics/references/posthog-python.md +2 -3
  44. package/dist/global-skills/instrument-product-analytics/references/usage.md +0 -35
  45. package/dist/global-skills/manifest.json +2 -1
  46. package/dist/global-skills/neon/SKILL.md +20 -27
  47. package/dist/global-skills/neon-ai-gateway/SKILL.md +2 -68
  48. package/dist/global-skills/neon-functions/SKILL.md +7 -7
  49. package/dist/global-skills/neon-object-storage/SKILL.md +2 -2
  50. package/dist/global-skills/neon-postgres/SKILL.md +5 -5
  51. package/dist/global-skills/neon-postgres-branches/SKILL.md +1 -1
  52. package/dist/global-skills/onboarding/SKILL.md +1 -1
  53. package/dist/global-skills/plan-my-idea/SKILL.md +1 -1
  54. package/dist/global-skills/publish-app/SKILL.md +1 -1
  55. package/dist/global-skills/publish-extension/SKILL.md +1 -1
  56. package/dist/global-skills/reset-password/SKILL.md +1 -1
  57. package/dist/global-skills/save-data/SKILL.md +1 -1
  58. package/dist/global-skills/setup-email/SKILL.md +1 -1
  59. package/dist/global-skills/setup-payments/SKILL.md +1 -1
  60. package/dist/global-skills/setup-sms/SKILL.md +1 -1
  61. package/dist/global-skills/sign-in-with-email-link/SKILL.md +1 -1
  62. package/dist/global-skills/sign-in-with-google/SKILL.md +1 -1
  63. package/dist/global-skills/sign-in-with-phone/SKILL.md +1 -1
  64. package/dist/global-skills/stripe-best-practices/SKILL.md +6 -11
  65. package/dist/global-skills/stripe-best-practices/references/billing.md +0 -5
  66. package/dist/global-skills/stripe-best-practices/references/payments.md +2 -4
  67. package/dist/global-skills/stripe-best-practices/references/tax.md +8 -78
  68. package/dist/global-skills/track-errors/SKILL.md +1 -1
  69. package/dist/global-skills/update-kit/SKILL.md +1 -1
  70. package/dist/global-skills/wire-auth/SKILL.md +1 -1
  71. package/dist/global-skills/wire-database/SKILL.md +1 -1
  72. package/dist/global-skills/wire-email/SKILL.md +1 -1
  73. package/dist/global-skills/wire-payments/SKILL.md +1 -1
  74. package/package.json +8 -8
  75. package/dist/global-skills/email-best-practices/.github/workflows/sync-skills.yml +0 -30
  76. package/dist/global-skills/email-best-practices/README.md +0 -63
  77. package/dist/global-skills/email-best-practices/references/accessibility.md +0 -189
  78. package/dist/global-skills/email-best-practices/references/compliance.md +0 -125
  79. package/dist/global-skills/email-best-practices/references/deliverability.md +0 -121
  80. package/dist/global-skills/email-best-practices/references/email-capture.md +0 -129
  81. package/dist/global-skills/email-best-practices/references/email-types.md +0 -173
  82. package/dist/global-skills/email-best-practices/references/list-management.md +0 -157
  83. package/dist/global-skills/email-best-practices/references/marketing-emails.md +0 -115
  84. package/dist/global-skills/email-best-practices/references/sending-reliability.md +0 -155
  85. package/dist/global-skills/email-best-practices/references/transactional-email-catalog.md +0 -418
  86. package/dist/global-skills/email-best-practices/references/transactional-emails.md +0 -92
  87. package/dist/global-skills/email-best-practices/references/webhooks-events.md +0 -167
  88. package/dist/global-skills/email-best-practices/tests/README.md +0 -35
  89. package/dist/global-skills/email-best-practices/tests/scenarios/01-spam-deliverability.md +0 -46
  90. package/dist/global-skills/email-best-practices/tests/scenarios/02-multi-region-compliance.md +0 -48
  91. package/dist/global-skills/email-best-practices/tests/scenarios/03-retry-idempotency.md +0 -36
  92. package/dist/global-skills/email-best-practices/tests/scenarios/04-webhook-bounce-handling.md +0 -52
  93. package/dist/global-skills/email-best-practices/tests/scenarios/05-new-saas-email-plan.md +0 -51
  94. package/dist/global-skills/neon-postgres/references/neon-sdk.md +0 -262
@@ -1,173 +0,0 @@
1
- # Email Types: Transactional vs Marketing
2
-
3
- Understanding the difference between transactional and marketing emails is crucial for compliance, deliverability, and user experience. This guide explains the distinctions and provides a catalog of transactional emails your app should include.
4
-
5
- ## When to Use This
6
-
7
- - Deciding whether an email should be transactional or marketing
8
- - Understanding legal distinctions between email types
9
- - Planning what transactional emails your app needs
10
- - Ensuring compliance with email regulations
11
- - Setting up separate sending infrastructure
12
-
13
- ## Transactional vs Marketing: Key Differences
14
-
15
- ### Transactional Emails
16
-
17
- **Definition:** Emails that facilitate or confirm a transaction the user initiated or expects. They're directly related to an action the user took or are legal notices you're required to serve.
18
-
19
- **Characteristics:**
20
- - User-initiated or expected
21
- - Time-sensitive and actionable
22
- - Required for the user to complete an action
23
- - Does not include promotional material or offers
24
- - Can be sent without explicit opt-in (with limitations)
25
-
26
- **Examples:**
27
- - Password reset links
28
- - Order confirmations
29
- - Account verification
30
- - OTP/2FA codes
31
- - Shipping notifications
32
-
33
- **Analogy:**
34
- Think of transactional emails for everything that would leave you with a paper receipt in the real world: invoices, parking ticket, booking confirmation, etc.
35
-
36
- ### Marketing Emails
37
-
38
- **Definition:** Emails sent for promotional, advertising, or informational purposes that are not directly related to a specific transaction or legal requirement.
39
-
40
- **Characteristics:**
41
- - Promotional or informational content
42
- - Not time-sensitive to complete a transaction
43
- - Require explicit opt-in (consent)
44
- - Must include unsubscribe options
45
- - Subject to stricter compliance requirements
46
-
47
- **Examples:**
48
- - Newsletters
49
- - Abandoned cart
50
- - Product announcements
51
- - Promotional offers
52
- - Company updates
53
- - Educational content
54
-
55
- ## Legal Distinctions
56
-
57
- ### CAN-SPAM Act (US)
58
-
59
- **Transactional emails:**
60
- - Can be sent without opt-in
61
- - Must be related to a transaction
62
- - Cannot contain promotional content (with exceptions)
63
- - Must identify sender and provide contact information
64
-
65
- **Marketing emails:**
66
- - Require opt-out mechanism (not opt-in in US)
67
- - Must include clear sender identification
68
- - Must include physical mailing address
69
- - Must honor opt-out requests within 10 business days
70
-
71
- ### GDPR (EU)
72
-
73
- **Transactional emails:**
74
- - Can be sent based on legitimate interest or contract fulfillment
75
- - Must be necessary for service delivery
76
- - Cannot contain marketing content without consent
77
-
78
- **Marketing emails:**
79
- - Require explicit opt-in consent
80
- - Must clearly state purpose of data collection
81
- - Must provide easy unsubscribe
82
- - Subject to data protection requirements
83
-
84
- ### CASL (Canada)
85
-
86
- **Transactional emails:**
87
- - Can be sent without consent if related to ongoing business relationship
88
- - Must be factual and not promotional
89
-
90
- **Marketing emails:**
91
- - Require express or implied consent
92
- - Must include unsubscribe mechanism
93
- - Must identify sender clearly
94
-
95
- ## When to Use Each Type
96
-
97
- ### Use Transactional When:
98
-
99
- - User needs the email to complete an action
100
- - Email confirms a transaction or account change
101
- - Email provides security-related information
102
- - Email is expected based on user action
103
- - Content is time-sensitive and actionable
104
- - You're required to serve a notification for compliance
105
-
106
- ### Use Marketing When:
107
-
108
- - Promoting products or services
109
- - Sending newsletters or updates
110
- - Sharing educational content
111
- - Announcing features or company news
112
- - Content is not required for a transaction
113
-
114
- ## Hybrid Emails: The Gray Area
115
-
116
- Some emails mix transactional and marketing content. This isn't best practice and should be avoided.
117
-
118
- **Best practice:** Keep transactional and marketing separate.
119
-
120
- **Example of problematic hybrid:**
121
- - Newsletter (marketing) with a small order status update (transactional)
122
-
123
- ## Transactional Email Catalog
124
-
125
- For a complete catalog of transactional emails and recommended combinations by app type, see [Transactional Email Catalog](./transactional-email-catalog.md).
126
-
127
- **Quick reference - Essential emails for most apps:**
128
- 1. **Email verification** - Required for account creation
129
- 2. **Password reset** - Required for account recovery
130
- 3. **Welcome email** - Good user experience
131
-
132
- The catalog includes detailed guidance for:
133
- - Authentication-focused apps
134
- - Newsletter / content platforms
135
- - E-commerce / marketplaces
136
- - SaaS / subscription services
137
- - Financial / fintech apps
138
- - Social / community platforms
139
- - Developer tools / API platforms
140
- - Healthcare / HIPAA-compliant apps
141
-
142
- ## Sending Infrastructure
143
-
144
- ### Separate subdomains
145
-
146
- **Best practice:** Use separate sending subdomains for transactional and marketing emails.
147
-
148
- **Benefits:**
149
- - Protect transactional deliverability
150
- - Different authentication domains
151
- - Independent reputation
152
- - Easier compliance management
153
-
154
- **Implementation:**
155
- - Use different subdomains (e.g., `t.example.com` for transactional, `m.example.com` for marketing)
156
-
157
- ### Email Service Considerations
158
-
159
- Choose an email service that:
160
- - Provides reliable delivery for transactional emails
161
- - Offers separate sending domains
162
- - Has good API for programmatic sending
163
- - Provides webhooks for delivery events
164
- - Supports authentication setup (SPF, DKIM, DMARC)
165
-
166
- Services like Resend are designed for transactional emails and provide the infrastructure and tools needed for reliable delivery. They also offer powerful marketing features.
167
-
168
- ## Related Topics
169
-
170
- - [Transactional Emails](./transactional-emails.md) - Best practices for sending transactional emails
171
- - [Marketing Emails](./marketing-emails.md) - Best practices for marketing emails
172
- - [Compliance](./compliance.md) - Legal requirements for each email type
173
- - [Deliverability](./deliverability.md) - Ensuring transactional emails are delivered
@@ -1,157 +0,0 @@
1
- # List Management
2
-
3
- Maintaining clean email lists through suppression, hygiene, and data retention.
4
-
5
- ## Suppression Lists
6
-
7
- A suppression list prevents sending to addresses that should never receive email.
8
-
9
- ### What to Suppress
10
-
11
- | Reason | Action | Can Unsuppress? |
12
- |--------|--------|-----------------|
13
- | Hard bounce | Add immediately | No (address invalid) |
14
- | Complaint (spam) | Add immediately | No (legal requirement) |
15
- | Soft bounce (3x) | Add after threshold | Yes, after 30-90 days |
16
- | Manual removal | Add on request | Only if user requests |
17
-
18
- ### Implementation
19
-
20
- ```typescript
21
- // Suppression list schema
22
- interface SuppressionEntry {
23
- email: string;
24
- reason: 'hard_bounce' | 'complaint' | 'unsubscribe' | 'soft_bounce' | 'manual';
25
- created_at: Date;
26
- source_email_id?: string; // Which email triggered this
27
- }
28
-
29
- // Check before every send
30
- async function canSendTo(email: string): Promise<boolean> {
31
- const suppressed = await db.suppressions.findOne({ email });
32
- return !suppressed;
33
- }
34
-
35
- // Add to suppression list
36
- async function suppressEmail(email: string, reason: string, sourceId?: string) {
37
- await db.suppressions.upsert({
38
- email: email.toLowerCase(),
39
- reason,
40
- created_at: new Date(),
41
- source_email_id: sourceId,
42
- });
43
- }
44
- ```
45
-
46
- ### Pre-Send Check
47
-
48
- **Always check suppression before sending:**
49
-
50
- ```typescript
51
- async function sendEmail(to: string, emailData: EmailData) {
52
- if (!await canSendTo(to)) {
53
- console.log(`Skipping suppressed email: ${to}`);
54
- return { skipped: true, reason: 'suppressed' };
55
- }
56
-
57
- return await resend.emails.send({ to, ...emailData });
58
- }
59
- ```
60
-
61
- ## List Hygiene
62
-
63
- Regular maintenance to keep lists healthy.
64
-
65
- ### Automated Cleanup
66
-
67
- | Task | Frequency | Action |
68
- |------|-----------|--------|
69
- | Remove hard bounces | Real-time (via webhook) | Immediate suppression |
70
- | Remove complaints | Real-time (via webhook) | Immediate suppression |
71
- | Process unsubscribes | Real-time | Remove from marketing lists |
72
- | Review soft bounces | Daily | Suppress after 3 failures |
73
- | Remove inactive | Monthly | Re-engagement → remove |
74
-
75
- Learn more: https://resend.com/docs/knowledge-base/audience-hygiene
76
-
77
- ### Re-engagement Campaigns
78
-
79
- Before removing inactive subscribers:
80
-
81
- 1. **Identify inactive:** No opens/clicks in 45-90 days
82
- 2. **Send re-engagement:** "We miss you" or "Still interested?"
83
- 3. **Wait 14-30 days** for response
84
- 4. **Remove non-responders** from active lists
85
-
86
- ```typescript
87
- async function runReengagement() {
88
- const inactive = await getInactiveSubscribers(90); // 90 days
89
-
90
- for (const subscriber of inactive) {
91
- if (!subscriber.reengagement_sent) {
92
- await sendReengagementEmail(subscriber);
93
- await markReengagementSent(subscriber.email);
94
- } else if (daysSince(subscriber.reengagement_sent) > 30) {
95
- await removeFromMarketingLists(subscriber.email);
96
- }
97
- }
98
- }
99
- ```
100
-
101
- ## Data Retention
102
-
103
- ### Email Logs
104
-
105
- | Data Type | Recommended Retention | Notes |
106
- |-----------|----------------------|-------|
107
- | Send attempts | 90 days | Debugging, analytics |
108
- | Delivery status | 90 days | Compliance, reporting |
109
- | Bounce/complaint events | 3 years | Required for CASL |
110
- | Suppression list | Indefinite | Never delete |
111
- | Email content | 30 days | Storage costs |
112
- | Consent records | 3 years after expiry | Legal requirement |
113
-
114
- ### Retention Policy Implementation
115
-
116
- ```typescript
117
- // Daily cleanup job
118
- async function cleanupOldData() {
119
- const now = new Date();
120
-
121
- // Delete old email logs (keep 90 days)
122
- await db.emailLogs.deleteMany({
123
- created_at: { $lt: subDays(now, 90) }
124
- });
125
-
126
- // Delete old email content (keep 30 days)
127
- await db.emailContent.deleteMany({
128
- created_at: { $lt: subDays(now, 30) }
129
- });
130
-
131
- // Never delete: suppressions, consent records
132
- }
133
- ```
134
-
135
- ## Metrics to Monitor
136
-
137
- | Metric | Target | Alert Threshold |
138
- |--------|--------|-----------------|
139
- | Bounce rate | <2% | >2% |
140
- | Complaint rate | <0.05% | >0.05% |
141
- | Suppression list growth | Stable | Sudden spike |
142
-
143
- ## Transactional vs Marketing Lists
144
-
145
- **Keep separate:**
146
- - Transactional: Can send to anyone with account relationship
147
- - Marketing: Only opted-in subscribers
148
-
149
- **Suppression applies to both:** Hard bounces and complaints suppress across all email types.
150
-
151
- **Unsubscribe is marketing-only:** User unsubscribing from marketing can still receive transactional emails (password resets, order confirmations).
152
-
153
- ## Related
154
-
155
- - [Webhooks & Events](./webhooks-events.md) - Receive bounce/complaint notifications
156
- - [Deliverability](./deliverability.md) - How list hygiene affects sender reputation
157
- - [Compliance](./compliance.md) - Legal requirements for data retention
@@ -1,115 +0,0 @@
1
- # Marketing Email Best Practices
2
-
3
- Promotional emails that require explicit consent and provide value to recipients.
4
-
5
- ## Core Principles
6
-
7
- 1. **Consent first** - Explicit opt-in required (especially GDPR/CASL)
8
- 2. **Value-driven** - Provide useful content, not just promotions
9
- 3. **Respect preferences** - Let users control frequency and content types
10
-
11
- ## Opt-In Requirements
12
-
13
- ### Explicit Opt-In
14
-
15
- **What counts:**
16
- - User checks unchecked box
17
- - User clicks "Subscribe" button
18
- - User completes form with clear subscription intent
19
-
20
- **What doesn't count:**
21
- - Pre-checked boxes
22
- - Opt-out model
23
- - Assumed consent from purchase
24
- - Purchased/rented lists
25
-
26
- ### Informed Consent
27
-
28
- Disclose: email types, frequency, sender identity, how to unsubscribe.
29
-
30
- ✅ "Subscribe to our weekly newsletter with product updates and tips"
31
- ❌ "Sign up for emails"
32
-
33
- ### Double Opt-In (Recommended)
34
-
35
- 1. User submits email
36
- 2. Send confirmation email with verification link
37
- 3. User clicks to confirm
38
- 4. Add to list only after confirmation
39
-
40
- Benefits: Verifies deliverability, confirms intent, reduces complaints, required in some regions (Germany).
41
-
42
- ## Unsubscribe Requirements
43
-
44
- **Must be:**
45
- - Prominent in every email
46
- - One-click (preferred)
47
- - Immediate (GDPR) or within 10 days (CAN-SPAM) (immediate preferred)
48
- - Free, no login required
49
-
50
- **Preference center options:** Frequency (daily/weekly/monthly), content types, complete unsubscribe.
51
-
52
- ## Content and Design
53
-
54
- ### Subject Lines
55
-
56
- - Clear and specific (50 chars or less for mobile)
57
- - Create curiosity without misleading
58
- - A/B test regularly
59
-
60
- ✅ "Your weekly digest: 5 productivity tips"
61
- ❌ "You won't believe what happened!"
62
-
63
- ### Structure
64
-
65
- **Above fold:** Value proposition, primary CTA, engaging visual
66
-
67
- **Body:** Scannable (short paragraphs, bullets), clear hierarchy, multiple CTAs
68
-
69
- **Footer:** Unsubscribe link, company info, physical address (CAN-SPAM), social links
70
-
71
- ### Mobile-First
72
-
73
- - Single column layout
74
- - 44x44px minimum buttons
75
- - 16px minimum text
76
- - Test on iOS, Android, dark mode
77
-
78
- ## Segmentation
79
-
80
- **Segment by:** Behavior (purchases, activity), demographics, preferences, engagement level, signup source.
81
-
82
- Benefits: Higher open/click rates, lower unsubscribes, better experience.
83
-
84
- ## Personalization
85
-
86
- **Options:** Name in subject/greeting, location-specific content, behavior-based recommendations, purchase history.
87
-
88
- **Don't over-personalize** - can feel intrusive. Use data you have permission to use.
89
-
90
- ## Frequency and Timing
91
-
92
- **Frequency:** Start conservative, increase based on engagement, let users set preferences, monitor unsubscribe rates.
93
-
94
- **Timing:** Weekday mornings (9-11 AM local), Tuesday-Thursday often best. Test your specific audience.
95
-
96
- ## List Hygiene
97
-
98
- **Remove immediately:** Hard bounces, unsubscribes, complaints
99
-
100
- **Remove after inactivity:** Send re-engagement campaign first, then remove non-responders
101
-
102
- **Monitor:** Bounce rate <2%, complaint rate <0.05%
103
-
104
- ## Required Elements (All Marketing Emails)
105
-
106
- - Clear sender identification
107
- - Physical mailing address (CAN-SPAM)
108
- - Unsubscribe mechanism
109
- - Indication it's marketing (GDPR)
110
-
111
- ## Related
112
-
113
- - [Compliance](./compliance.md) - Detailed legal requirements by region
114
- - [Email Capture](./email-capture.md) - Collecting consent properly
115
- - [List Management](./list-management.md) - Maintaining list hygiene
@@ -1,155 +0,0 @@
1
- # Sending Reliability
2
-
3
- Ensuring emails are sent exactly once and handling failures gracefully.
4
-
5
- ## Idempotency
6
-
7
- Prevent duplicate emails when retrying failed requests.
8
-
9
- ### The Problem
10
-
11
- Network issues, timeouts, or server errors can leave you uncertain if an email was sent. Retrying without idempotency risks sending duplicates.
12
-
13
- ### Solution: Idempotency Keys
14
-
15
- Send a unique key with each request. If the same key is sent again, the server returns the original response instead of sending another email.
16
-
17
- ```typescript
18
- // Generate deterministic key based on the business event
19
- const idempotencyKey = `password-reset-${userId}-${resetRequestId}`;
20
-
21
- await resend.emails.send({
22
- from: 'noreply@example.com',
23
- to: user.email,
24
- subject: 'Reset your password',
25
- html: emailHtml,
26
- }, {
27
- headers: {
28
- 'Idempotency-Key': idempotencyKey
29
- }
30
- });
31
- ```
32
-
33
- ### Key Generation Strategies
34
-
35
- | Strategy | Example | Use When |
36
- |----------|---------|----------|
37
- | Event-based | `order-confirm-${orderId}` | One email per event (recommended) |
38
- | Request-scoped | `reset-${userId}-${resetRequestId}` | Retries within same request |
39
- | UUID | `crypto.randomUUID()` | No natural key (generate once, reuse on retry) |
40
-
41
- **Best practice:** Use deterministic keys based on the business event. If you retry the same logical send, the same key must be generated. Avoid `Date.now()` or random values generated fresh on each attempt.
42
-
43
- **Key expiration:** Idempotency keys are typically cached for 24 hours. Retries within this window return the original response. After expiration, the same key triggers a new send—so complete your retry logic well within 24 hours.
44
-
45
- ## Retry Logic
46
-
47
- Handle transient failures with exponential backoff.
48
-
49
- ### When to Retry
50
-
51
- | Error Type | Retry? | Notes |
52
- |------------|--------|-------|
53
- | 5xx (server error) | ✅ Yes | Transient, likely to resolve |
54
- | 429 (rate limit) | ✅ Yes | Wait for rate limit window |
55
- | 4xx (client error) | ❌ No | Fix the request first |
56
- | Network timeout | ✅ Yes | Transient |
57
- | DNS failure | ✅ Yes | May be transient |
58
-
59
- ### Exponential Backoff
60
-
61
- ```typescript
62
- async function sendWithRetry(emailData, maxRetries = 3) {
63
- for (let attempt = 0; attempt < maxRetries; attempt++) {
64
- try {
65
- return await resend.emails.send(emailData);
66
- } catch (error) {
67
- if (!isRetryable(error) || attempt === maxRetries - 1) {
68
- throw error;
69
- }
70
- const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
71
- await sleep(delay + Math.random() * 1000); // Add jitter
72
- }
73
- }
74
- }
75
-
76
- function isRetryable(error) {
77
- return error.statusCode >= 500 ||
78
- error.statusCode === 429 ||
79
- error.code === 'ETIMEDOUT';
80
- }
81
- ```
82
-
83
- **Backoff schedule:** 1s → 2s → 4s → 8s (with jitter to prevent thundering herd)
84
-
85
- ## Error Handling
86
-
87
- ### Common Error Codes
88
-
89
- | Code | Meaning | Action |
90
- |------|---------|--------|
91
- | 400 | Bad request | Fix payload (invalid email, missing field) |
92
- | 401 | Unauthorized | Check API key |
93
- | 403 | Forbidden | Check permissions, domain verification |
94
- | 404 | Not found | Check endpoint URL |
95
- | 422 | Validation error | Fix request data |
96
- | 429 | Rate limited | Back off, retry after delay |
97
- | 500 | Server error | Retry with backoff |
98
- | 503 | Service unavailable | Retry with backoff |
99
-
100
- ### Error Handling Pattern
101
-
102
- ```typescript
103
- try {
104
- const result = await resend.emails.send(emailData);
105
- await logSuccess(result.id, emailData);
106
- } catch (error) {
107
- if (error.statusCode === 429) {
108
- await queueForRetry(emailData, error.retryAfter);
109
- } else if (error.statusCode >= 500) {
110
- await queueForRetry(emailData);
111
- } else {
112
- await logFailure(error, emailData);
113
- await alertOnCriticalEmail(emailData); // For password resets, etc.
114
- }
115
- }
116
- ```
117
-
118
- ## Queuing for Reliability
119
-
120
- For critical emails, use a queue to ensure delivery even if the initial send fails.
121
-
122
- **Benefits:**
123
- - Survives application restarts
124
- - Automatic retry handling
125
- - Rate limit management
126
- - Audit trail
127
-
128
- **Simple pattern:**
129
- 1. Write email to queue/database with "pending" status
130
- 2. Process queue, attempt send
131
- 3. On success: mark "sent", store message ID
132
- 4. On retryable failure: increment retry count, schedule retry
133
- 5. On permanent failure: mark "failed", alert
134
-
135
- ## Timeouts
136
-
137
- Set appropriate timeouts to avoid hanging requests.
138
-
139
- ```typescript
140
- const controller = new AbortController();
141
- const timeout = setTimeout(() => controller.abort(), 10000);
142
-
143
- try {
144
- await resend.emails.send(emailData, { signal: controller.signal });
145
- } finally {
146
- clearTimeout(timeout);
147
- }
148
- ```
149
-
150
- **Recommended:** 10-30 seconds for email API calls.
151
-
152
- ## Related
153
-
154
- - [Webhooks & Events](./webhooks-events.md) - Process delivery confirmations and failures
155
- - [List Management](./list-management.md) - Handle bounces and suppress invalid addresses