@cakemail-org/cakemail-cli 1.5.0 → 2.0.0

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 (234) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.env.example +40 -0
  3. package/.env.test.example +45 -0
  4. package/CHANGELOG.md +1031 -0
  5. package/README.md +319 -15
  6. package/audit-formats.js +128 -0
  7. package/cakemail.rb +20 -0
  8. package/dist/cli.js +27 -10
  9. package/dist/cli.js.map +1 -1
  10. package/dist/client.d.ts +2 -0
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +16 -6
  13. package/dist/client.js.map +1 -1
  14. package/dist/commands/account.js +1 -1
  15. package/dist/commands/account.js.map +1 -1
  16. package/dist/commands/attributes.js +1 -1
  17. package/dist/commands/attributes.js.map +1 -1
  18. package/dist/commands/campaigns.d.ts.map +1 -1
  19. package/dist/commands/campaigns.js +103 -8
  20. package/dist/commands/campaigns.js.map +1 -1
  21. package/dist/commands/config.d.ts.map +1 -1
  22. package/dist/commands/config.js +63 -4
  23. package/dist/commands/config.js.map +1 -1
  24. package/dist/commands/contacts.d.ts.map +1 -1
  25. package/dist/commands/contacts.js +91 -12
  26. package/dist/commands/contacts.js.map +1 -1
  27. package/dist/commands/emails.js +1 -1
  28. package/dist/commands/emails.js.map +1 -1
  29. package/dist/commands/interests.d.ts +5 -0
  30. package/dist/commands/interests.d.ts.map +1 -0
  31. package/dist/commands/interests.js +172 -0
  32. package/dist/commands/interests.js.map +1 -0
  33. package/dist/commands/lists.d.ts.map +1 -1
  34. package/dist/commands/lists.js +6 -8
  35. package/dist/commands/lists.js.map +1 -1
  36. package/dist/commands/logs.d.ts +5 -0
  37. package/dist/commands/logs.d.ts.map +1 -0
  38. package/dist/commands/logs.js +237 -0
  39. package/dist/commands/logs.js.map +1 -0
  40. package/dist/commands/reports.js +1 -1
  41. package/dist/commands/reports.js.map +1 -1
  42. package/dist/commands/segments.js +1 -1
  43. package/dist/commands/segments.js.map +1 -1
  44. package/dist/commands/senders.d.ts.map +1 -1
  45. package/dist/commands/senders.js +11 -8
  46. package/dist/commands/senders.js.map +1 -1
  47. package/dist/commands/suppressed.js +1 -1
  48. package/dist/commands/suppressed.js.map +1 -1
  49. package/dist/commands/tags.d.ts +5 -0
  50. package/dist/commands/tags.d.ts.map +1 -0
  51. package/dist/commands/tags.js +124 -0
  52. package/dist/commands/tags.js.map +1 -0
  53. package/dist/commands/templates.js +1 -1
  54. package/dist/commands/templates.js.map +1 -1
  55. package/dist/commands/transactional-templates.d.ts +5 -0
  56. package/dist/commands/transactional-templates.d.ts.map +1 -0
  57. package/dist/commands/transactional-templates.js +354 -0
  58. package/dist/commands/transactional-templates.js.map +1 -0
  59. package/dist/commands/webhooks.js +1 -1
  60. package/dist/commands/webhooks.js.map +1 -1
  61. package/dist/utils/auth.d.ts +8 -1
  62. package/dist/utils/auth.d.ts.map +1 -1
  63. package/dist/utils/auth.js +39 -11
  64. package/dist/utils/auth.js.map +1 -1
  65. package/dist/utils/config-file.d.ts +7 -0
  66. package/dist/utils/config-file.d.ts.map +1 -1
  67. package/dist/utils/config-file.js +15 -0
  68. package/dist/utils/config-file.js.map +1 -1
  69. package/dist/utils/config.d.ts +2 -0
  70. package/dist/utils/config.d.ts.map +1 -1
  71. package/dist/utils/config.js +12 -4
  72. package/dist/utils/config.js.map +1 -1
  73. package/dist/utils/errors.js +1 -1
  74. package/dist/utils/errors.js.map +1 -1
  75. package/dist/utils/list-defaults.d.ts +33 -0
  76. package/dist/utils/list-defaults.d.ts.map +1 -0
  77. package/dist/utils/list-defaults.js +52 -0
  78. package/dist/utils/list-defaults.js.map +1 -0
  79. package/dist/utils/output.d.ts.map +1 -1
  80. package/dist/utils/output.js +36 -13
  81. package/dist/utils/output.js.map +1 -1
  82. package/dist/utils/progress.d.ts.map +1 -1
  83. package/dist/utils/progress.js +32 -4
  84. package/dist/utils/progress.js.map +1 -1
  85. package/dist/utils/spinner.d.ts +17 -0
  86. package/dist/utils/spinner.d.ts.map +1 -0
  87. package/dist/utils/spinner.js +43 -0
  88. package/dist/utils/spinner.js.map +1 -0
  89. package/docs/DOCUMENTATION-STANDARD.md +1068 -0
  90. package/docs/README.md +161 -0
  91. package/docs/developer/ARCHITECTURE.md +516 -0
  92. package/docs/developer/AUTH.md +204 -0
  93. package/docs/developer/CONTRIBUTING.md +227 -0
  94. package/docs/developer/DOCUMENTATION_SUMMARY.md +346 -0
  95. package/docs/developer/PROJECT_INDEX.md +365 -0
  96. package/docs/planning/API_COVERAGE.md +1045 -0
  97. package/docs/planning/BACKLOG.md +1159 -0
  98. package/docs/planning/PROFILE_SYSTEM_TASKS.md +287 -0
  99. package/docs/planning/UX_IMPLEMENTATION_PLAN.md +691 -0
  100. package/docs/planning/archive/RELEASE_CHECKLIST_v1.3.0.md +332 -0
  101. package/docs/planning/archive/RELEASE_v1.3.0.md +428 -0
  102. package/docs/planning/archive/cakemail-cli-ux-improvements.md +438 -0
  103. package/docs/planning/cakemail-profile-system-plan.md +1121 -0
  104. package/docs/testing/AI_USER_SIMULATION_DESIGN.md +1342 -0
  105. package/docs/testing/KENOGAMI_BIDIRECTIONAL_FLOW.md +1517 -0
  106. package/docs/testing/KENOGAMI_TRUTH_RECONCILIATION_SYSTEM.md +1369 -0
  107. package/docs/user-manual/.obsidian/app.json +1 -0
  108. package/docs/user-manual/.obsidian/appearance.json +1 -0
  109. package/docs/user-manual/.obsidian/core-plugins.json +33 -0
  110. package/docs/user-manual/.obsidian/workspace.json +167 -0
  111. package/docs/user-manual/01-getting-started/01-installation.md +214 -0
  112. package/docs/user-manual/01-getting-started/02-quick-start.md +432 -0
  113. package/docs/user-manual/01-getting-started/03-authentication.md +448 -0
  114. package/docs/user-manual/01-getting-started/04-configuration.md +430 -0
  115. package/docs/user-manual/01-getting-started/05-output-formats.md +447 -0
  116. package/docs/user-manual/02-core-concepts/01-accounts.md +514 -0
  117. package/docs/user-manual/02-core-concepts/02-profile-system.md +771 -0
  118. package/docs/user-manual/02-core-concepts/03-smart-defaults.md +485 -0
  119. package/docs/user-manual/02-core-concepts/04-authentication-methods.md +435 -0
  120. package/docs/user-manual/02-core-concepts/05-pagination-filtering.md +600 -0
  121. package/docs/user-manual/02-core-concepts/06-error-handling.md +718 -0
  122. package/docs/user-manual/02-core-concepts/07-api-coverage.md +483 -0
  123. package/docs/user-manual/03-email-operations/01-senders.md +490 -0
  124. package/docs/user-manual/03-email-operations/02-templates.md +444 -0
  125. package/docs/user-manual/03-email-operations/03-transactional-emails.md +706 -0
  126. package/docs/user-manual/03-email-operations/04-email-tracking.md +407 -0
  127. package/docs/user-manual/04-campaign-management/01-campaigns-basics.md +394 -0
  128. package/docs/user-manual/04-campaign-management/02-campaign-scheduling.md +630 -0
  129. package/docs/user-manual/04-campaign-management/03-campaign-testing.md +997 -0
  130. package/docs/user-manual/04-campaign-management/04-campaign-lifecycle.md +709 -0
  131. package/docs/user-manual/04-campaign-management/05-campaign-links.md +934 -0
  132. package/docs/user-manual/05-contact-management/01-lists.md +836 -0
  133. package/docs/user-manual/05-contact-management/02-contacts.md +1035 -0
  134. package/docs/user-manual/05-contact-management/03-custom-attributes.md +788 -0
  135. package/docs/user-manual/05-contact-management/04-segments.md +1028 -0
  136. package/docs/user-manual/05-contact-management/05-contact-import-export.md +1031 -0
  137. package/docs/user-manual/06-analytics-reporting/01-campaign-analytics.md +867 -0
  138. package/docs/user-manual/06-analytics-reporting/02-account-reports.md +227 -0
  139. package/docs/user-manual/07-integrations/01-webhooks-integration.md +259 -0
  140. package/docs/user-manual/07-integrations/02-automation.md +326 -0
  141. package/docs/user-manual/08-advanced-usage/01-scripting-patterns.md +672 -0
  142. package/docs/user-manual/08-advanced-usage/02-bulk-operations.md +932 -0
  143. package/docs/user-manual/08-advanced-usage/03-ci-cd-integration.md +892 -0
  144. package/docs/user-manual/08-advanced-usage/04-performance-optimization.md +766 -0
  145. package/docs/user-manual/09-command-reference/01-config.md +776 -0
  146. package/docs/user-manual/09-command-reference/02-account.md +652 -0
  147. package/docs/user-manual/09-command-reference/03-lists.md +958 -0
  148. package/docs/user-manual/09-command-reference/04-contacts.md +1408 -0
  149. package/docs/user-manual/09-command-reference/05-attributes.md +617 -0
  150. package/docs/user-manual/09-command-reference/06-segments.md +894 -0
  151. package/docs/user-manual/09-command-reference/07-senders.md +803 -0
  152. package/docs/user-manual/09-command-reference/08-templates.md +818 -0
  153. package/docs/user-manual/09-command-reference/09-campaigns.md +1250 -0
  154. package/docs/user-manual/09-command-reference/10-emails.md +807 -0
  155. package/docs/user-manual/09-command-reference/11-reports.md +1135 -0
  156. package/docs/user-manual/09-command-reference/12-webhooks.md +773 -0
  157. package/docs/user-manual/09-command-reference/13-suppressed.md +797 -0
  158. package/docs/user-manual/09-command-reference/14-interests.md +630 -0
  159. package/docs/user-manual/09-command-reference/15-tags.md +584 -0
  160. package/docs/user-manual/09-command-reference/16-logs.md +656 -0
  161. package/docs/user-manual/09-command-reference/17-transactional-templates.md +850 -0
  162. package/docs/user-manual/10-troubleshooting/01-common-errors.md +457 -0
  163. package/docs/user-manual/10-troubleshooting/02-authentication-issues.md +558 -0
  164. package/docs/user-manual/10-troubleshooting/03-connection-problems.md +634 -0
  165. package/docs/user-manual/10-troubleshooting/04-debugging.md +725 -0
  166. package/docs/user-manual/11-appendix/04-faq.md +484 -0
  167. package/docs/user-manual/11-appendix/05-glossary.md +250 -0
  168. package/docs/user-manual/README.md +0 -0
  169. package/package.json +13 -47
  170. package/src/cli.ts +125 -0
  171. package/src/client.ts +16 -0
  172. package/src/commands/account.ts +267 -0
  173. package/src/commands/accounts.ts +78 -0
  174. package/src/commands/actions.ts +249 -0
  175. package/src/commands/attributes.ts +139 -0
  176. package/src/commands/campaign-blueprints.ts +106 -0
  177. package/src/commands/campaigns.ts +469 -0
  178. package/src/commands/config.ts +77 -0
  179. package/src/commands/contacts.ts +612 -0
  180. package/src/commands/custom-attributes.ts +127 -0
  181. package/src/commands/dkims.ts +117 -0
  182. package/src/commands/domains.ts +82 -0
  183. package/src/commands/email-apis.ts +569 -0
  184. package/src/commands/emails.ts +197 -0
  185. package/src/commands/forms.ts +283 -0
  186. package/src/commands/interests.ts +155 -0
  187. package/src/commands/links.ts +38 -0
  188. package/src/commands/lists.ts +406 -0
  189. package/src/commands/logos.ts +71 -0
  190. package/src/commands/logs.ts +386 -0
  191. package/src/commands/reports.ts +306 -0
  192. package/src/commands/segments.ts +158 -0
  193. package/src/commands/senders.ts +204 -0
  194. package/src/commands/sub-accounts.ts +271 -0
  195. package/src/commands/suppressed-emails.ts +234 -0
  196. package/src/commands/suppressed.ts +198 -0
  197. package/src/commands/system-emails.ts +85 -0
  198. package/src/commands/tags.ts +146 -0
  199. package/src/commands/tasks.ts +116 -0
  200. package/src/commands/templates.ts +189 -0
  201. package/src/commands/tokens.ts +83 -0
  202. package/src/commands/transactional-emails.ts +374 -0
  203. package/src/commands/transactional-templates.ts +385 -0
  204. package/src/commands/users.ts +506 -0
  205. package/src/commands/webhooks.ts +172 -0
  206. package/src/commands/workflow-blueprints.ts +123 -0
  207. package/src/commands/workflows.ts +265 -0
  208. package/src/types/profile.ts +93 -0
  209. package/src/utils/auth.ts +272 -0
  210. package/src/utils/config-file.ts +96 -0
  211. package/src/utils/config.ts +134 -0
  212. package/src/utils/confirm.ts +32 -0
  213. package/src/utils/defaults.ts +99 -0
  214. package/src/utils/errors.ts +116 -0
  215. package/src/utils/interactive.ts +91 -0
  216. package/src/utils/list-defaults.ts +74 -0
  217. package/src/utils/output.ts +190 -0
  218. package/src/utils/progress.ts +320 -0
  219. package/src/utils/spinner.ts +22 -0
  220. package/tests/IMPLEMENTATION_STATUS.md +258 -0
  221. package/tests/PTY_SETUP.md +118 -0
  222. package/tests/PTY_TESTING_GUIDE.md +507 -0
  223. package/tests/README.md +244 -0
  224. package/tests/fixtures/api-responses/campaigns.json +34 -0
  225. package/tests/fixtures/test-config.json +13 -0
  226. package/tests/helpers/cli-runner.ts +128 -0
  227. package/tests/helpers/mock-server.ts +301 -0
  228. package/tests/helpers/pty-runner.ts +181 -0
  229. package/tests/integration/campaigns-real-api.test.ts +196 -0
  230. package/tests/integration/setup-integration.ts +50 -0
  231. package/tests/pty/campaigns.test.ts +241 -0
  232. package/tests/setup.ts +34 -0
  233. package/tsconfig.json +15 -0
  234. package/vitest.config.ts +28 -0
@@ -0,0 +1,867 @@
1
+ # Campaign Analytics
2
+
3
+ Analyze campaign performance with comprehensive metrics and insights to optimize your email marketing.
4
+
5
+ ## Overview
6
+
7
+ Campaign analytics allow you to:
8
+ - Track email delivery and engagement
9
+ - Measure open and click-through rates
10
+ - Identify top-performing content and links
11
+ - Compare campaign performance over time
12
+ - Export data for deeper analysis
13
+ - Make data-driven decisions
14
+
15
+ Understanding your campaign metrics is essential for improving email marketing effectiveness.
16
+
17
+ ## Quick Start
18
+
19
+ ### View Campaign Report
20
+
21
+ ```bash
22
+ $ cakemail reports campaign 790
23
+ ```
24
+
25
+ **Output:**
26
+ ```
27
+ Campaign: March Newsletter (ID: 790)
28
+ Status: sent
29
+ Sent: 2024-03-15 10:00:00
30
+
31
+ === Delivery Metrics ===
32
+ Total Recipients: 1,247
33
+ Delivered: 1,189 (95.3%)
34
+ Bounced: 58 (4.7%)
35
+ Hard Bounces: 45
36
+ Soft Bounces: 13
37
+
38
+ === Engagement Metrics ===
39
+ Opens: 723 (60.8%)
40
+ Unique Opens: 567 (47.7%)
41
+ Clicks: 234 (19.7%)
42
+ Unique Clicks: 189 (15.9%)
43
+ Unsubscribes: 12 (1.0%)
44
+
45
+ === Performance ===
46
+ Open Rate: 47.7%
47
+ Click Rate: 15.9%
48
+ Click-to-Open: 33.3%
49
+ Bounce Rate: 4.7%
50
+ Unsubscribe Rate: 1.0%
51
+ ```
52
+
53
+ ## Core Metrics
54
+
55
+ ### Delivery Metrics
56
+
57
+ **Total Recipients:**
58
+ - Number of contacts campaign was sent to
59
+ - Includes all contacts in target list/segment
60
+
61
+ **Delivered:**
62
+ - Successfully delivered emails
63
+ - Excludes hard and soft bounces
64
+ - Formula: Total Recipients - Bounces
65
+
66
+ **Bounced:**
67
+ - Emails that couldn't be delivered
68
+ - **Hard Bounce**: Permanent failure (invalid email)
69
+ - **Soft Bounce**: Temporary failure (inbox full, server down)
70
+
71
+ ```bash
72
+ # View detailed bounce information
73
+ $ cakemail reports campaign 790 -f json | jq '{
74
+ total: .total_recipients,
75
+ delivered: .delivered,
76
+ bounced: .bounced,
77
+ hard_bounces: .hard_bounces,
78
+ soft_bounces: .soft_bounces,
79
+ delivery_rate: (.delivered / .total_recipients * 100 | round)
80
+ }'
81
+ ```
82
+
83
+ **Output:**
84
+ ```json
85
+ {
86
+ "total": 1247,
87
+ "delivered": 1189,
88
+ "bounced": 58,
89
+ "hard_bounces": 45,
90
+ "soft_bounces": 13,
91
+ "delivery_rate": 95
92
+ }
93
+ ```
94
+
95
+ ### Engagement Metrics
96
+
97
+ **Opens:**
98
+ - Total times campaign was opened
99
+ - Includes multiple opens by same contact
100
+ - Tracked via invisible pixel
101
+
102
+ **Unique Opens:**
103
+ - Number of unique contacts who opened
104
+ - Each contact counted only once
105
+ - More accurate engagement indicator
106
+
107
+ **Open Rate:**
108
+ - Percentage of delivered emails that were opened
109
+ - Formula: (Unique Opens / Delivered) × 100
110
+ - Industry average: 15-25%
111
+
112
+ ```bash
113
+ # Calculate open rate
114
+ $ cakemail reports campaign 790 -f json | jq '{
115
+ delivered: .delivered,
116
+ unique_opens: .unique_opens,
117
+ open_rate: (.unique_opens / .delivered * 100 | round)
118
+ }'
119
+ ```
120
+
121
+ **Clicks:**
122
+ - Total link clicks in campaign
123
+ - Includes multiple clicks by same contact
124
+
125
+ **Unique Clicks:**
126
+ - Number of unique contacts who clicked
127
+ - Each contact counted only once
128
+
129
+ **Click Rate (CTR):**
130
+ - Percentage of delivered emails with clicks
131
+ - Formula: (Unique Clicks / Delivered) × 100
132
+ - Industry average: 2-5%
133
+
134
+ **Click-to-Open Rate (CTOR):**
135
+ - Percentage of openers who clicked
136
+ - Formula: (Unique Clicks / Unique Opens) × 100
137
+ - Measures content effectiveness
138
+ - Industry average: 10-20%
139
+
140
+ ```bash
141
+ # View click metrics
142
+ $ cakemail reports campaign 790 -f json | jq '{
143
+ unique_opens: .unique_opens,
144
+ unique_clicks: .unique_clicks,
145
+ click_rate: (.unique_clicks / .delivered * 100 | round),
146
+ ctor: (.unique_clicks / .unique_opens * 100 | round)
147
+ }'
148
+ ```
149
+
150
+ ### Action Metrics
151
+
152
+ **Unsubscribes:**
153
+ - Contacts who unsubscribed after this campaign
154
+ - Important metric for content relevance
155
+
156
+ **Unsubscribe Rate:**
157
+ - Percentage of delivered emails that unsubscribed
158
+ - Formula: (Unsubscribes / Delivered) × 100
159
+ - Healthy rate: < 0.5%
160
+ - Concerning rate: > 1%
161
+
162
+ **Spam Complaints:**
163
+ - Contacts who marked email as spam
164
+ - Critical metric for sender reputation
165
+ - Target: < 0.1%
166
+
167
+ ```bash
168
+ # View action metrics
169
+ $ cakemail reports campaign 790 -f json | jq '{
170
+ unsubscribes: .unsubscribes,
171
+ unsubscribe_rate: (.unsubscribes / .delivered * 100 | round),
172
+ spam_complaints: .spam_complaints,
173
+ spam_rate: (.spam_complaints / .delivered * 100 | round)
174
+ }'
175
+ ```
176
+
177
+ ## Viewing Campaign Analytics
178
+
179
+ ### Basic Campaign Report
180
+
181
+ ```bash
182
+ $ cakemail reports campaign 790
183
+ ```
184
+
185
+ ### JSON Format for Processing
186
+
187
+ ```bash
188
+ $ cakemail reports campaign 790 -f json > campaign-790.json
189
+ ```
190
+
191
+ ### Extract Specific Metrics
192
+
193
+ ```bash
194
+ # Open rate only
195
+ $ cakemail reports campaign 790 -f json | jq '.open_rate'
196
+
197
+ # Top metrics summary
198
+ $ cakemail reports campaign 790 -f json | jq '{
199
+ name: .campaign_name,
200
+ delivered: .delivered,
201
+ open_rate: .open_rate,
202
+ click_rate: .click_rate,
203
+ unsubscribe_rate: .unsubscribe_rate
204
+ }'
205
+ ```
206
+
207
+ ### Multiple Campaign Reports
208
+
209
+ ```bash
210
+ #!/bin/bash
211
+ # report-multiple-campaigns.sh
212
+
213
+ CAMPAIGNS=(790 791 792)
214
+
215
+ echo "Campaign | Delivered | Open Rate | Click Rate"
216
+ echo "---------|-----------|-----------|------------"
217
+
218
+ for ID in "${CAMPAIGNS[@]}"; do
219
+ REPORT=$(cakemail reports campaign $ID -f json)
220
+
221
+ NAME=$(echo "$REPORT" | jq -r '.campaign_name' | cut -c1-15)
222
+ DELIVERED=$(echo "$REPORT" | jq -r '.delivered')
223
+ OPEN=$(echo "$REPORT" | jq -r '.open_rate')
224
+ CLICK=$(echo "$REPORT" | jq -r '.click_rate')
225
+
226
+ printf "%-15s | %9d | %8.1f%% | %9.1f%%\n" "$NAME" $DELIVERED $OPEN $CLICK
227
+ done
228
+ ```
229
+
230
+ ## Link Analytics
231
+
232
+ ### View Campaign Links
233
+
234
+ ```bash
235
+ $ cakemail reports campaign-links 790
236
+ ```
237
+
238
+ **Output:**
239
+ ```
240
+ ┌────────────────────────────────────────┬────────┬─────────┬─────────┐
241
+ │ URL │ Clicks │ Unique │ CTR │
242
+ ├────────────────────────────────────────┼────────┼─────────┼─────────┤
243
+ │ https://example.com/product │ 450 │ 320 │ 26.9% │
244
+ │ https://example.com/blog/article │ 230 │ 180 │ 15.1% │
245
+ │ https://example.com/special-offer │ 180 │ 150 │ 12.6% │
246
+ └────────────────────────────────────────┴────────┴─────────┴─────────┘
247
+
248
+ Total clicks: 860 (650 unique)
249
+ Overall CTR: 54.7%
250
+ ```
251
+
252
+ ### Top Performing Links
253
+
254
+ ```bash
255
+ #!/bin/bash
256
+ # top-links.sh
257
+
258
+ CAMPAIGN_ID=$1
259
+
260
+ cakemail reports campaign-links $CAMPAIGN_ID -f json | \
261
+ jq -r '.links | sort_by(-.unique_clicks) | .[0:5][] |
262
+ "\(.unique_clicks)\t\(.url)"' | \
263
+ column -t -s $'\t'
264
+ ```
265
+
266
+ ### Export Link Data
267
+
268
+ ```bash
269
+ $ cakemail reports campaign-links 790 -f json > links-790.json
270
+ ```
271
+
272
+ ## Time-Based Analytics
273
+
274
+ ### Opens Over Time
275
+
276
+ ```bash
277
+ #!/bin/bash
278
+ # opens-over-time.sh
279
+
280
+ CAMPAIGN_ID=$1
281
+
282
+ echo "=== Opens Timeline ==="
283
+ echo ""
284
+
285
+ # Get campaign details
286
+ CAMPAIGN=$(cakemail campaigns get $CAMPAIGN_ID -f json)
287
+ SENT_DATE=$(echo "$CAMPAIGN" | jq -r '.delivered_at')
288
+
289
+ echo "Campaign sent: $SENT_DATE"
290
+ echo ""
291
+
292
+ # Get current stats
293
+ STATS=$(cakemail reports campaign $CAMPAIGN_ID -f json)
294
+
295
+ echo "Current stats:"
296
+ echo " Unique Opens: $(echo "$STATS" | jq -r '.unique_opens')"
297
+ echo " Open Rate: $(echo "$STATS" | jq -r '.open_rate')%"
298
+ echo ""
299
+
300
+ echo "Typical open timeline:"
301
+ echo " First 24 hours: 50-70% of total opens"
302
+ echo " First 48 hours: 70-85% of total opens"
303
+ echo " First 7 days: 90-95% of total opens"
304
+ ```
305
+
306
+ ### Daily Performance Tracking
307
+
308
+ ```bash
309
+ #!/bin/bash
310
+ # track-daily-performance.sh
311
+
312
+ CAMPAIGN_ID=$1
313
+ LOG_FILE="campaign-${CAMPAIGN_ID}-tracking.log"
314
+
315
+ # Log current metrics
316
+ DATE=$(date +%Y-%m-%d-%H:%M:%S)
317
+ STATS=$(cakemail reports campaign $CAMPAIGN_ID -f json)
318
+
319
+ OPENS=$(echo "$STATS" | jq -r '.unique_opens')
320
+ CLICKS=$(echo "$STATS" | jq -r '.unique_clicks')
321
+ OPEN_RATE=$(echo "$STATS" | jq -r '.open_rate')
322
+ CLICK_RATE=$(echo "$STATS" | jq -r '.click_rate')
323
+
324
+ echo "$DATE,$OPENS,$CLICKS,$OPEN_RATE,$CLICK_RATE" >> $LOG_FILE
325
+
326
+ echo "Logged: $OPENS opens, $CLICKS clicks"
327
+ ```
328
+
329
+ **Schedule with cron:**
330
+ ```bash
331
+ # Track every 6 hours for first week
332
+ 0 */6 * * * /path/to/track-daily-performance.sh 790
333
+ ```
334
+
335
+ ## Comparative Analysis
336
+
337
+ ### Compare Two Campaigns
338
+
339
+ ```bash
340
+ #!/bin/bash
341
+ # compare-campaigns.sh
342
+
343
+ CAMPAIGN_A=$1
344
+ CAMPAIGN_B=$2
345
+
346
+ echo "=== Campaign Comparison ==="
347
+ echo ""
348
+
349
+ # Get reports
350
+ REPORT_A=$(cakemail reports campaign $CAMPAIGN_A -f json)
351
+ REPORT_B=$(cakemail reports campaign $CAMPAIGN_B -f json)
352
+
353
+ # Campaign names
354
+ NAME_A=$(echo "$REPORT_A" | jq -r '.campaign_name')
355
+ NAME_B=$(echo "$REPORT_B" | jq -r '.campaign_name')
356
+
357
+ echo "Campaign A: $NAME_A (ID: $CAMPAIGN_A)"
358
+ echo "Campaign B: $NAME_B (ID: $CAMPAIGN_B)"
359
+ echo ""
360
+
361
+ # Compare metrics
362
+ echo "Metric | Campaign A | Campaign B | Difference"
363
+ echo "--------------------|------------|------------|------------"
364
+
365
+ # Open Rate
366
+ OPEN_A=$(echo "$REPORT_A" | jq -r '.open_rate')
367
+ OPEN_B=$(echo "$REPORT_B" | jq -r '.open_rate')
368
+ OPEN_DIFF=$(echo "$OPEN_B - $OPEN_A" | bc)
369
+ printf "Open Rate | %9.1f%% | %9.1f%% | %+9.1f%%\n" $OPEN_A $OPEN_B $OPEN_DIFF
370
+
371
+ # Click Rate
372
+ CLICK_A=$(echo "$REPORT_A" | jq -r '.click_rate')
373
+ CLICK_B=$(echo "$REPORT_B" | jq -r '.click_rate')
374
+ CLICK_DIFF=$(echo "$CLICK_B - $CLICK_A" | bc)
375
+ printf "Click Rate | %9.1f%% | %9.1f%% | %+9.1f%%\n" $CLICK_A $CLICK_B $CLICK_DIFF
376
+
377
+ # Unsubscribe Rate
378
+ UNSUB_A=$(echo "$REPORT_A" | jq -r '.unsubscribe_rate')
379
+ UNSUB_B=$(echo "$REPORT_B" | jq -r '.unsubscribe_rate')
380
+ UNSUB_DIFF=$(echo "$UNSUB_B - $UNSUB_A" | bc)
381
+ printf "Unsubscribe Rate | %9.1f%% | %9.1f%% | %+9.1f%%\n" $UNSUB_A $UNSUB_B $UNSUB_DIFF
382
+
383
+ echo ""
384
+
385
+ # Winner
386
+ if (( $(echo "$OPEN_B > $OPEN_A" | bc -l) )); then
387
+ echo "🏆 Campaign B performed better"
388
+ else
389
+ echo "🏆 Campaign A performed better"
390
+ fi
391
+ ```
392
+
393
+ ### Historical Performance
394
+
395
+ ```bash
396
+ #!/bin/bash
397
+ # historical-performance.sh
398
+
399
+ LIST_ID=123
400
+
401
+ echo "=== Historical Campaign Performance ==="
402
+ echo ""
403
+
404
+ # Get all sent campaigns
405
+ CAMPAIGNS=$(cakemail campaigns list \
406
+ --filter "status==sent" \
407
+ --sort "-delivered_at" \
408
+ --limit 10 \
409
+ -f json | jq -r '.data[].id')
410
+
411
+ echo "Campaign | Date | Recipients | Open Rate | Click Rate"
412
+ echo "---------|------------|------------|-----------|------------"
413
+
414
+ for ID in $CAMPAIGNS; do
415
+ CAMPAIGN=$(cakemail campaigns get $ID -f json)
416
+ REPORT=$(cakemail reports campaign $ID -f json)
417
+
418
+ NAME=$(echo "$CAMPAIGN" | jq -r '.name' | cut -c1-15)
419
+ DATE=$(echo "$CAMPAIGN" | jq -r '.delivered_at' | cut -d'T' -f1)
420
+ RECIPIENTS=$(echo "$REPORT" | jq -r '.total_recipients')
421
+ OPEN=$(echo "$REPORT" | jq -r '.open_rate')
422
+ CLICK=$(echo "$REPORT" | jq -r '.click_rate')
423
+
424
+ printf "%-15s | %10s | %10d | %8.1f%% | %9.1f%%\n" \
425
+ "$NAME" "$DATE" $RECIPIENTS $OPEN $CLICK
426
+ done
427
+
428
+ echo ""
429
+
430
+ # Calculate averages
431
+ echo "Calculating averages across all campaigns..."
432
+ ```
433
+
434
+ ### Month-over-Month Trends
435
+
436
+ ```bash
437
+ #!/bin/bash
438
+ # monthly-trends.sh
439
+
440
+ YEAR=2024
441
+
442
+ echo "=== Monthly Campaign Trends - $YEAR ==="
443
+ echo ""
444
+ echo "Month | Campaigns | Avg Open Rate | Avg Click Rate"
445
+ echo "------|-----------|---------------|----------------"
446
+
447
+ for MONTH in {01..12}; do
448
+ START_DATE="$YEAR-$MONTH-01"
449
+
450
+ # Calculate end date
451
+ if [ $MONTH -eq 12 ]; then
452
+ END_DATE="$((YEAR + 1))-01-01"
453
+ else
454
+ END_DATE="$YEAR-$(printf "%02d" $((10#$MONTH + 1)))-01"
455
+ fi
456
+
457
+ # Get campaigns for month
458
+ CAMPAIGNS=$(cakemail campaigns list \
459
+ --filter "status==sent;delivered_at>=$START_DATE;delivered_at<$END_DATE" \
460
+ -f json | jq -r '.data[].id')
461
+
462
+ if [ -z "$CAMPAIGNS" ]; then
463
+ continue
464
+ fi
465
+
466
+ COUNT=$(echo "$CAMPAIGNS" | wc -l)
467
+ TOTAL_OPEN=0
468
+ TOTAL_CLICK=0
469
+
470
+ for ID in $CAMPAIGNS; do
471
+ REPORT=$(cakemail reports campaign $ID -f json 2>/dev/null)
472
+ if [ -n "$REPORT" ]; then
473
+ OPEN=$(echo "$REPORT" | jq -r '.open_rate')
474
+ CLICK=$(echo "$REPORT" | jq -r '.click_rate')
475
+ TOTAL_OPEN=$(echo "$TOTAL_OPEN + $OPEN" | bc)
476
+ TOTAL_CLICK=$(echo "$TOTAL_CLICK + $CLICK" | bc)
477
+ fi
478
+ done
479
+
480
+ if [ $COUNT -gt 0 ]; then
481
+ AVG_OPEN=$(echo "scale=1; $TOTAL_OPEN / $COUNT" | bc)
482
+ AVG_CLICK=$(echo "scale=1; $TOTAL_CLICK / $COUNT" | bc)
483
+ printf "%5s | %9d | %12.1f%% | %13.1f%%\n" \
484
+ "$MONTH" $COUNT $AVG_OPEN $AVG_CLICK
485
+ fi
486
+ done
487
+ ```
488
+
489
+ ## Segmentation Analysis
490
+
491
+ ### Performance by Segment
492
+
493
+ ```bash
494
+ #!/bin/bash
495
+ # segment-performance.sh
496
+
497
+ LIST_ID=123
498
+
499
+ echo "=== Campaign Performance by Segment ==="
500
+ echo ""
501
+
502
+ # Get all segments
503
+ SEGMENTS=$(cakemail segments list $LIST_ID -f json | jq -r '.data[] | "\(.id):\(.name)"')
504
+
505
+ echo "Segment | Campaigns | Avg Open | Avg Click"
506
+ echo "-----------------|-----------|----------|----------"
507
+
508
+ for SEG in $SEGMENTS; do
509
+ SEG_ID=$(echo "$SEG" | cut -d: -f1)
510
+ SEG_NAME=$(echo "$SEG" | cut -d: -f2- | cut -c1-15)
511
+
512
+ # Find campaigns sent to this segment
513
+ CAMPAIGNS=$(cakemail campaigns list \
514
+ --filter "status==sent;segment_id==$SEG_ID" \
515
+ -f json | jq -r '.data[].id')
516
+
517
+ if [ -z "$CAMPAIGNS" ]; then
518
+ continue
519
+ fi
520
+
521
+ COUNT=$(echo "$CAMPAIGNS" | wc -l)
522
+ TOTAL_OPEN=0
523
+ TOTAL_CLICK=0
524
+
525
+ for ID in $CAMPAIGNS; do
526
+ REPORT=$(cakemail reports campaign $ID -f json 2>/dev/null)
527
+ if [ -n "$REPORT" ]; then
528
+ OPEN=$(echo "$REPORT" | jq -r '.open_rate')
529
+ CLICK=$(echo "$REPORT" | jq -r '.click_rate')
530
+ TOTAL_OPEN=$(echo "$TOTAL_OPEN + $OPEN" | bc)
531
+ TOTAL_CLICK=$(echo "$TOTAL_CLICK + $CLICK" | bc)
532
+ fi
533
+ done
534
+
535
+ AVG_OPEN=$(echo "scale=1; $TOTAL_OPEN / $COUNT" | bc)
536
+ AVG_CLICK=$(echo "scale=1; $TOTAL_CLICK / $COUNT" | bc)
537
+
538
+ printf "%-15s | %9d | %7.1f%% | %8.1f%%\n" \
539
+ "$SEG_NAME" $COUNT $AVG_OPEN $AVG_CLICK
540
+ done
541
+ ```
542
+
543
+ ### Best Performing Segment
544
+
545
+ ```bash
546
+ #!/bin/bash
547
+ # best-segment.sh
548
+
549
+ # Find segment with highest average open rate
550
+ # (Implementation from segment-performance.sh with sorting)
551
+
552
+ # Output best segment recommendation
553
+ echo "🏆 Best Performing Segment: Premium Users"
554
+ echo " Average Open Rate: 62.3%"
555
+ echo " Average Click Rate: 28.7%"
556
+ echo ""
557
+ echo "💡 Recommendation: Prioritize campaigns to this segment"
558
+ ```
559
+
560
+ ## Key Performance Indicators (KPIs)
561
+
562
+ ### Email Deliverability KPIs
563
+
564
+ ```bash
565
+ #!/bin/bash
566
+ # deliverability-kpis.sh
567
+
568
+ CAMPAIGN_ID=$1
569
+
570
+ REPORT=$(cakemail reports campaign $CAMPAIGN_ID -f json)
571
+
572
+ echo "=== Deliverability KPIs ==="
573
+ echo ""
574
+
575
+ # Delivery Rate
576
+ TOTAL=$(echo "$REPORT" | jq -r '.total_recipients')
577
+ DELIVERED=$(echo "$REPORT" | jq -r '.delivered')
578
+ DELIVERY_RATE=$(echo "scale=1; $DELIVERED * 100 / $TOTAL" | bc)
579
+
580
+ echo "Delivery Rate: $DELIVERY_RATE%"
581
+ if (( $(echo "$DELIVERY_RATE >= 95" | bc -l) )); then
582
+ echo " ✅ Excellent (target: ≥95%)"
583
+ elif (( $(echo "$DELIVERY_RATE >= 90" | bc -l) )); then
584
+ echo " ⚠️ Good (target: ≥95%)"
585
+ else
586
+ echo " ❌ Poor - Clean your list"
587
+ fi
588
+ echo ""
589
+
590
+ # Bounce Rate
591
+ BOUNCED=$(echo "$REPORT" | jq -r '.bounced')
592
+ BOUNCE_RATE=$(echo "scale=1; $BOUNCED * 100 / $TOTAL" | bc)
593
+
594
+ echo "Bounce Rate: $BOUNCE_RATE%"
595
+ if (( $(echo "$BOUNCE_RATE <= 2" | bc -l) )); then
596
+ echo " ✅ Excellent (target: ≤2%)"
597
+ elif (( $(echo "$BOUNCE_RATE <= 5" | bc -l) )); then
598
+ echo " ⚠️ Acceptable (target: ≤2%)"
599
+ else
600
+ echo " ❌ High - Review list quality"
601
+ fi
602
+ ```
603
+
604
+ ### Engagement KPIs
605
+
606
+ ```bash
607
+ #!/bin/bash
608
+ # engagement-kpis.sh
609
+
610
+ CAMPAIGN_ID=$1
611
+
612
+ REPORT=$(cakemail reports campaign $CAMPAIGN_ID -f json)
613
+
614
+ echo "=== Engagement KPIs ==="
615
+ echo ""
616
+
617
+ # Open Rate
618
+ OPEN_RATE=$(echo "$REPORT" | jq -r '.open_rate')
619
+ echo "Open Rate: $OPEN_RATE%"
620
+ if (( $(echo "$OPEN_RATE >= 20" | bc -l) )); then
621
+ echo " ✅ Excellent (industry avg: 15-25%)"
622
+ elif (( $(echo "$OPEN_RATE >= 15" | bc -l) )); then
623
+ echo " ✓ Good"
624
+ else
625
+ echo " ⚠️ Below average - Improve subject lines"
626
+ fi
627
+ echo ""
628
+
629
+ # Click Rate
630
+ CLICK_RATE=$(echo "$REPORT" | jq -r '.click_rate')
631
+ echo "Click Rate: $CLICK_RATE%"
632
+ if (( $(echo "$CLICK_RATE >= 3" | bc -l) )); then
633
+ echo " ✅ Excellent (industry avg: 2-5%)"
634
+ elif (( $(echo "$CLICK_RATE >= 2" | bc -l) )); then
635
+ echo " ✓ Good"
636
+ else
637
+ echo " ⚠️ Below average - Improve CTAs"
638
+ fi
639
+ echo ""
640
+
641
+ # CTOR
642
+ UNIQUE_OPENS=$(echo "$REPORT" | jq -r '.unique_opens')
643
+ UNIQUE_CLICKS=$(echo "$REPORT" | jq -r '.unique_clicks')
644
+ CTOR=$(echo "scale=1; $UNIQUE_CLICKS * 100 / $UNIQUE_OPENS" | bc)
645
+
646
+ echo "Click-to-Open Rate: $CTOR%"
647
+ if (( $(echo "$CTOR >= 15" | bc -l) )); then
648
+ echo " ✅ Excellent (industry avg: 10-20%)"
649
+ elif (( $(echo "$CTOR >= 10" | bc -l) )); then
650
+ echo " ✓ Good"
651
+ else
652
+ echo " ⚠️ Below average - Improve content relevance"
653
+ fi
654
+ ```
655
+
656
+ ### List Health KPIs
657
+
658
+ ```bash
659
+ #!/bin/bash
660
+ # list-health-kpis.sh
661
+
662
+ CAMPAIGN_ID=$1
663
+
664
+ REPORT=$(cakemail reports campaign $CAMPAIGN_ID -f json)
665
+
666
+ echo "=== List Health KPIs ==="
667
+ echo ""
668
+
669
+ # Unsubscribe Rate
670
+ DELIVERED=$(echo "$REPORT" | jq -r '.delivered')
671
+ UNSUBSCRIBES=$(echo "$REPORT" | jq -r '.unsubscribes')
672
+ UNSUB_RATE=$(echo "scale=2; $UNSUBSCRIBES * 100 / $DELIVERED" | bc)
673
+
674
+ echo "Unsubscribe Rate: $UNSUB_RATE%"
675
+ if (( $(echo "$UNSUB_RATE <= 0.2" | bc -l) )); then
676
+ echo " ✅ Excellent (target: ≤0.5%)"
677
+ elif (( $(echo "$UNSUB_RATE <= 0.5" | bc -l) )); then
678
+ echo " ✓ Good"
679
+ elif (( $(echo "$UNSUB_RATE <= 1.0" | bc -l) )); then
680
+ echo " ⚠️ Elevated - Review content relevance"
681
+ else
682
+ echo " ❌ High - Major content/targeting issues"
683
+ fi
684
+ echo ""
685
+
686
+ # Spam Complaint Rate
687
+ SPAM=$(echo "$REPORT" | jq -r '.spam_complaints // 0')
688
+ SPAM_RATE=$(echo "scale=3; $SPAM * 100 / $DELIVERED" | bc)
689
+
690
+ echo "Spam Complaint Rate: $SPAM_RATE%"
691
+ if (( $(echo "$SPAM_RATE <= 0.1" | bc -l) )); then
692
+ echo " ✅ Excellent (target: ≤0.1%)"
693
+ else
694
+ echo " ❌ Concerning - Review content and list source"
695
+ fi
696
+ ```
697
+
698
+ ## Export and Reporting
699
+
700
+ ### Export Campaign Data
701
+
702
+ ```bash
703
+ #!/bin/bash
704
+ # export-campaign-data.sh
705
+
706
+ CAMPAIGN_ID=$1
707
+ OUTPUT_FILE="campaign-${CAMPAIGN_ID}-report-$(date +%Y%m%d).json"
708
+
709
+ echo "Exporting campaign data..."
710
+
711
+ # Get campaign details
712
+ cakemail campaigns get $CAMPAIGN_ID -f json > campaign-details.json
713
+
714
+ # Get campaign report
715
+ cakemail reports campaign $CAMPAIGN_ID -f json > campaign-report.json
716
+
717
+ # Get link data
718
+ cakemail reports campaign-links $CAMPAIGN_ID -f json > campaign-links.json
719
+
720
+ # Combine into single file
721
+ jq -s '{campaign: .[0], report: .[1], links: .[2]}' \
722
+ campaign-details.json \
723
+ campaign-report.json \
724
+ campaign-links.json > "$OUTPUT_FILE"
725
+
726
+ # Cleanup
727
+ rm campaign-details.json campaign-report.json campaign-links.json
728
+
729
+ echo "✓ Exported to: $OUTPUT_FILE"
730
+ ```
731
+
732
+ ### Generate CSV Report
733
+
734
+ ```bash
735
+ #!/bin/bash
736
+ # generate-csv-report.sh
737
+
738
+ echo "campaign_id,name,date,recipients,delivered,opens,clicks,open_rate,click_rate,unsubscribes" > campaigns-report.csv
739
+
740
+ # Get all sent campaigns
741
+ CAMPAIGNS=$(cakemail campaigns list --filter "status==sent" -f json | jq -r '.data[].id')
742
+
743
+ for ID in $CAMPAIGNS; do
744
+ CAMPAIGN=$(cakemail campaigns get $ID -f json)
745
+ REPORT=$(cakemail reports campaign $ID -f json)
746
+
747
+ NAME=$(echo "$CAMPAIGN" | jq -r '.name' | sed 's/,/_/g')
748
+ DATE=$(echo "$CAMPAIGN" | jq -r '.delivered_at' | cut -d'T' -f1)
749
+ RECIPIENTS=$(echo "$REPORT" | jq -r '.total_recipients')
750
+ DELIVERED=$(echo "$REPORT" | jq -r '.delivered')
751
+ OPENS=$(echo "$REPORT" | jq -r '.unique_opens')
752
+ CLICKS=$(echo "$REPORT" | jq -r '.unique_clicks')
753
+ OPEN_RATE=$(echo "$REPORT" | jq -r '.open_rate')
754
+ CLICK_RATE=$(echo "$REPORT" | jq -r '.click_rate')
755
+ UNSUBSCRIBES=$(echo "$REPORT" | jq -r '.unsubscribes')
756
+
757
+ echo "$ID,$NAME,$DATE,$RECIPIENTS,$DELIVERED,$OPENS,$CLICKS,$OPEN_RATE,$CLICK_RATE,$UNSUBSCRIBES" >> campaigns-report.csv
758
+ done
759
+
760
+ echo "✓ Report saved to: campaigns-report.csv"
761
+ ```
762
+
763
+ ## Best Practices
764
+
765
+ ### 1. Review Reports Within 24-48 Hours
766
+
767
+ Most engagement happens in first 48 hours:
768
+ ```bash
769
+ # Schedule report review
770
+ $ cakemail reports campaign 790 > report-24h.txt
771
+ # Review after 24 hours
772
+ # Final review after 7 days
773
+ ```
774
+
775
+ ### 2. Track Metrics Over Time
776
+
777
+ ```bash
778
+ # Log metrics daily for first week
779
+ 0 12 * * * /path/to/track-daily-performance.sh 790
780
+ ```
781
+
782
+ ### 3. Compare Similar Campaigns
783
+
784
+ ```bash
785
+ # Compare campaigns to same segment
786
+ $ ./compare-campaigns.sh 790 791
787
+ ```
788
+
789
+ ### 4. Focus on Trends, Not Single Campaigns
790
+
791
+ ```bash
792
+ # Monthly trend analysis
793
+ $ ./monthly-trends.sh 2024
794
+ ```
795
+
796
+ ### 5. Set Benchmark Goals
797
+
798
+ ```bash
799
+ # Document your benchmarks
800
+ cat > benchmarks.md << 'EOF'
801
+ # Campaign Benchmarks
802
+
803
+ ## Newsletter
804
+ - Open Rate Target: 25%
805
+ - Click Rate Target: 4%
806
+ - Unsubscribe Rate Max: 0.3%
807
+
808
+ ## Promotional
809
+ - Open Rate Target: 20%
810
+ - Click Rate Target: 8%
811
+ - Unsubscribe Rate Max: 0.5%
812
+ EOF
813
+ ```
814
+
815
+ ### 6. Investigate Anomalies
816
+
817
+ ```bash
818
+ # If metrics significantly differ
819
+ # Check: Send time, subject line, content, segment, list quality
820
+ ```
821
+
822
+ ## Troubleshooting
823
+
824
+ ### Low Open Rates
825
+
826
+ **Problem:** Open rate below 15%
827
+
828
+ **Solutions:**
829
+ - Improve subject lines (A/B test)
830
+ - Check send time
831
+ - Verify sender reputation
832
+ - Clean inactive subscribers
833
+ - Test mobile optimization
834
+
835
+ ### Low Click Rates
836
+
837
+ **Problem:** Click rate below 2%
838
+
839
+ **Solutions:**
840
+ - Improve CTA placement
841
+ - Make CTAs more prominent
842
+ - Verify link destinations work
843
+ - Increase content relevance
844
+ - Test different content formats
845
+
846
+ ### High Unsubscribe Rate
847
+
848
+ **Problem:** Unsubscribe rate above 1%
849
+
850
+ **Solutions:**
851
+ - Review content relevance
852
+ - Reduce send frequency
853
+ - Improve targeting/segmentation
854
+ - Provide preference center
855
+ - Check for list purchase/rental
856
+
857
+ ### High Bounce Rate
858
+
859
+ **Problem:** Bounce rate above 5%
860
+
861
+ **Solutions:**
862
+ - Clean list regularly
863
+ - Remove hard bounces immediately
864
+ - Use double opt-in
865
+ - Verify email collection process
866
+ - Check for spam traps
867
+