emailr-cli 1.5.4 → 1.6.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.
- package/dist/index.js +2643 -112
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {Command}from'commander';import {Emailr}from'emailr';import
|
|
2
|
+
import {Command}from'commander';import {Emailr}from'emailr';import v,{readFileSync}from'fs';import W from'os';import N from'path';import re from'cli-table3';import S from'chalk';import Je from'http';import {URL}from'url';import ct from'crypto';import {spawn,execSync,exec}from'child_process';var Re=[N.join(W.homedir(),".emailrrc"),N.join(W.homedir(),".config","emailr","config.json")];function m(){if(process.env.EMAILR_API_KEY)return {apiKey:process.env.EMAILR_API_KEY,baseUrl:process.env.EMAILR_BASE_URL,format:process.env.EMAILR_FORMAT||"table"};for(let i of Re)if(v.existsSync(i))try{let t=v.readFileSync(i,"utf-8"),e=JSON.parse(t);if(e.apiKey)return {apiKey:e.apiKey,baseUrl:e.baseUrl,format:e.format||"table"}}catch{}throw new Error(`No API key configured.
|
|
3
3
|
|
|
4
4
|
Set the EMAILR_API_KEY environment variable:
|
|
5
5
|
export EMAILR_API_KEY=your-api-key
|
|
@@ -7,9 +7,419 @@ Set the EMAILR_API_KEY environment variable:
|
|
|
7
7
|
Or create a config file at ~/.emailrrc:
|
|
8
8
|
{ "apiKey": "your-api-key" }
|
|
9
9
|
|
|
10
|
-
Or run: emailr config set api-key <your-api-key>`)}function
|
|
11
|
-
`);}function
|
|
12
|
-
|
|
10
|
+
Or run: emailr config set api-key <your-api-key>`)}function I(){let i=N.join(W.homedir(),".config","emailr");return N.join(i,"config.json")}function G(i){let t=I(),e=N.dirname(t);v.existsSync(e)||v.mkdirSync(e,{recursive:true});let a={};if(v.existsSync(t))try{a=JSON.parse(v.readFileSync(t,"utf-8"));}catch{}let r={...a,...i};v.writeFileSync(t,JSON.stringify(r,null,2)+`
|
|
11
|
+
`);}function ne(i){try{return m()[i]?.toString()}catch{return}}function c(i,t="table"){t==="json"?console.log(JSON.stringify(i,null,2)):Me(i);}function Me(i){Array.isArray(i)?Le(i):typeof i=="object"&&i!==null?De(i):console.log(i);}function Le(i){if(i.length===0){console.log(S.gray("No results"));return}let t=i[0];if(typeof t!="object"||t===null){i.forEach(r=>console.log(r));return}let e=Object.keys(t),a=new re({head:e.map(r=>S.cyan(r)),style:{head:[],border:[]}});for(let r of i){let o=e.map(n=>{let s=r[n];return se(s)});a.push(o);}console.log(a.toString());}function De(i){let t=new re({style:{head:[],border:[]}});for(let[e,a]of Object.entries(i))t.push([S.cyan(e),se(a)]);console.log(t.toString());}function se(i){return i==null?S.gray("-"):typeof i=="boolean"?i?S.green("\u2713"):S.red("\u2717"):typeof i=="object"?JSON.stringify(i):String(i)}function d(i){console.log(S.green("\u2713"),i);}function l(i){console.error(S.red("\u2717"),i);}function k(i){console.warn(S.yellow("\u26A0"),i);}function u(i){console.log(S.blue("\u2139"),i);}function ce(){return new Command("send").description(`Send an email
|
|
12
|
+
|
|
13
|
+
USAGE
|
|
14
|
+
emailr send --to <email_address> [options]
|
|
15
|
+
|
|
16
|
+
DESCRIPTION
|
|
17
|
+
Send a transactional email to one or more recipients. You can provide
|
|
18
|
+
email content directly via options, load content from files, or use
|
|
19
|
+
a pre-defined template with variable substitution.
|
|
20
|
+
|
|
21
|
+
OPTIONS
|
|
22
|
+
--to <email_address> Recipient email address (required)
|
|
23
|
+
Use comma-separated values for multiple recipients
|
|
24
|
+
--from <email_address> Sender email address (uses default if not specified)
|
|
25
|
+
--subject <subject_line> Email subject line
|
|
26
|
+
|
|
27
|
+
Content Options (choose one approach):
|
|
28
|
+
--html <html_content> Inline HTML content for the email body
|
|
29
|
+
--text <text_content> Inline plain text content for the email body
|
|
30
|
+
--html-file <file_path> Read HTML content from a file
|
|
31
|
+
--text-file <file_path> Read plain text content from a file
|
|
32
|
+
--template <template_id> Use a pre-defined template by ID
|
|
33
|
+
--template-data <json> JSON object with template variable values
|
|
34
|
+
|
|
35
|
+
Additional Recipients:
|
|
36
|
+
--cc <email_addresses> CC recipients (comma-separated)
|
|
37
|
+
--bcc <email_addresses> BCC recipients (comma-separated)
|
|
38
|
+
--reply-to <email_address> Reply-to email address
|
|
39
|
+
|
|
40
|
+
Scheduling:
|
|
41
|
+
--schedule <datetime> Schedule send time in ISO 8601 format
|
|
42
|
+
Example: 2024-12-25T09:00:00Z
|
|
43
|
+
|
|
44
|
+
Output:
|
|
45
|
+
--format <format> Output format: json | table (default: table)
|
|
46
|
+
|
|
47
|
+
CONTENT OPTIONS EXPLAINED
|
|
48
|
+
There are three ways to provide email content:
|
|
49
|
+
|
|
50
|
+
1. Inline Content (--html / --text):
|
|
51
|
+
Provide content directly on the command line. Best for short content
|
|
52
|
+
or when content is generated by a script.
|
|
53
|
+
|
|
54
|
+
--html "<h1>Hello</h1><p>Welcome!</p>"
|
|
55
|
+
--text "Hello, Welcome to our service!"
|
|
56
|
+
|
|
57
|
+
2. File Content (--html-file / --text-file):
|
|
58
|
+
Read content from local files. Best for longer emails or when you
|
|
59
|
+
want to edit content in your preferred editor.
|
|
60
|
+
|
|
61
|
+
--html-file ./email.html
|
|
62
|
+
--text-file ./email.txt
|
|
63
|
+
|
|
64
|
+
3. Template (--template / --template-data):
|
|
65
|
+
Use a pre-defined template with variable substitution. Best for
|
|
66
|
+
consistent branding and reusable email designs.
|
|
67
|
+
|
|
68
|
+
--template tmpl_abc123 --template-data '{"name": "John"}'
|
|
69
|
+
|
|
70
|
+
You can combine --html/--html-file with --text/--text-file to provide
|
|
71
|
+
both HTML and plain text versions. The plain text version is used as
|
|
72
|
+
a fallback for email clients that don't support HTML.
|
|
73
|
+
|
|
74
|
+
TEMPLATE DATA FORMAT
|
|
75
|
+
The --template-data option accepts a JSON object where keys match
|
|
76
|
+
the variable names defined in your template.
|
|
77
|
+
|
|
78
|
+
Template with variables {{name}} and {{order_id}}:
|
|
79
|
+
--template-data '{"name": "John Doe", "order_id": "ORD-12345"}'
|
|
80
|
+
|
|
81
|
+
Complex data with nested objects:
|
|
82
|
+
--template-data '{"user": {"name": "John", "email": "john@example.com"}, "items": ["Item 1", "Item 2"]}'
|
|
83
|
+
|
|
84
|
+
Using a JSON file (via shell):
|
|
85
|
+
--template-data "$(cat data.json)"
|
|
86
|
+
|
|
87
|
+
OUTPUT FORMATS
|
|
88
|
+
--format json Machine-readable JSON with message_id, recipients, status
|
|
89
|
+
--format table Human-readable table output (default)
|
|
90
|
+
|
|
91
|
+
EXAMPLES
|
|
92
|
+
# Simple email with inline HTML
|
|
93
|
+
emailr send --to "user@example.com" \\
|
|
94
|
+
--subject "Welcome!" \\
|
|
95
|
+
--html "<h1>Welcome</h1><p>Thanks for signing up!</p>"
|
|
96
|
+
|
|
97
|
+
# Email with plain text fallback
|
|
98
|
+
emailr send --to "user@example.com" \\
|
|
99
|
+
--subject "Hello" \\
|
|
100
|
+
--html "<h1>Hello</h1>" \\
|
|
101
|
+
--text "Hello (plain text version)"
|
|
102
|
+
|
|
103
|
+
# Email from HTML file
|
|
104
|
+
emailr send --to "user@example.com" \\
|
|
105
|
+
--subject "Newsletter" \\
|
|
106
|
+
--html-file ./newsletter.html \\
|
|
107
|
+
--text-file ./newsletter.txt
|
|
108
|
+
|
|
109
|
+
# Multiple recipients
|
|
110
|
+
emailr send --to "user1@example.com,user2@example.com,user3@example.com" \\
|
|
111
|
+
--subject "Team Update" \\
|
|
112
|
+
--html-file ./update.html
|
|
113
|
+
|
|
114
|
+
# Using a template with variables
|
|
115
|
+
emailr send --to "customer@example.com" \\
|
|
116
|
+
--template tmpl_welcome123 \\
|
|
117
|
+
--template-data '{"name": "John", "company": "Acme Inc"}'
|
|
118
|
+
|
|
119
|
+
# Template email with complex data
|
|
120
|
+
emailr send --to "customer@example.com" \\
|
|
121
|
+
--template tmpl_order_confirm \\
|
|
122
|
+
--template-data '{"customer_name": "Jane", "order_id": "ORD-789", "total": "$99.99", "items": [{"name": "Widget", "qty": 2}]}'
|
|
123
|
+
|
|
124
|
+
# Email with CC and BCC
|
|
125
|
+
emailr send --to "primary@example.com" \\
|
|
126
|
+
--cc "manager@example.com,team@example.com" \\
|
|
127
|
+
--bcc "archive@example.com" \\
|
|
128
|
+
--subject "Project Update" \\
|
|
129
|
+
--html-file ./update.html
|
|
130
|
+
|
|
131
|
+
# Scheduled email
|
|
132
|
+
emailr send --to "user@example.com" \\
|
|
133
|
+
--subject "Reminder" \\
|
|
134
|
+
--html "<p>Don't forget your appointment tomorrow!</p>" \\
|
|
135
|
+
--schedule "2024-12-25T09:00:00Z"
|
|
136
|
+
|
|
137
|
+
# Custom sender and reply-to
|
|
138
|
+
emailr send --to "customer@example.com" \\
|
|
139
|
+
--from "support@mycompany.com" \\
|
|
140
|
+
--reply-to "help@mycompany.com" \\
|
|
141
|
+
--subject "Support Response" \\
|
|
142
|
+
--html-file ./response.html
|
|
143
|
+
|
|
144
|
+
# Get JSON output for scripting
|
|
145
|
+
emailr send --to "user@example.com" \\
|
|
146
|
+
--subject "Test" \\
|
|
147
|
+
--text "Test email" \\
|
|
148
|
+
--format json
|
|
149
|
+
|
|
150
|
+
# Send and capture message ID
|
|
151
|
+
emailr send --to "user@example.com" \\
|
|
152
|
+
--subject "Test" \\
|
|
153
|
+
--text "Hello" \\
|
|
154
|
+
--format json | jq -r '.message_id'
|
|
155
|
+
|
|
156
|
+
SEE ALSO
|
|
157
|
+
emailr templates Manage email templates
|
|
158
|
+
emailr contacts Manage contacts
|
|
159
|
+
emailr broadcasts Send bulk emails to segments`).requiredOption("--to <email>","Recipient email address (comma-separated for multiple)").option("--from <email>","Sender email address").option("--subject <subject>","Email subject").option("--html <html>","HTML content (inline)").option("--text <text>","Plain text content (inline)").option("--html-file <path>","Read HTML content from file").option("--text-file <path>","Read plain text content from file").option("--template <id>","Template ID to use").option("--template-data <json>","Template data as JSON").option("--cc <emails>","CC recipients (comma-separated)").option("--bcc <emails>","BCC recipients (comma-separated)").option("--reply-to <email>","Reply-to email address").option("--schedule <datetime>","Schedule send time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r=t.to.split(",").map(s=>s.trim()),o={to:r.length===1?r[0]:r};if(t.from&&(o.from=t.from),t.subject&&(o.subject=t.subject),t.htmlFile)try{o.html=readFileSync(t.htmlFile,"utf-8");}catch{l(`Failed to read HTML file: ${t.htmlFile}`),process.exit(1);}else t.html&&(o.html=t.html);if(t.textFile)try{o.text=readFileSync(t.textFile,"utf-8");}catch{l(`Failed to read text file: ${t.textFile}`),process.exit(1);}else t.text&&(o.text=t.text);if(t.template&&(o.template_id=t.template),t.templateData)try{o.template_data=JSON.parse(t.templateData);}catch{l("Invalid JSON for --template-data"),process.exit(1);}if(t.cc){let s=t.cc.split(",").map(p=>p.trim());o.cc=s.length===1?s[0]:s;}if(t.bcc){let s=t.bcc.split(",").map(p=>p.trim());o.bcc=s.length===1?s[0]:s;}t.replyTo&&(o.reply_to_email=t.replyTo),t.schedule&&(o.scheduled_at=t.schedule);let n=await a.emails.send(o);t.format==="json"?c(n,"json"):(d("Email sent successfully!"),c({"Message ID":n.message_id,Recipients:n.recipients,Status:n.status,...n.scheduled_at&&{"Scheduled At":n.scheduled_at}},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to send email"),process.exit(1);}})}function me(){let i=new Command("contacts").description(`Manage contacts
|
|
160
|
+
|
|
161
|
+
USAGE
|
|
162
|
+
emailr contacts <subcommand> [options]
|
|
163
|
+
|
|
164
|
+
DESCRIPTION
|
|
165
|
+
Create, update, list, and manage email contacts. Contacts are the recipients
|
|
166
|
+
of your email campaigns and can be organized into segments for targeted
|
|
167
|
+
broadcasts.
|
|
168
|
+
|
|
169
|
+
SUBCOMMANDS
|
|
170
|
+
list List all contacts with pagination and filtering
|
|
171
|
+
get <id> Get a contact by ID
|
|
172
|
+
create Create a new contact
|
|
173
|
+
update <id> Update an existing contact
|
|
174
|
+
delete <id> Delete a contact
|
|
175
|
+
|
|
176
|
+
OPTIONS
|
|
177
|
+
--format <format> Output format: json | table (default: table)
|
|
178
|
+
|
|
179
|
+
OUTPUT FORMATS
|
|
180
|
+
--format json Machine-readable JSON output
|
|
181
|
+
--format table Human-readable table output (default)
|
|
182
|
+
|
|
183
|
+
PAGINATION OPTIONS
|
|
184
|
+
--limit <count> Number of contacts to return (default: 20)
|
|
185
|
+
--offset <count> Number of contacts to skip (default: 0)
|
|
186
|
+
|
|
187
|
+
FILTER OPTIONS
|
|
188
|
+
--subscribed Only show subscribed contacts
|
|
189
|
+
--unsubscribed Only show unsubscribed contacts
|
|
190
|
+
|
|
191
|
+
METADATA OPTION
|
|
192
|
+
--metadata <json> Custom metadata as JSON object
|
|
193
|
+
|
|
194
|
+
The metadata option accepts a JSON object with custom key-value pairs.
|
|
195
|
+
This allows you to store additional information about contacts.
|
|
196
|
+
|
|
197
|
+
JSON Format Examples:
|
|
198
|
+
'{"plan": "premium", "source": "website"}'
|
|
199
|
+
'{"company": "Acme Inc", "role": "developer"}'
|
|
200
|
+
'{"tags": ["vip", "early-adopter"], "score": 100}'
|
|
201
|
+
|
|
202
|
+
EXAMPLES
|
|
203
|
+
# List all contacts
|
|
204
|
+
emailr contacts list
|
|
205
|
+
|
|
206
|
+
# List with pagination
|
|
207
|
+
emailr contacts list --limit 50 --offset 100
|
|
208
|
+
|
|
209
|
+
# List only subscribed contacts
|
|
210
|
+
emailr contacts list --subscribed
|
|
211
|
+
|
|
212
|
+
# List only unsubscribed contacts
|
|
213
|
+
emailr contacts list --unsubscribed
|
|
214
|
+
|
|
215
|
+
# Get a specific contact
|
|
216
|
+
emailr contacts get con_abc123
|
|
217
|
+
|
|
218
|
+
# Create a new contact
|
|
219
|
+
emailr contacts create --email "user@example.com" --first-name "John" --last-name "Doe"
|
|
220
|
+
|
|
221
|
+
# Create contact with metadata
|
|
222
|
+
emailr contacts create --email "user@example.com" --metadata '{"plan": "premium"}'
|
|
223
|
+
|
|
224
|
+
# Update a contact
|
|
225
|
+
emailr contacts update con_abc123 --first-name "Jane"
|
|
226
|
+
|
|
227
|
+
# Update contact subscription status
|
|
228
|
+
emailr contacts update con_abc123 --unsubscribed
|
|
229
|
+
|
|
230
|
+
# Update contact metadata
|
|
231
|
+
emailr contacts update con_abc123 --metadata '{"plan": "enterprise", "upgraded": true}'
|
|
232
|
+
|
|
233
|
+
# Delete a contact
|
|
234
|
+
emailr contacts delete con_abc123
|
|
235
|
+
|
|
236
|
+
# Get JSON output for scripting
|
|
237
|
+
emailr contacts list --format json
|
|
238
|
+
|
|
239
|
+
SEE ALSO
|
|
240
|
+
emailr segments Manage contact segments
|
|
241
|
+
emailr broadcasts Send emails to contacts`);return i.command("list").description(`List all contacts
|
|
242
|
+
|
|
243
|
+
USAGE
|
|
244
|
+
emailr contacts list [options]
|
|
245
|
+
|
|
246
|
+
DESCRIPTION
|
|
247
|
+
Retrieves a paginated list of all contacts in your account. Supports
|
|
248
|
+
filtering by subscription status and pagination for large contact lists.
|
|
249
|
+
|
|
250
|
+
OPTIONS
|
|
251
|
+
--limit <count> Number of contacts to return (default: 20)
|
|
252
|
+
--offset <count> Number of contacts to skip for pagination (default: 0)
|
|
253
|
+
--subscribed Only show subscribed contacts
|
|
254
|
+
--unsubscribed Only show unsubscribed contacts
|
|
255
|
+
--format <format> Output format: json | table (default: table)
|
|
256
|
+
|
|
257
|
+
OUTPUT FORMATS
|
|
258
|
+
--format json Machine-readable JSON with contacts array and total count
|
|
259
|
+
--format table Human-readable table with columns: ID, Email, Name, Subscribed, Created
|
|
260
|
+
|
|
261
|
+
PAGINATION
|
|
262
|
+
Use --limit and --offset together for pagination:
|
|
263
|
+
- Page 1: --limit 20 --offset 0 (default)
|
|
264
|
+
- Page 2: --limit 20 --offset 20
|
|
265
|
+
- Page 3: --limit 20 --offset 40
|
|
266
|
+
|
|
267
|
+
EXAMPLES
|
|
268
|
+
# List first 20 contacts (default)
|
|
269
|
+
emailr contacts list
|
|
270
|
+
|
|
271
|
+
# List 50 contacts
|
|
272
|
+
emailr contacts list --limit 50
|
|
273
|
+
|
|
274
|
+
# Get page 2 of results (20 per page)
|
|
275
|
+
emailr contacts list --limit 20 --offset 20
|
|
276
|
+
|
|
277
|
+
# List only subscribed contacts
|
|
278
|
+
emailr contacts list --subscribed
|
|
279
|
+
|
|
280
|
+
# List only unsubscribed contacts
|
|
281
|
+
emailr contacts list --unsubscribed
|
|
282
|
+
|
|
283
|
+
# Get JSON output for scripting
|
|
284
|
+
emailr contacts list --format json
|
|
285
|
+
|
|
286
|
+
# Combine filters with pagination
|
|
287
|
+
emailr contacts list --subscribed --limit 100 --offset 0`).option("--limit <number>","Number of contacts to return","20").option("--offset <number>","Offset for pagination","0").option("--subscribed","Only show subscribed contacts").option("--unsubscribed","Only show unsubscribed contacts").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={limit:parseInt(t.limit,10),offset:parseInt(t.offset,10)};t.subscribed&&(r.subscribed=!0),t.unsubscribed&&(r.subscribed=!1);let o=await a.contacts.list(r);if(t.format==="json")c(o,"json");else {let n=o.contacts.map(s=>({ID:s.id,Email:s.email,Name:[s.first_name,s.last_name].filter(Boolean).join(" ")||"-",Subscribed:s.subscribed,Created:s.created_at}));c(n,"table"),console.log(`
|
|
288
|
+
Total: ${o.total}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list contacts"),process.exit(1);}}),i.command("get <contact_id>").description(`Get a contact by ID
|
|
289
|
+
|
|
290
|
+
USAGE
|
|
291
|
+
emailr contacts get <contact_id> [options]
|
|
292
|
+
|
|
293
|
+
DESCRIPTION
|
|
294
|
+
Retrieves detailed information about a specific contact including
|
|
295
|
+
email, name, subscription status, metadata, and timestamps.
|
|
296
|
+
|
|
297
|
+
ARGUMENTS
|
|
298
|
+
<contact_id> The unique contact identifier (e.g., con_abc123)
|
|
299
|
+
|
|
300
|
+
OPTIONS
|
|
301
|
+
--format <format> Output format: json | table (default: table)
|
|
302
|
+
|
|
303
|
+
OUTPUT FORMATS
|
|
304
|
+
--format json Full contact object including all fields and metadata
|
|
305
|
+
--format table Summary table with key contact information
|
|
306
|
+
|
|
307
|
+
EXAMPLES
|
|
308
|
+
# Get contact details
|
|
309
|
+
emailr contacts get con_abc123
|
|
310
|
+
|
|
311
|
+
# Get full contact data as JSON
|
|
312
|
+
emailr contacts get con_abc123 --format json
|
|
313
|
+
|
|
314
|
+
# Pipe JSON to jq for processing
|
|
315
|
+
emailr contacts get con_abc123 --format json | jq '.metadata'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).contacts.get(t);c(o,e.format);}catch(a){l(a instanceof Error?a.message:"Failed to get contact"),process.exit(1);}}),i.command("create").description(`Create a new contact
|
|
316
|
+
|
|
317
|
+
USAGE
|
|
318
|
+
emailr contacts create --email <email_address> [options]
|
|
319
|
+
|
|
320
|
+
DESCRIPTION
|
|
321
|
+
Creates a new contact with the specified email address and optional
|
|
322
|
+
details. Contacts are subscribed by default unless --subscribed is
|
|
323
|
+
explicitly set to false.
|
|
324
|
+
|
|
325
|
+
OPTIONS
|
|
326
|
+
--email <email_address> Contact email address (required)
|
|
327
|
+
--first-name <name> First name
|
|
328
|
+
--last-name <name> Last name
|
|
329
|
+
--subscribed Mark as subscribed (default: true)
|
|
330
|
+
--metadata <json> Custom metadata as JSON object
|
|
331
|
+
--format <format> Output format: json | table (default: table)
|
|
332
|
+
|
|
333
|
+
METADATA FORMAT
|
|
334
|
+
The --metadata option accepts a JSON object with custom key-value pairs:
|
|
335
|
+
'{"plan": "premium", "source": "website"}'
|
|
336
|
+
'{"company": "Acme Inc", "role": "developer"}'
|
|
337
|
+
'{"tags": ["vip", "early-adopter"], "score": 100}'
|
|
338
|
+
|
|
339
|
+
OUTPUT FORMATS
|
|
340
|
+
--format json Full contact object with all fields
|
|
341
|
+
--format table Summary table with ID, Email, Name, Subscribed
|
|
342
|
+
|
|
343
|
+
EXAMPLES
|
|
344
|
+
# Create contact with email only
|
|
345
|
+
emailr contacts create --email "user@example.com"
|
|
346
|
+
|
|
347
|
+
# Create contact with full name
|
|
348
|
+
emailr contacts create --email "user@example.com" --first-name "John" --last-name "Doe"
|
|
349
|
+
|
|
350
|
+
# Create contact with metadata
|
|
351
|
+
emailr contacts create --email "user@example.com" --metadata '{"plan": "premium", "source": "signup"}'
|
|
352
|
+
|
|
353
|
+
# Create unsubscribed contact (for import purposes)
|
|
354
|
+
emailr contacts create --email "user@example.com" --subscribed false
|
|
355
|
+
|
|
356
|
+
# Get JSON output for scripting
|
|
357
|
+
emailr contacts create --email "user@example.com" --format json`).requiredOption("--email <email>","Contact email address").option("--first-name <name>","First name").option("--last-name <name>","Last name").option("--subscribed","Mark as subscribed (default: true)").option("--metadata <json>","Metadata as JSON").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={email:t.email};if(t.firstName&&(r.first_name=t.firstName),t.lastName&&(r.last_name=t.lastName),t.subscribed!==void 0&&(r.subscribed=t.subscribed),t.metadata)try{r.metadata=JSON.parse(t.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}let o=await a.contacts.create(r);t.format==="json"?c(o,"json"):(d(`Contact created: ${o.id}`),c(o,"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create contact"),process.exit(1);}}),i.command("update <contact_id>").description(`Update a contact
|
|
358
|
+
|
|
359
|
+
USAGE
|
|
360
|
+
emailr contacts update <contact_id> [options]
|
|
361
|
+
|
|
362
|
+
DESCRIPTION
|
|
363
|
+
Updates an existing contact with new values. Only specified fields
|
|
364
|
+
are updated; omitted fields remain unchanged.
|
|
365
|
+
|
|
366
|
+
ARGUMENTS
|
|
367
|
+
<contact_id> The unique contact identifier (e.g., con_abc123)
|
|
368
|
+
|
|
369
|
+
OPTIONS
|
|
370
|
+
--email <email_address> New email address
|
|
371
|
+
--first-name <name> New first name
|
|
372
|
+
--last-name <name> New last name
|
|
373
|
+
--subscribed Mark as subscribed
|
|
374
|
+
--unsubscribed Mark as unsubscribed
|
|
375
|
+
--metadata <json> New metadata as JSON object (replaces existing)
|
|
376
|
+
--format <format> Output format: json | table (default: table)
|
|
377
|
+
|
|
378
|
+
METADATA FORMAT
|
|
379
|
+
The --metadata option accepts a JSON object. Note that this replaces
|
|
380
|
+
the entire metadata object, not individual fields:
|
|
381
|
+
'{"plan": "enterprise", "upgraded": true}'
|
|
382
|
+
'{"tags": ["vip"], "notes": "Important customer"}'
|
|
383
|
+
|
|
384
|
+
OUTPUT FORMATS
|
|
385
|
+
--format json Full updated contact object
|
|
386
|
+
--format table Summary table with updated contact information
|
|
387
|
+
|
|
388
|
+
EXAMPLES
|
|
389
|
+
# Update contact name
|
|
390
|
+
emailr contacts update con_abc123 --first-name "Jane" --last-name "Smith"
|
|
391
|
+
|
|
392
|
+
# Update email address
|
|
393
|
+
emailr contacts update con_abc123 --email "newemail@example.com"
|
|
394
|
+
|
|
395
|
+
# Unsubscribe a contact
|
|
396
|
+
emailr contacts update con_abc123 --unsubscribed
|
|
397
|
+
|
|
398
|
+
# Resubscribe a contact
|
|
399
|
+
emailr contacts update con_abc123 --subscribed
|
|
400
|
+
|
|
401
|
+
# Update metadata
|
|
402
|
+
emailr contacts update con_abc123 --metadata '{"plan": "enterprise", "upgraded": true}'
|
|
403
|
+
|
|
404
|
+
# Get JSON output
|
|
405
|
+
emailr contacts update con_abc123 --first-name "Jane" --format json`).option("--email <email>","New email address").option("--first-name <name>","First name").option("--last-name <name>","Last name").option("--subscribed","Mark as subscribed").option("--unsubscribed","Mark as unsubscribed").option("--metadata <json>","Metadata as JSON").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={};if(e.email&&(o.email=e.email),e.firstName&&(o.first_name=e.firstName),e.lastName&&(o.last_name=e.lastName),e.subscribed&&(o.subscribed=!0),e.unsubscribed&&(o.subscribed=!1),e.metadata)try{o.metadata=JSON.parse(e.metadata);}catch{l("Invalid JSON for --metadata"),process.exit(1);}let n=await r.contacts.update(t,o);e.format==="json"?c(n,"json"):(d(`Contact updated: ${n.id}`),c(n,"table"));}catch(a){l(a instanceof Error?a.message:"Failed to update contact"),process.exit(1);}}),i.command("delete <contact_id>").description(`Delete a contact
|
|
406
|
+
|
|
407
|
+
USAGE
|
|
408
|
+
emailr contacts delete <contact_id>
|
|
409
|
+
|
|
410
|
+
DESCRIPTION
|
|
411
|
+
Permanently deletes a contact. This action cannot be undone.
|
|
412
|
+
The contact will be removed from all segments.
|
|
413
|
+
|
|
414
|
+
ARGUMENTS
|
|
415
|
+
<contact_id> The unique contact identifier (e.g., con_abc123)
|
|
416
|
+
|
|
417
|
+
EXAMPLES
|
|
418
|
+
# Delete a contact
|
|
419
|
+
emailr contacts delete con_abc123
|
|
420
|
+
|
|
421
|
+
WARNING
|
|
422
|
+
This action is permanent and cannot be undone.`).action(async t=>{try{let e=m();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).contacts.delete(t),d(`Contact deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete contact"),process.exit(1);}}),i}function pe(){return N.join(W.homedir(),".config","emailr","templates")}function qe(){let i=pe();v.existsSync(i)||v.mkdirSync(i,{recursive:true});}function B(i){return N.join(pe(),`${i}.html`)}function X(i,t){qe();let e=B(i);v.writeFileSync(e,t,"utf-8");}function ue(i){let t=B(i);return v.existsSync(t)?v.readFileSync(t,"utf-8"):null}function fe(i){let t=B(i);return v.existsSync(t)}var w=null,C=null,H=false;function he(i){return i.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function $e(i){return i===null||i.trim()===""}function We(i){return `<!DOCTYPE html>
|
|
13
423
|
<html lang="en">
|
|
14
424
|
<head>
|
|
15
425
|
<meta charset="UTF-8">
|
|
@@ -75,14 +485,14 @@ Total: ${a.total}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list co
|
|
|
75
485
|
<div class="icon">\u{1F4ED}</div>
|
|
76
486
|
<h1>No Content Available</h1>
|
|
77
487
|
<p>This template exists but has no HTML content to display.</p>
|
|
78
|
-
<div class="template-id">${
|
|
488
|
+
<div class="template-id">${he(i)}</div>
|
|
79
489
|
<div class="hint">
|
|
80
490
|
<p><strong>To add content:</strong></p>
|
|
81
491
|
<p>Update the template using the CLI with the <code>--html</code> option or provide HTML content when creating the template.</p>
|
|
82
492
|
</div>
|
|
83
493
|
</div>
|
|
84
494
|
</body>
|
|
85
|
-
</html>`}function
|
|
495
|
+
</html>`}function be(i){return `<!DOCTYPE html>
|
|
86
496
|
<html lang="en">
|
|
87
497
|
<head>
|
|
88
498
|
<meta charset="UTF-8">
|
|
@@ -134,11 +544,11 @@ Total: ${a.total}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list co
|
|
|
134
544
|
<div class="icon">404</div>
|
|
135
545
|
<h1>Template Not Found</h1>
|
|
136
546
|
<p>The requested template could not be found in local storage.</p>
|
|
137
|
-
<div class="template-id">${
|
|
547
|
+
<div class="template-id">${he(i)}</div>
|
|
138
548
|
<p style="margin-top: 1rem;">Try creating or retrieving the template first using the CLI.</p>
|
|
139
549
|
</div>
|
|
140
550
|
</body>
|
|
141
|
-
</html>`}function
|
|
551
|
+
</html>`}function Be(i){let t=i.match(/^\/preview\/([^/]+)$/);return t?t[1]:null}function Xe(){return (i,t)=>{if(i.method!=="GET"){t.writeHead(405,{"Content-Type":"text/plain"}),t.end("Method Not Allowed");return}let e=i.url||"/",a=Be(e);if(!a){t.writeHead(404,{"Content-Type":"text/plain"}),t.end("Not Found");return}if(!fe(a)){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(be(a));return}let r=ue(a);if(r===null){t.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),t.end(be(a));return}if($e(r)){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(We(a));return}t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(r);}}var ge={async start(){return H&&C!==null?C:new Promise((i,t)=>{w=Je.createServer(Xe()),w.listen(0,"127.0.0.1",()=>{let e=w.address();e&&typeof e=="object"?(C=e.port,H=true,w.unref(),i(C)):t(new Error("Failed to get server address"));}),w.on("error",e=>{H=false,C=null,w=null,t(new Error(`Failed to start preview server: ${e.message}`));});})},getPort(){return C},isRunning(){return H},async stop(){if(w)return new Promise(i=>{w.close(()=>{w=null,C=null,H=false,i();});})}};function we(){return ge}async function V(i){try{return `http://127.0.0.1:${await ge.start()}/preview/${i}`}catch{return null}}function ye(){w&&w.ref();}var A=null,z=null,J=null,R=[],ve=`
|
|
142
552
|
<script>
|
|
143
553
|
(function() {
|
|
144
554
|
const evtSource = new EventSource('/__live-reload');
|
|
@@ -152,31 +562,369 @@ Total: ${a.total}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list co
|
|
|
152
562
|
};
|
|
153
563
|
})();
|
|
154
564
|
</script>
|
|
155
|
-
`;function Ye(
|
|
565
|
+
`;function Ye(i){return i.includes("</body>")?i.replace("</body>",`${ve}</body>`):i+ve}function Ze(){R.forEach(i=>{try{i.write(`data: reload
|
|
566
|
+
|
|
567
|
+
`);}catch{}});}async function Y(i,t){let e=N.resolve(i);return new Promise((a,r)=>{A=Je.createServer((o,n)=>{if(o.url==="/__live-reload"){n.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),n.write(`data: connected
|
|
568
|
+
|
|
569
|
+
`),R.push(n),o.on("close",()=>{R=R.filter(s=>s!==n);});return}if(o.method==="GET"){try{let s=v.readFileSync(e,"utf-8"),p=Ye(s);n.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),n.end(p);}catch(s){n.writeHead(500,{"Content-Type":"text/plain"}),n.end(`Error reading file: ${s instanceof Error?s.message:String(s)}`);}return}n.writeHead(405,{"Content-Type":"text/plain"}),n.end("Method Not Allowed");}),A.listen(0,"127.0.0.1",()=>{let o=A.address();if(o&&typeof o=="object"){z=o.port;let n=null;J=v.watch(e,s=>{s==="change"&&(n&&clearTimeout(n),n=setTimeout(()=>{Ze(),t?.();},100));}),a(z);}else r(new Error("Failed to get server address"));}),A.on("error",o=>{r(new Error(`Failed to start server: ${o.message}`));});})}async function Z(){if(J&&(J.close(),J=null),R.forEach(i=>{try{i.end();}catch{}}),R=[],A)return new Promise(i=>{A.close(()=>{A=null,z=null,i();});})}async function Te(i){try{let t=i.html_content??"";X(i.id,t);}catch(t){return k(`Could not save template for preview: ${t instanceof Error?t.message:String(t)}`),null}try{let t=await V(i.id);return t===null?(k("Could not start preview server"),null):t}catch(t){return k(`Could not generate preview URL: ${t instanceof Error?t.message:String(t)}`),null}}function Oe(){let i=new Command("templates").description(`Manage email templates
|
|
570
|
+
|
|
571
|
+
USAGE
|
|
572
|
+
emailr templates <subcommand> [options]
|
|
573
|
+
|
|
574
|
+
DESCRIPTION
|
|
575
|
+
Create, edit, preview, and publish email templates. Supports both local
|
|
576
|
+
live-preview editing and server-side preview URLs for sharing with AI agents.
|
|
577
|
+
|
|
578
|
+
SUBCOMMANDS
|
|
579
|
+
list List all templates
|
|
580
|
+
get <id> Get a template by ID
|
|
581
|
+
create Create a new template
|
|
582
|
+
update <id> Update an existing template
|
|
583
|
+
delete <id> Delete a template
|
|
584
|
+
fetch <id> Download template HTML to file or stdout
|
|
585
|
+
push-preview <id> Upload HTML to template's preview for sharing
|
|
586
|
+
preview <id> Preview a template in the browser
|
|
587
|
+
edit <id> Start live editing session with hot-reload
|
|
588
|
+
draft Start live drafting session for new template
|
|
589
|
+
stop-preview Stop any running background preview server
|
|
590
|
+
|
|
591
|
+
OUTPUT FORMATS
|
|
592
|
+
--format json Machine-readable JSON output
|
|
593
|
+
--format table Human-readable table output (default)
|
|
594
|
+
|
|
595
|
+
EXAMPLES
|
|
596
|
+
# List all templates
|
|
597
|
+
emailr templates list
|
|
598
|
+
|
|
599
|
+
# Get a specific template
|
|
600
|
+
emailr templates get tpl_abc123
|
|
601
|
+
|
|
602
|
+
# Create a template from file
|
|
603
|
+
emailr templates create --name "Welcome" --subject "Welcome!" --html-file welcome.html
|
|
604
|
+
|
|
605
|
+
# Update a template
|
|
606
|
+
emailr templates update tpl_abc123 --subject "New Subject"
|
|
607
|
+
|
|
608
|
+
# Download template HTML to a file
|
|
609
|
+
emailr templates fetch tpl_abc123 --output template.html
|
|
610
|
+
|
|
611
|
+
# Push preview for AI agent review
|
|
612
|
+
emailr templates push-preview tpl_abc123 --html-file template.html
|
|
613
|
+
|
|
614
|
+
# Start live editing with hot-reload
|
|
615
|
+
emailr templates edit tpl_abc123 --file ./template.html
|
|
616
|
+
|
|
617
|
+
# Draft a new template with live preview
|
|
618
|
+
emailr templates draft --file ./new-template.html
|
|
619
|
+
|
|
620
|
+
AGENTIC WORKFLOW
|
|
621
|
+
For collaborating with AI agents on template design:
|
|
622
|
+
|
|
623
|
+
1. Fetch current template:
|
|
624
|
+
emailr templates fetch <id> --output template.html
|
|
625
|
+
|
|
626
|
+
2. Edit locally or with AI assistance
|
|
627
|
+
|
|
628
|
+
3. Push preview for AI review:
|
|
629
|
+
emailr templates push-preview <id> --html-file template.html
|
|
630
|
+
|
|
631
|
+
4. Share the preview URL with your AI agent for feedback
|
|
632
|
+
|
|
633
|
+
5. Repeat steps 2-4 until satisfied
|
|
634
|
+
|
|
635
|
+
6. Publish the final version:
|
|
636
|
+
emailr templates update <id> --html-file template.html
|
|
637
|
+
|
|
638
|
+
SEE ALSO
|
|
639
|
+
emailr send Send emails using templates
|
|
640
|
+
emailr broadcasts Send bulk emails to segments`);return i.command("list").description(`List all templates
|
|
641
|
+
|
|
642
|
+
USAGE
|
|
643
|
+
emailr templates list [options]
|
|
644
|
+
|
|
645
|
+
DESCRIPTION
|
|
646
|
+
Retrieves a paginated list of all email templates in your account.
|
|
647
|
+
Returns template ID, name, subject, variables, and creation date.
|
|
648
|
+
|
|
649
|
+
OPTIONS
|
|
650
|
+
--limit <count> Number of templates to return (default: 20)
|
|
651
|
+
--page <page_number> Page number for pagination (default: 1)
|
|
652
|
+
--format <format> Output format: json | table (default: table)
|
|
653
|
+
|
|
654
|
+
OUTPUT FORMATS
|
|
655
|
+
--format json Machine-readable JSON array of template objects
|
|
656
|
+
--format table Human-readable table with columns: ID, Name, Subject, Variables, Created
|
|
156
657
|
|
|
157
|
-
|
|
658
|
+
EXAMPLES
|
|
659
|
+
# List first 20 templates
|
|
660
|
+
emailr templates list
|
|
158
661
|
|
|
159
|
-
|
|
662
|
+
# List 50 templates
|
|
663
|
+
emailr templates list --limit 50
|
|
160
664
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
edit <id> Edit existing template with live preview
|
|
665
|
+
# Get page 2 of results
|
|
666
|
+
emailr templates list --page 2 --limit 10
|
|
164
667
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
Total: ${
|
|
668
|
+
# Get JSON output for scripting
|
|
669
|
+
emailr templates list --format json`).option("--limit <count>","Number of templates to return","20").option("--page <page_number>","Page number for pagination","1").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).templates.list({limit:parseInt(t.limit,10),page:parseInt(t.page,10)});if(t.format==="json")c(r,"json");else {let o=r.map(n=>({ID:n.id,Name:n.name,Subject:n.subject,Variables:n.variables?.join(", ")||"-",Created:n.created_at}));c(o,"table"),console.log(`
|
|
670
|
+
Total: ${r.length}`);}}catch(e){l(e instanceof Error?e.message:"Failed to list templates"),process.exit(1);}}),i.command("get <template_id>").description(`Get a template by ID
|
|
168
671
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
Open the Preview URL in your browser to view the rendered template.`);}}catch(e){l(e instanceof Error?e.message:"Failed to create template"),process.exit(1);}}),n.command("update <id>").description(`Update a template
|
|
672
|
+
USAGE
|
|
673
|
+
emailr templates get <template_id> [options]
|
|
172
674
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
675
|
+
DESCRIPTION
|
|
676
|
+
Retrieves detailed information about a specific template including
|
|
677
|
+
its HTML content, subject, variables, and preview URL if available.
|
|
678
|
+
Also starts a local preview server for viewing the template.
|
|
679
|
+
|
|
680
|
+
ARGUMENTS
|
|
681
|
+
<template_id> The unique template identifier (e.g., tpl_abc123)
|
|
682
|
+
|
|
683
|
+
OPTIONS
|
|
684
|
+
--format <format> Output format: json | table (default: table)
|
|
685
|
+
|
|
686
|
+
OUTPUT FORMATS
|
|
687
|
+
--format json Full template object including html_content and preview_url
|
|
688
|
+
--format table Summary table with ID, Name, Subject, Variables, Created, Preview URL
|
|
689
|
+
|
|
690
|
+
EXAMPLES
|
|
691
|
+
# Get template details
|
|
692
|
+
emailr templates get tpl_abc123
|
|
693
|
+
|
|
694
|
+
# Get full template data as JSON
|
|
695
|
+
emailr templates get tpl_abc123 --format json
|
|
696
|
+
|
|
697
|
+
# Pipe JSON to jq for processing
|
|
698
|
+
emailr templates get tpl_abc123 --format json | jq '.html_content'`).option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).templates.get(t),n=await Te({id:o.id,html_content:o.html_content??void 0}),s=o.preview_html?`${a.baseUrl}/preview/${o.id}`:null;if(e.format==="json")c({...o,preview_url:s??n},"json");else {let p={ID:o.id,Name:o.name,Subject:o.subject,Variables:o.variables?.join(", ")||"-",Created:o.created_at};s?p["Preview URL"]=s:n&&(p["Local Preview"]=n),c(p,"table");}}catch(a){l(a instanceof Error?a.message:"Failed to get template"),process.exit(1);}}),i.command("fetch <template_id>").description(`Download template HTML to file or stdout
|
|
699
|
+
|
|
700
|
+
USAGE
|
|
701
|
+
emailr templates fetch <template_id> [options]
|
|
702
|
+
|
|
703
|
+
DESCRIPTION
|
|
704
|
+
Downloads the HTML content of a template to a local file or prints
|
|
705
|
+
it to stdout. Use this to get a local copy for editing before
|
|
706
|
+
pushing a preview or updating the template.
|
|
707
|
+
|
|
708
|
+
ARGUMENTS
|
|
709
|
+
<template_id> The unique template identifier (e.g., tpl_abc123)
|
|
710
|
+
|
|
711
|
+
OPTIONS
|
|
712
|
+
--output <file_path> Write HTML to specified file (default: print to stdout)
|
|
713
|
+
--preview Fetch preview HTML instead of published HTML
|
|
714
|
+
|
|
715
|
+
EXAMPLES
|
|
716
|
+
# Print HTML to stdout
|
|
717
|
+
emailr templates fetch tpl_abc123
|
|
718
|
+
|
|
719
|
+
# Save to file for editing
|
|
720
|
+
emailr templates fetch tpl_abc123 --output template.html
|
|
721
|
+
|
|
722
|
+
# Fetch preview HTML instead of published
|
|
723
|
+
emailr templates fetch tpl_abc123 --preview --output preview.html
|
|
724
|
+
|
|
725
|
+
# Pipe to another command
|
|
726
|
+
emailr templates fetch tpl_abc123 | grep "{{.*}}"
|
|
727
|
+
|
|
728
|
+
AGENTIC WORKFLOW
|
|
729
|
+
This is step 1 of the agentic workflow:
|
|
730
|
+
1. Fetch: emailr templates fetch <id> --output template.html
|
|
731
|
+
2. Edit locally or with AI assistance
|
|
732
|
+
3. Push preview: emailr templates push-preview <id> --html-file template.html`).option("--output <file_path>","Write HTML to file (default: stdout)").option("--preview","Fetch preview HTML instead of published HTML").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).templates.fetch(t,{preview:e.preview??!1});if(o||(e.preview?(l("No preview HTML exists for this template"),console.log(`
|
|
733
|
+
Run 'emailr templates push-preview <id> --html-file <path>' to create a preview.`)):l("Template has no HTML content"),process.exit(1)),e.output){let n=N.resolve(e.output);v.writeFileSync(n,o,"utf-8"),d(`HTML saved to: ${n}`);}else console.log(o);}catch(a){l(a instanceof Error?a.message:"Failed to fetch template"),process.exit(1);}}),i.command("push-preview <template_id>").description(`Upload HTML to template's preview for sharing with AI agents
|
|
734
|
+
|
|
735
|
+
USAGE
|
|
736
|
+
emailr templates push-preview <template_id> --html-file <file_path>
|
|
737
|
+
emailr templates push-preview <template_id> --html <html_content>
|
|
738
|
+
|
|
739
|
+
DESCRIPTION
|
|
740
|
+
Uploads HTML content to a template's preview slot. The preview is
|
|
741
|
+
accessible via a public URL that can be shared with AI agents for
|
|
742
|
+
review and feedback. The preview persists until the template is
|
|
743
|
+
published or a new preview is pushed.
|
|
744
|
+
|
|
745
|
+
ARGUMENTS
|
|
746
|
+
<template_id> The unique template identifier (e.g., tpl_abc123)
|
|
747
|
+
|
|
748
|
+
OPTIONS
|
|
749
|
+
--html-file <file_path> Read HTML content from specified file
|
|
750
|
+
--html <html_content> Inline HTML content string
|
|
751
|
+
--format <format> Output format: json | table (default: table)
|
|
752
|
+
|
|
753
|
+
OUTPUT FORMATS
|
|
754
|
+
--format json Returns: { template_id, preview_url, status }
|
|
755
|
+
--format table Human-readable table with Template ID, Preview URL, Status
|
|
756
|
+
|
|
757
|
+
EXAMPLES
|
|
758
|
+
# Push preview from file
|
|
759
|
+
emailr templates push-preview tpl_abc123 --html-file template.html
|
|
760
|
+
|
|
761
|
+
# Push preview with inline HTML
|
|
762
|
+
emailr templates push-preview tpl_abc123 --html "<h1>Hello {{name}}</h1>"
|
|
763
|
+
|
|
764
|
+
# Get JSON output for scripting
|
|
765
|
+
emailr templates push-preview tpl_abc123 --html-file template.html --format json
|
|
766
|
+
|
|
767
|
+
# Extract just the preview URL
|
|
768
|
+
emailr templates push-preview tpl_abc123 --html-file template.html --format json | jq -r '.preview_url'
|
|
769
|
+
|
|
770
|
+
AGENTIC WORKFLOW
|
|
771
|
+
This is step 3 of the agentic workflow:
|
|
772
|
+
1. Fetch: emailr templates fetch <id> --output template.html
|
|
773
|
+
2. Edit locally or with AI assistance
|
|
774
|
+
3. Push preview: emailr templates push-preview <id> --html-file template.html
|
|
775
|
+
4. Share the preview URL with your AI agent for feedback
|
|
776
|
+
5. Repeat steps 2-4 until satisfied`).option("--html-file <file_path>","Read HTML content from file").option("--html <html_content>","Inline HTML content").option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{!e.htmlFile&&!e.html&&(l("Either --html-file or --html is required"),console.log(`
|
|
777
|
+
Usage:`),console.log(" emailr templates push-preview <id> --html-file <path>"),console.log(" emailr templates push-preview <id> --html <content>"),process.exit(1)),e.htmlFile&&e.html&&(l("Cannot use both --html-file and --html. Choose one."),process.exit(1));let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o;if(e.htmlFile){let p=N.resolve(e.htmlFile);try{o=v.readFileSync(p,"utf-8");}catch{l(`Failed to read file: ${p}`),process.exit(1);}}else o=e.html;let n=await r.templates.pushPreview(t,o),s="updated";e.format==="json"?c({template_id:t,preview_url:n.preview_url,status:s},"json"):(d("Preview uploaded successfully"),c({"Template ID":t,"Preview URL":n.preview_url,Status:s},"table"),console.log(`
|
|
778
|
+
Share this URL with your AI agent for feedback.`));}catch(a){l(a instanceof Error?a.message:"Failed to push preview"),process.exit(1);}}),i.command("create").description(`Create a new email template
|
|
779
|
+
|
|
780
|
+
USAGE
|
|
781
|
+
emailr templates create --name <template_name> --subject <subject_line> [options]
|
|
782
|
+
|
|
783
|
+
DESCRIPTION
|
|
784
|
+
Creates a new email template with the specified name, subject, and content.
|
|
785
|
+
HTML content can be provided inline or from a file. After creation, a local
|
|
786
|
+
preview URL is generated for viewing the template.
|
|
787
|
+
|
|
788
|
+
OPTIONS
|
|
789
|
+
--name <template_name> Template name (required)
|
|
790
|
+
--subject <subject_line> Email subject line (required)
|
|
791
|
+
--html <html_content> Inline HTML content
|
|
792
|
+
--text <text_content> Plain text content
|
|
793
|
+
--html-file <file_path> Read HTML content from file
|
|
794
|
+
--text-file <file_path> Read text content from file
|
|
795
|
+
--from <email_address> Default from email address
|
|
796
|
+
--reply-to <email_address> Default reply-to email address
|
|
797
|
+
--preview-text <text> Preview text shown in email clients
|
|
798
|
+
--format <format> Output format: json | table (default: table)
|
|
799
|
+
|
|
800
|
+
OUTPUT FORMATS
|
|
801
|
+
--format json Full template object with preview_url
|
|
802
|
+
--format table Summary table with ID, Name, Subject, Variables, Preview URL
|
|
803
|
+
|
|
804
|
+
EXAMPLES
|
|
805
|
+
# Create template with inline HTML
|
|
806
|
+
emailr templates create --name "Welcome" --subject "Welcome to {{company}}!" \\
|
|
807
|
+
--html "<h1>Hello {{name}}</h1>"
|
|
808
|
+
|
|
809
|
+
# Create template from file
|
|
810
|
+
emailr templates create --name "Newsletter" --subject "Weekly Update" \\
|
|
811
|
+
--html-file newsletter.html
|
|
812
|
+
|
|
813
|
+
# Create with all options
|
|
814
|
+
emailr templates create --name "Order Confirmation" --subject "Order #{{order_id}}" \\
|
|
815
|
+
--html-file order.html --text-file order.txt \\
|
|
816
|
+
--from "orders@example.com" --reply-to "support@example.com"
|
|
817
|
+
|
|
818
|
+
# Get JSON output for scripting
|
|
819
|
+
emailr templates create --name "Test" --subject "Test" --html "<p>Test</p>" --format json
|
|
820
|
+
|
|
821
|
+
TIP
|
|
822
|
+
For live preview while building, use "emailr templates draft" first.
|
|
823
|
+
It creates a local file with hot-reload preview, then use this command to save.`).requiredOption("--name <template_name>","Template name").requiredOption("--subject <subject_line>","Email subject line").option("--html <html_content>","Inline HTML content").option("--text <text_content>","Plain text content").option("--html-file <file_path>","Read HTML content from file").option("--text-file <file_path>","Read text content from file").option("--from <email_address>","Default from email address").option("--reply-to <email_address>","Default reply-to email address").option("--preview-text <text>","Preview text shown in email clients").option("--format <format>","Output format: json | table","table").action(async t=>{try{let e=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r={name:t.name,subject:t.subject};if(t.htmlFile){let s=await import('fs');r.html_content=s.readFileSync(t.htmlFile,"utf-8");}else t.html&&(r.html_content=t.html);if(t.textFile){let s=await import('fs');r.text_content=s.readFileSync(t.textFile,"utf-8");}else t.text&&(r.text_content=t.text);t.from&&(r.from_email=t.from),t.replyTo&&(r.reply_to=t.replyTo),t.previewText&&(r.preview_text=t.previewText);let o=await a.templates.create(r),n=await Te({id:o.id,html_content:o.html_content??void 0});if(t.format==="json")c({...o,preview_url:n},"json");else {d(`Template created: ${o.id}`);let s={ID:o.id,Name:o.name,Subject:o.subject,Variables:o.variables?.join(", ")||"-"};n&&(s["Preview URL"]=n),c(s,"table"),n&&console.log(`
|
|
824
|
+
Open the Preview URL in your browser to view the rendered template.`);}}catch(e){l(e instanceof Error?e.message:"Failed to create template"),process.exit(1);}}),i.command("update <template_id>").description(`Update an existing email template
|
|
825
|
+
|
|
826
|
+
USAGE
|
|
827
|
+
emailr templates update <template_id> [options]
|
|
828
|
+
|
|
829
|
+
DESCRIPTION
|
|
830
|
+
Updates an existing template with new values. Only specified fields
|
|
831
|
+
are updated; omitted fields remain unchanged. Use this to publish
|
|
832
|
+
changes after editing with the agentic workflow.
|
|
833
|
+
|
|
834
|
+
ARGUMENTS
|
|
835
|
+
<template_id> The unique template identifier (e.g., tpl_abc123)
|
|
836
|
+
|
|
837
|
+
OPTIONS
|
|
838
|
+
--name <template_name> New template name
|
|
839
|
+
--subject <subject_line> New email subject line
|
|
840
|
+
--html <html_content> New inline HTML content
|
|
841
|
+
--text <text_content> New plain text content
|
|
842
|
+
--html-file <file_path> Read new HTML content from file
|
|
843
|
+
--text-file <file_path> Read new text content from file
|
|
844
|
+
--from <email_address> New default from email address
|
|
845
|
+
--reply-to <email_address> New default reply-to email address
|
|
846
|
+
--preview-text <text> New preview text
|
|
847
|
+
--format <format> Output format: json | table (default: table)
|
|
848
|
+
|
|
849
|
+
OUTPUT FORMATS
|
|
850
|
+
--format json Full updated template object
|
|
851
|
+
--format table Summary table with ID, Name, Subject, Variables, Updated
|
|
852
|
+
|
|
853
|
+
EXAMPLES
|
|
854
|
+
# Update subject line
|
|
855
|
+
emailr templates update tpl_abc123 --subject "New Subject Line"
|
|
856
|
+
|
|
857
|
+
# Update HTML from file (publish after editing)
|
|
858
|
+
emailr templates update tpl_abc123 --html-file template.html
|
|
859
|
+
|
|
860
|
+
# Update multiple fields
|
|
861
|
+
emailr templates update tpl_abc123 --name "Updated Name" --subject "Updated Subject"
|
|
862
|
+
|
|
863
|
+
# Get JSON output
|
|
864
|
+
emailr templates update tpl_abc123 --subject "Test" --format json
|
|
865
|
+
|
|
866
|
+
TIP
|
|
867
|
+
For live preview while editing, use "emailr templates edit <id>" first.
|
|
868
|
+
It downloads the template with hot-reload preview, then use this command to save.
|
|
869
|
+
|
|
870
|
+
AGENTIC WORKFLOW
|
|
871
|
+
This is the final step of the agentic workflow:
|
|
872
|
+
1. Fetch: emailr templates fetch <id> --output template.html
|
|
873
|
+
2. Edit locally or with AI assistance
|
|
874
|
+
3. Push preview: emailr templates push-preview <id> --html-file template.html
|
|
875
|
+
4. Share URL with AI agent, iterate on feedback
|
|
876
|
+
5. Publish: emailr templates update <id> --html-file template.html`).option("--name <template_name>","New template name").option("--subject <subject_line>","New email subject line").option("--html <html_content>","New inline HTML content").option("--text <text_content>","New plain text content").option("--html-file <file_path>","Read new HTML content from file").option("--text-file <file_path>","Read new text content from file").option("--from <email_address>","New default from email address").option("--reply-to <email_address>","New default reply-to email address").option("--preview-text <text>","New preview text").option("--format <format>","Output format: json | table","table").action(async(t,e)=>{try{let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={};if(e.name&&(o.name=e.name),e.subject&&(o.subject=e.subject),e.htmlFile){let s=await import('fs');o.html_content=s.readFileSync(e.htmlFile,"utf-8");}else e.html&&(o.html_content=e.html);if(e.textFile){let s=await import('fs');o.text_content=s.readFileSync(e.textFile,"utf-8");}else e.text&&(o.text_content=e.text);e.from&&(o.from_email=e.from),e.replyTo&&(o.reply_to=e.replyTo),e.previewText&&(o.preview_text=e.previewText);let n=await r.templates.update(t,o);if(e.format==="json")c(n,"json");else {d(`Template updated: ${n.id}`);let s={ID:n.id,Name:n.name,Subject:n.subject,Variables:n.variables?.join(", ")||"-",Updated:n.updated_at};c(s,"table");}}catch(a){l(a instanceof Error?a.message:"Failed to update template"),process.exit(1);}}),i.command("delete <template_id>").description(`Delete a template
|
|
877
|
+
|
|
878
|
+
USAGE
|
|
879
|
+
emailr templates delete <template_id>
|
|
880
|
+
|
|
881
|
+
DESCRIPTION
|
|
882
|
+
Permanently deletes a template. This action cannot be undone.
|
|
883
|
+
Any broadcasts or automations using this template will fail.
|
|
884
|
+
|
|
885
|
+
ARGUMENTS
|
|
886
|
+
<template_id> The unique template identifier (e.g., tpl_abc123)
|
|
887
|
+
|
|
888
|
+
EXAMPLES
|
|
889
|
+
# Delete a template
|
|
890
|
+
emailr templates delete tpl_abc123
|
|
891
|
+
|
|
892
|
+
WARNING
|
|
893
|
+
This action is permanent and cannot be undone.`).action(async t=>{try{let e=m();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).templates.delete(t),d(`Template deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete template"),process.exit(1);}}),i.command("preview <template_id>").description(`Preview a template in the browser
|
|
894
|
+
|
|
895
|
+
USAGE
|
|
896
|
+
emailr templates preview <template_id> [options]
|
|
897
|
+
|
|
898
|
+
DESCRIPTION
|
|
899
|
+
Opens a template preview in your default browser. By default, runs
|
|
900
|
+
a background server that serves the template HTML. Use --foreground
|
|
901
|
+
to keep the process running in the terminal.
|
|
902
|
+
|
|
903
|
+
ARGUMENTS
|
|
904
|
+
<template_id> The unique template identifier (e.g., tpl_abc123)
|
|
905
|
+
|
|
906
|
+
OPTIONS
|
|
907
|
+
--no-open Do not automatically open browser
|
|
908
|
+
--foreground Keep process running in foreground (blocks terminal)
|
|
909
|
+
|
|
910
|
+
EXAMPLES
|
|
911
|
+
# Preview template in browser
|
|
912
|
+
emailr templates preview tpl_abc123
|
|
913
|
+
|
|
914
|
+
# Preview without opening browser (just get URL)
|
|
915
|
+
emailr templates preview tpl_abc123 --no-open
|
|
916
|
+
|
|
917
|
+
# Run in foreground (Ctrl+C to stop)
|
|
918
|
+
emailr templates preview tpl_abc123 --foreground
|
|
919
|
+
|
|
920
|
+
SEE ALSO
|
|
921
|
+
emailr templates edit Live editing with hot-reload
|
|
922
|
+
emailr templates stop-preview Stop background preview server`).option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(t,e)=>{try{let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl});console.log(`Fetching template ${t}...`);let o=await r.templates.get(t),n=o.html_content??"";if(X(o.id,n),e.foreground){let f=await V(o.id);if(f||(l("Failed to start preview server"),process.exit(1)),ye(),console.log(`
|
|
923
|
+
Template: ${o.name}`),console.log(`Preview URL: ${f}`),e.open!==!1)try{await(await import('open')).default(f),console.log(`
|
|
176
924
|
Browser opened.`);}catch{console.log(`
|
|
177
925
|
Could not open browser automatically. Open the URL above manually.`);}process.on("SIGINT",async()=>{console.log(`
|
|
178
926
|
|
|
179
|
-
Stopping preview server...`),await
|
|
927
|
+
Stopping preview server...`),await we().stop(),console.log("Done."),process.exit(0);});return}let s=N.join(process.cwd(),`.template-preview-${o.id}.html`);v.writeFileSync(s,n,"utf-8");let{spawn:p}=await import('child_process'),b=await import('os'),T=N.join(b.tmpdir(),"emailr-preview.pid");try{let f=v.readFileSync(T,"utf-8").trim();process.kill(parseInt(f,10),"SIGTERM");}catch{}let h=`
|
|
180
928
|
const http = require('http');
|
|
181
929
|
const fs = require('fs');
|
|
182
930
|
const path = require('path');
|
|
@@ -201,25 +949,59 @@ Stopping preview server...`),await he().stop(),console.log("Done."),process.exit
|
|
|
201
949
|
console.log('PORT:' + port);
|
|
202
950
|
});
|
|
203
951
|
process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); fs.unlinkSync(filePath); } catch {} process.exit(0); });
|
|
204
|
-
`,
|
|
205
|
-
Template: ${
|
|
206
|
-
To stop: emailr templates stop-preview`),e.open!==!1)try{await(await import('open')).default(D);}catch{}process.exit(0);}catch(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
952
|
+
`,y=p("node",["-e",h],{detached:!0,stdio:["ignore","pipe","ignore"]}),O="";await new Promise(f=>{y.stdout?.on("data",_=>{let q=_.toString().match(/PORT:(\d+)/);q&&(O=q[1],f());}),setTimeout(f,3e3);}),y.unref();let D=`http://127.0.0.1:${O}/`;if(console.log(`
|
|
953
|
+
Template: ${o.name}`),console.log(`Preview URL: ${D}`),console.log(`
|
|
954
|
+
To stop: emailr templates stop-preview`),e.open!==!1)try{await(await import('open')).default(D);}catch{}process.exit(0);}catch(a){l(a instanceof Error?a.message:"Failed to preview template"),process.exit(1);}}),i.command("edit <template_id>").description(`Start a live editing session for a template with hot-reload
|
|
955
|
+
|
|
956
|
+
USAGE
|
|
957
|
+
emailr templates edit <template_id> [options]
|
|
958
|
+
|
|
959
|
+
DESCRIPTION
|
|
960
|
+
Downloads a template's HTML to a local file and starts a live preview
|
|
961
|
+
server with hot-reload. Edit the file in your editor and see changes
|
|
962
|
+
instantly in the browser. When done, save changes with the update command.
|
|
963
|
+
|
|
964
|
+
ARGUMENTS
|
|
965
|
+
<template_id> The unique template identifier (e.g., tpl_abc123)
|
|
966
|
+
|
|
967
|
+
OPTIONS
|
|
968
|
+
--file <file_path> Path to save the HTML file for editing (default: ./template.html)
|
|
969
|
+
--no-open Do not automatically open browser
|
|
970
|
+
--foreground Keep process running in foreground (blocks terminal)
|
|
971
|
+
|
|
972
|
+
WORKFLOW
|
|
973
|
+
1. Run: emailr templates edit <id> --file ./template.html
|
|
974
|
+
2. Edit the file at ./template.html - browser auto-refreshes on save
|
|
975
|
+
3. When done: emailr templates update <id> --html-file ./template.html
|
|
976
|
+
4. Stop server: emailr templates stop-preview
|
|
977
|
+
|
|
978
|
+
EXAMPLES
|
|
979
|
+
# Start editing with default file path
|
|
980
|
+
emailr templates edit tpl_abc123
|
|
981
|
+
|
|
982
|
+
# Specify custom file path
|
|
983
|
+
emailr templates edit tpl_abc123 --file ./emails/welcome.html
|
|
984
|
+
|
|
985
|
+
# Run in foreground (Ctrl+C to stop)
|
|
986
|
+
emailr templates edit tpl_abc123 --foreground
|
|
987
|
+
|
|
988
|
+
# Don't open browser automatically
|
|
989
|
+
emailr templates edit tpl_abc123 --no-open
|
|
990
|
+
|
|
991
|
+
SEE ALSO
|
|
992
|
+
emailr templates draft Draft a new template with live preview
|
|
993
|
+
emailr templates update Save changes to the template
|
|
994
|
+
emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for editing","./template.html").option("--no-open","Do not automatically open browser").option("--foreground","Keep process running in foreground (blocks terminal)").action(async(t,e)=>{try{let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o=N.resolve(e.file);console.log(`Fetching template ${t}...`);let n=await r.templates.get(t),s=n.html_content??"";if(v.writeFileSync(o,s,"utf-8"),console.log(`Template saved to: ${o}`),e.foreground){let _=`http://127.0.0.1:${await Y(o,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
|
|
995
|
+
Template: ${n.name}`),console.log(`Template ID: ${n.id}`),console.log(`Live Preview: ${_}`),console.log(`File: ${o}`),console.log(`
|
|
996
|
+
Watching for changes... Edit the file and see live updates.`),console.log(`When done, run: emailr templates update ${t} --html-file ${e.file}`),e.open!==!1)try{await(await import('open')).default(_);}catch{}process.on("SIGINT",async()=>{console.log(`
|
|
997
|
+
|
|
998
|
+
Stopping live preview server...`),await Z(),console.log("Done."),console.log(`
|
|
999
|
+
To save changes: emailr templates update ${t} --html-file ${e.file}`),process.exit(0);});return}let{spawn:p}=await import('child_process'),b=await import('os'),T=N.join(b.tmpdir(),"emailr-preview.pid");try{let f=v.readFileSync(T,"utf-8").trim();process.kill(parseInt(f,10),"SIGTERM");}catch{}let h=`
|
|
218
1000
|
const http = require('http');
|
|
219
1001
|
const fs = require('fs');
|
|
220
1002
|
const path = require('path');
|
|
221
1003
|
const os = require('os');
|
|
222
|
-
const filePath = ${JSON.stringify(
|
|
1004
|
+
const filePath = ${JSON.stringify(o)};
|
|
223
1005
|
const pidFile = path.join(os.tmpdir(), 'emailr-preview.pid');
|
|
224
1006
|
const portFile = path.join(os.tmpdir(), 'emailr-preview.port');
|
|
225
1007
|
let clients = [];
|
|
@@ -254,16 +1036,52 @@ To save changes: emailr templates update ${t} --html-file ${e.file}`),process.ex
|
|
|
254
1036
|
});
|
|
255
1037
|
});
|
|
256
1038
|
process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); } catch {} process.exit(0); });
|
|
257
|
-
`,
|
|
258
|
-
Template: ${
|
|
1039
|
+
`,y=p("node",["-e",h],{detached:!0,stdio:["ignore","pipe","ignore"]}),O="";await new Promise(f=>{y.stdout?.on("data",_=>{let ie=_.toString().match(/PORT:(\d+)/);ie&&(O=ie[1],f());}),setTimeout(f,3e3);}),y.unref();let D=`http://127.0.0.1:${O}/`;if(console.log(`
|
|
1040
|
+
Template: ${n.name}`),console.log(`Template ID: ${n.id}`),console.log(`Live Preview: ${D}`),console.log(`File: ${o}`),console.log(`
|
|
259
1041
|
Edit the file and see live updates in the browser.`),console.log(`
|
|
260
|
-
When done:`),console.log(` emailr templates update ${t} --html-file ${e.file}`),console.log(" emailr templates stop-preview"),e.open!==!1)try{await(await import('open')).default(D);}catch{}process.exit(0);}catch(
|
|
1042
|
+
When done:`),console.log(` emailr templates update ${t} --html-file ${e.file}`),console.log(" emailr templates stop-preview"),e.open!==!1)try{await(await import('open')).default(D);}catch{}process.exit(0);}catch(a){l(a instanceof Error?a.message:"Failed to edit template"),process.exit(1);}}),i.command("draft").description(`Start a live drafting session for a new template with hot-reload
|
|
1043
|
+
|
|
1044
|
+
USAGE
|
|
1045
|
+
emailr templates draft [options]
|
|
1046
|
+
|
|
1047
|
+
DESCRIPTION
|
|
1048
|
+
Creates a new HTML file with a starter template and starts a live
|
|
1049
|
+
preview server with hot-reload. Edit the file in your editor and
|
|
1050
|
+
see changes instantly in the browser. When done, create the template
|
|
1051
|
+
with the create command.
|
|
1052
|
+
|
|
1053
|
+
OPTIONS
|
|
1054
|
+
--file <file_path> Path to save the HTML file for drafting (default: ./new-template.html)
|
|
1055
|
+
--no-open Do not automatically open browser
|
|
1056
|
+
--blank Start with a blank file instead of starter template
|
|
1057
|
+
--foreground Keep process running in foreground (blocks terminal)
|
|
261
1058
|
|
|
262
|
-
WORKFLOW
|
|
263
|
-
1. Run: emailr templates draft --file ./new-template.html
|
|
264
|
-
2. Edit the file at ./new-template.html - browser auto-refreshes on save
|
|
265
|
-
3. When done: emailr templates create --name "My Template" --subject "Subject" --html-file ./new-template.html
|
|
266
|
-
4. Stop server: emailr templates stop-preview
|
|
1059
|
+
WORKFLOW
|
|
1060
|
+
1. Run: emailr templates draft --file ./new-template.html
|
|
1061
|
+
2. Edit the file at ./new-template.html - browser auto-refreshes on save
|
|
1062
|
+
3. When done: emailr templates create --name "My Template" --subject "Subject" --html-file ./new-template.html
|
|
1063
|
+
4. Stop server: emailr templates stop-preview
|
|
1064
|
+
|
|
1065
|
+
EXAMPLES
|
|
1066
|
+
# Start drafting with default file path
|
|
1067
|
+
emailr templates draft
|
|
1068
|
+
|
|
1069
|
+
# Specify custom file path
|
|
1070
|
+
emailr templates draft --file ./emails/new-welcome.html
|
|
1071
|
+
|
|
1072
|
+
# Start with blank file (no starter template)
|
|
1073
|
+
emailr templates draft --blank
|
|
1074
|
+
|
|
1075
|
+
# Run in foreground (Ctrl+C to stop)
|
|
1076
|
+
emailr templates draft --foreground
|
|
1077
|
+
|
|
1078
|
+
# Don't open browser automatically
|
|
1079
|
+
emailr templates draft --no-open
|
|
1080
|
+
|
|
1081
|
+
SEE ALSO
|
|
1082
|
+
emailr templates edit Edit an existing template with live preview
|
|
1083
|
+
emailr templates create Create the template from your draft
|
|
1084
|
+
emailr templates stop-preview Stop the background preview server`).option("--file <file_path>","Path to save the HTML file for drafting","./new-template.html").option("--no-open","Do not automatically open browser").option("--blank","Start with a blank file instead of starter template").option("--foreground","Keep process running in foreground (blocks terminal)").action(async t=>{try{let e=N.resolve(t.file),a;if(t.blank?a="":a=`<!DOCTYPE html>
|
|
267
1085
|
<html>
|
|
268
1086
|
<head>
|
|
269
1087
|
<meta charset="UTF-8">
|
|
@@ -298,13 +1116,13 @@ WORKFLOW:
|
|
|
298
1116
|
<p><a href="{{unsubscribe_link}}">Unsubscribe</a></p>
|
|
299
1117
|
</div>
|
|
300
1118
|
</body>
|
|
301
|
-
</html>`,
|
|
302
|
-
Live Preview: ${
|
|
1119
|
+
</html>`,v.writeFileSync(e,a,"utf-8"),console.log(`Template draft created: ${e}`),t.foreground){let y=`http://127.0.0.1:${await Y(e,()=>{console.log("File changed - browser refreshed");})}/`;if(console.log(`
|
|
1120
|
+
Live Preview: ${y}`),console.log(`File: ${e}`),console.log(`
|
|
303
1121
|
Watching for changes... Edit the file and see live updates.`),console.log(`
|
|
304
|
-
When done, create the template:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),t.open!==!1)try{await(await import('open')).default(
|
|
1122
|
+
When done, create the template:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),t.open!==!1)try{await(await import('open')).default(y);}catch{}process.on("SIGINT",async()=>{console.log(`
|
|
305
1123
|
|
|
306
|
-
Stopping live preview server...`),await
|
|
307
|
-
To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),process.exit(0);});return}let{spawn:
|
|
1124
|
+
Stopping live preview server...`),await Z(),console.log("Done."),console.log(`
|
|
1125
|
+
To create template: emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),process.exit(0);});return}let{spawn:r}=await import('child_process'),o=await import('os'),n=N.join(o.tmpdir(),"emailr-preview.pid");try{let h=v.readFileSync(n,"utf-8").trim();process.kill(parseInt(h,10),"SIGTERM");}catch{}let s=`
|
|
308
1126
|
const http = require('http');
|
|
309
1127
|
const fs = require('fs');
|
|
310
1128
|
const path = require('path');
|
|
@@ -344,61 +1162,1688 @@ To create template: emailr templates create --name "Template Name" --subject "Em
|
|
|
344
1162
|
});
|
|
345
1163
|
});
|
|
346
1164
|
process.on('SIGTERM', () => { try { fs.unlinkSync(pidFile); fs.unlinkSync(portFile); } catch {} process.exit(0); });
|
|
347
|
-
`,
|
|
348
|
-
Live Preview: ${
|
|
1165
|
+
`,p=r("node",["-e",s],{detached:!0,stdio:["ignore","pipe","ignore"]}),b="";await new Promise(h=>{p.stdout?.on("data",y=>{let O=y.toString().match(/PORT:(\d+)/);O&&(b=O[1],h());}),setTimeout(h,3e3);}),p.unref();let T=`http://127.0.0.1:${b}/`;if(console.log(`
|
|
1166
|
+
Live Preview: ${T}`),console.log(`File: ${e}`),console.log(`
|
|
349
1167
|
Edit the file and see live updates in the browser.`),console.log(`
|
|
350
|
-
When done:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),console.log(" emailr templates stop-preview"),t.open!==!1)try{await(await import('open')).default(x);}catch{}process.exit(0);}catch(e){l(e instanceof Error?e.message:"Failed to start draft session"),process.exit(1);}}),n.command("stop-preview").description("Stop any running background preview server").action(async()=>{let t=await import('os'),e=U.join(t.tmpdir(),"emailr-preview.pid");try{let o=E.readFileSync(e,"utf-8").trim();process.kill(parseInt(o,10),"SIGTERM"),E.unlinkSync(e),d("Preview server stopped");}catch{d("No preview server running");}}),n}function Ce(){let n=new Command("domains").description("Manage sending domains");return n.command("list").description("List all domains").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),i=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.list();if(t.format==="json")c(i,"json");else {let a=i.map(r=>({ID:r.id,Domain:r.domain,Status:r.status,DKIM:r.dkim_verified,SPF:r.spf_verified,DMARC:r.dmarc_verified,Created:r.created_at}));c(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list domains"),process.exit(1);}}),n.command("get <id>").description("Get a domain by ID").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.get(t);c(a,e.format);}catch(o){l(o instanceof Error?o.message:"Failed to get domain"),process.exit(1);}}),n.command("add <domain>").description("Add a new domain").option("--receiving-subdomain <subdomain>","Subdomain for receiving emails").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),i=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={domain:t};e.receivingSubdomain&&(a.receivingSubdomain=e.receivingSubdomain);let r=await i.domains.add(a);if(e.format==="json")c(r,"json");else {if(d(`Domain added: ${r.domain}`),p("Add the following DNS records to verify your domain:"),console.log(""),r.dns_records){let s=[{Type:"DKIM",...Z(r.dns_records.dkim)},{Type:"SPF",...Z(r.dns_records.spf)},{Type:"DMARC",...Z(r.dns_records.dmarc)}];c(s,"table");}console.log(""),p(`Run 'emailr domains verify ${r.id}' after adding DNS records`);}}catch(o){l(o instanceof Error?o.message:"Failed to add domain"),process.exit(1);}}),n.command("verify <id>").description("Verify a domain").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.verify(t);e.format==="json"?c(a,"json"):a.verified?d("Domain verified successfully!"):(p(`Domain status: ${a.status}`),a.dkim_status&&p(`DKIM status: ${a.dkim_status}`));}catch(o){l(o instanceof Error?o.message:"Failed to verify domain"),process.exit(1);}}),n.command("check-dns <id>").description("Check DNS records for a domain").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).domains.checkDns(t);if(e.format==="json")c(a,"json");else {let r=Object.entries(a).map(([s,f])=>({Record:s,Verified:f.verified,Expected:f.expected||"-",Found:f.found?.join(", ")||"-"}));c(r,"table");}}catch(o){l(o instanceof Error?o.message:"Failed to check DNS"),process.exit(1);}}),n.command("delete <id>").description("Delete a domain").action(async t=>{try{let e=m();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.delete(t),d(`Domain deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete domain"),process.exit(1);}}),n}function Z(n){return {"Record Type":n.type,Name:n.name,Value:n.value.length>50?n.value.substring(0,47)+"...":n.value,...n.priority!==void 0&&{Priority:n.priority}}}function je(){let n=new Command("config").description("Manage CLI configuration");return n.command("set <key> <value>").description("Set a configuration value").action(async(t,e)=>{try{let o=["api-key","base-url","format"],i=t.toLowerCase();o.includes(i)||(l(`Invalid config key: ${t}`),p(`Valid keys: ${o.join(", ")}`),process.exit(1));let r={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[i];i==="format"&&!["json","table"].includes(e)&&(l(`Invalid format value: ${e}`),p("Valid formats: json, table"),process.exit(1)),A({[r]:e}),d(`Configuration saved: ${t} = ${i==="api-key"?"***":e}`),p(`Config file: ${j()}`);}catch(o){l(o instanceof Error?o.message:"Failed to save configuration"),process.exit(1);}}),n.command("get <key>").description("Get a configuration value").action(async t=>{try{let e=["api-key","base-url","format"],o=t.toLowerCase();e.includes(o)||(l(`Invalid config key: ${t}`),p(`Valid keys: ${e.join(", ")}`),process.exit(1));let a={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[o],r=re(a);r?console.log(o==="api-key"?r.substring(0,8)+"..."+r.substring(r.length-4):r):p(`${t} is not set`);}catch(e){l(e instanceof Error?e.message:"Failed to get configuration"),process.exit(1);}}),n.command("list").description("List all configuration values").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),o={"api-key":e.apiKey?e.apiKey.substring(0,8)+"..."+e.apiKey.substring(e.apiKey.length-4):"(not set)","base-url":e.baseUrl||"(default)",format:e.format||"table"};if(t.format==="json")c(o,"json");else {let i=Object.entries(o).map(([a,r])=>({Key:a,Value:r}));c(i,"table");}console.log(""),p(`Config file: ${j()}`);}catch(e){e instanceof Error&&e.message.includes("No API key configured")?(p("No configuration found."),p("Run 'emailr config set api-key <your-api-key>' to get started.")):(l(e instanceof Error?e.message:"Failed to list configuration"),process.exit(1));}}),n.command("path").description("Show the configuration file path").action(()=>{console.log(j());}),n.command("init").description("Initialize configuration interactively").option("--api-key <key>","API key to use").option("--base-url <url>","Base URL for API").action(async t=>{try{t.apiKey?(A({apiKey:t.apiKey,baseUrl:t.baseUrl}),d("Configuration initialized!"),p(`Config file: ${j()}`)):(p("Initialize your Emailr CLI configuration:"),console.log(""),p("Run with --api-key flag:"),console.log(" emailr config init --api-key <your-api-key>"),console.log(""),p("Or set environment variable:"),console.log(" export EMAILR_API_KEY=<your-api-key>"));}catch(e){l(e instanceof Error?e.message:"Failed to initialize configuration"),process.exit(1);}}),n}function ke(){let n=new Command("broadcasts").description("Manage broadcast campaigns");return n.command("list").description("List all broadcasts").option("--status <status>","Filter by status (draft, scheduled, sending, sent)").option("--limit <number>","Number of broadcasts to return","20").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),i=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).broadcasts.list({status:t.status,limit:parseInt(t.limit)});if(t.format==="json")c(i,"json");else {if(i.length===0){console.log("No broadcasts found.");return}let a=i.map(r=>({ID:r.id,Name:r.name,Subject:r.subject.substring(0,30)+(r.subject.length>30?"...":""),Status:r.status,Recipients:r.total_recipients||0,Sent:r.sent_count||0,Created:new Date(r.created_at).toLocaleDateString()}));c(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list broadcasts"),process.exit(1);}}),n.command("get <id>").description("Get broadcast details").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.get(t);e.format==="json"?c(a,"json"):c({ID:a.id,Name:a.name,Subject:a.subject,"From Email":a.from_email,Status:a.status,"Total Recipients":a.total_recipients||0,"Sent Count":a.sent_count||0,Delivered:a.delivered_count||0,Opened:a.opened_count||0,Clicked:a.clicked_count||0,Bounced:a.bounced_count||0,"Scheduled At":a.scheduled_at||"N/A","Started At":a.started_at||"N/A","Completed At":a.completed_at||"N/A","Created At":a.created_at},"table");}catch(o){l(o instanceof Error?o.message:"Failed to get broadcast"),process.exit(1);}}),n.command("create").description("Create a new broadcast").requiredOption("--name <name>","Broadcast name").requiredOption("--subject <subject>","Email subject").requiredOption("--from <email>","Sender email address").option("--template <id>","Template ID to use").option("--segment <id>","Segment ID to target").option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>","Schedule time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),i=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).broadcasts.create({name:t.name,subject:t.subject,from_email:t.from,template_id:t.template,segment_id:t.segment,html_content:t.html,text_content:t.text,scheduled_at:t.schedule});t.format==="json"?c(i,"json"):(d("Broadcast created successfully!"),c({ID:i.id,Name:i.name,Status:i.status,"Scheduled At":i.scheduled_at||"Not scheduled"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create broadcast"),process.exit(1);}}),n.command("send <id>").description("Send a broadcast immediately").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.send(t);e.format==="json"?c(a,"json"):(d("Broadcast sent successfully!"),c({Success:a.success,Sent:a.sent,Total:a.total},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to send broadcast"),process.exit(1);}}),n.command("schedule <id>").description("Schedule a broadcast").requiredOption("--at <datetime>","Schedule time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.schedule(t,e.at);e.format==="json"?c(a,"json"):(d("Broadcast scheduled successfully!"),c({ID:a.id,Name:a.name,Status:a.status,"Scheduled At":a.scheduled_at},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to schedule broadcast"),process.exit(1);}}),n.command("cancel <id>").description("Cancel a scheduled broadcast").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.cancel(t);e.format==="json"?c(a,"json"):d("Broadcast cancelled successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to cancel broadcast"),process.exit(1);}}),n.command("delete <id>").description("Delete a broadcast").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).broadcasts.delete(t);e.format==="json"?c(a,"json"):d("Broadcast deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete broadcast"),process.exit(1);}}),n}function Ee(){let n=new Command("webhooks").description("Manage webhooks");return n.command("list").description("List all webhooks").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),i=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).webhooks.list();if(t.format==="json")c(i,"json");else {if(i.length===0){console.log("No webhooks found.");return}let a=i.map(r=>({ID:r.id,Name:r.name,URL:r.url.substring(0,40)+(r.url.length>40?"...":""),Events:r.events.join(", "),Active:r.active?"Yes":"No"}));c(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list webhooks"),process.exit(1);}}),n.command("get <id>").description("Get webhook details").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.get(t);e.format==="json"?c(a,"json"):c({ID:a.id,Name:a.name,URL:a.url,Events:a.events.join(", "),Active:a.active?"Yes":"No",Secret:a.secret,"Created At":a.created_at},"table");}catch(o){l(o instanceof Error?o.message:"Failed to get webhook"),process.exit(1);}}),n.command("create").description("Create a new webhook").requiredOption("--name <name>","Webhook name").requiredOption("--url <url>","Webhook URL").requiredOption("--events <events>","Events to subscribe to (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),i=t.events.split(",").map(r=>r.trim()),a=await o.webhooks.create({name:t.name,url:t.url,events:i});t.format==="json"?c(a,"json"):(d("Webhook created successfully!"),c({ID:a.id,Name:a.name,URL:a.url,Secret:a.secret},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create webhook"),process.exit(1);}}),n.command("update <id>").description("Update a webhook").option("--name <name>","New webhook name").option("--url <url>","New webhook URL").option("--events <events>","New events (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),i=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};e.name&&(a.name=e.name),e.url&&(a.url=e.url),e.events&&(a.events=e.events.split(",").map(s=>s.trim()));let r=await i.webhooks.update(t,a);e.format==="json"?c(r,"json"):(d("Webhook updated successfully!"),c({ID:r.id,Name:r.name,URL:r.url},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update webhook"),process.exit(1);}}),n.command("enable <id>").description("Enable a webhook").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.enable(t);e.format==="json"?c(a,"json"):d("Webhook enabled successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to enable webhook"),process.exit(1);}}),n.command("disable <id>").description("Disable a webhook").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.disable(t);e.format==="json"?c(a,"json"):d("Webhook disabled successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to disable webhook"),process.exit(1);}}),n.command("delete <id>").description("Delete a webhook").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).webhooks.delete(t);e.format==="json"?c(a,"json"):d("Webhook deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete webhook"),process.exit(1);}}),n}function Fe(){let n=new Command("segments").description("Manage contact segments");return n.command("list").description("List all segments").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),i=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).segments.list();if(t.format==="json")c(i,"json");else {if(i.length===0){console.log("No segments found.");return}let a=i.map(r=>({ID:r.id,Name:r.name,Description:(r.description||"").substring(0,30)+((r.description?.length||0)>30?"...":""),"Created At":new Date(r.created_at).toLocaleDateString()}));c(a,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list segments"),process.exit(1);}}),n.command("get <id>").description("Get segment details").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.get(t);e.format==="json"?c(a,"json"):c({ID:a.id,Name:a.name,Description:a.description||"N/A",Conditions:JSON.stringify(a.conditions),"Created At":a.created_at,"Updated At":a.updated_at},"table");}catch(o){l(o instanceof Error?o.message:"Failed to get segment"),process.exit(1);}}),n.command("create").description("Create a new segment").requiredOption("--name <name>","Segment name").requiredOption("--conditions <json>","Segment conditions as JSON").option("--description <description>","Segment description").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),o=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),i;try{i=JSON.parse(t.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let a=await o.segments.create({name:t.name,description:t.description,conditions:i});t.format==="json"?c(a,"json"):(d("Segment created successfully!"),c({ID:a.id,Name:a.name},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create segment"),process.exit(1);}}),n.command("update <id>").description("Update a segment").option("--name <name>","New segment name").option("--description <description>","New description").option("--conditions <json>","New conditions as JSON").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),i=new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}),a={};if(e.name&&(a.name=e.name),e.description&&(a.description=e.description),e.conditions)try{a.conditions=JSON.parse(e.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let r=await i.segments.update(t,a);e.format==="json"?c(r,"json"):(d("Segment updated successfully!"),c({ID:r.id,Name:r.name},"table"));}catch(o){l(o instanceof Error?o.message:"Failed to update segment"),process.exit(1);}}),n.command("delete <id>").description("Delete a segment").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.delete(t);e.format==="json"?c(a,"json"):d("Segment deleted successfully!");}catch(o){l(o instanceof Error?o.message:"Failed to delete segment"),process.exit(1);}}),n.command("count <id>").description("Get the number of contacts in a segment").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let o=m(),a=await new Emailr({apiKey:o.apiKey,baseUrl:o.baseUrl}).segments.getContactsCount(t);e.format==="json"?c(a,"json"):console.log(`Segment contains ${a.count} contacts.`);}catch(o){l(o instanceof Error?o.message:"Failed to get segment count"),process.exit(1);}}),n}function it(n){try{let t=n.startsWith("http")?n:`http://localhost${n}`,e=new URL(t);return {key:e.searchParams.get("key")??void 0,code:e.searchParams.get("code")??void 0,state:e.searchParams.get("state")??void 0,error:e.searchParams.get("error")??void 0,message:e.searchParams.get("message")??void 0}}catch{return {}}}function st(){return `<!DOCTYPE html>
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
<
|
|
390
|
-
<
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
1168
|
+
When done:`),console.log(` emailr templates create --name "Template Name" --subject "Email Subject" --html-file ${t.file}`),console.log(" emailr templates stop-preview"),t.open!==!1)try{await(await import('open')).default(T);}catch{}process.exit(0);}catch(e){l(e instanceof Error?e.message:"Failed to start draft session"),process.exit(1);}}),i.command("stop-preview").description(`Stop any running background preview server
|
|
1169
|
+
|
|
1170
|
+
USAGE
|
|
1171
|
+
emailr templates stop-preview
|
|
1172
|
+
|
|
1173
|
+
DESCRIPTION
|
|
1174
|
+
Stops the background preview server started by the edit, draft, or
|
|
1175
|
+
preview commands. Use this when you're done editing and want to
|
|
1176
|
+
clean up the background process.
|
|
1177
|
+
|
|
1178
|
+
EXAMPLES
|
|
1179
|
+
# Stop the preview server
|
|
1180
|
+
emailr templates stop-preview
|
|
1181
|
+
|
|
1182
|
+
SEE ALSO
|
|
1183
|
+
emailr templates edit Start live editing session
|
|
1184
|
+
emailr templates draft Start live drafting session
|
|
1185
|
+
emailr templates preview Preview a template in browser`).action(async()=>{let t=await import('os'),e=N.join(t.tmpdir(),"emailr-preview.pid");try{let a=v.readFileSync(e,"utf-8").trim();process.kill(parseInt(a,10),"SIGTERM"),v.unlinkSync(e),d("Preview server stopped");}catch{d("No preview server running");}}),i}function Ee(){let i=new Command("domains").description(`Manage sending domains
|
|
1186
|
+
|
|
1187
|
+
USAGE
|
|
1188
|
+
emailr domains <subcommand> [options]
|
|
1189
|
+
|
|
1190
|
+
DESCRIPTION
|
|
1191
|
+
Add, verify, and manage sending domains for email delivery. Domains must
|
|
1192
|
+
be verified before you can send emails from addresses on that domain.
|
|
1193
|
+
Verification requires adding DNS records to prove domain ownership.
|
|
1194
|
+
|
|
1195
|
+
SUBCOMMANDS
|
|
1196
|
+
list List all domains with verification status
|
|
1197
|
+
get <id> Get domain details including DNS records
|
|
1198
|
+
add <domain> Add a new domain and get required DNS records
|
|
1199
|
+
verify <id> Trigger verification check for a domain
|
|
1200
|
+
check-dns <id> Check current DNS record status
|
|
1201
|
+
delete <id> Delete a domain permanently
|
|
1202
|
+
|
|
1203
|
+
DOMAIN VERIFICATION WORKFLOW
|
|
1204
|
+
1. Add domain: emailr domains add example.com
|
|
1205
|
+
2. Configure DNS: Add the DKIM, SPF, and DMARC records shown
|
|
1206
|
+
3. Wait: DNS propagation can take up to 48 hours
|
|
1207
|
+
4. Verify: emailr domains verify <domain_id>
|
|
1208
|
+
5. Check status: emailr domains check-dns <domain_id>
|
|
1209
|
+
|
|
1210
|
+
DNS RECORD TYPES
|
|
1211
|
+
DKIM (DomainKeys Identified Mail):
|
|
1212
|
+
- Cryptographic signature for email authentication
|
|
1213
|
+
- Proves emails are authorized by domain owner
|
|
1214
|
+
- TXT record with public key
|
|
1215
|
+
|
|
1216
|
+
SPF (Sender Policy Framework):
|
|
1217
|
+
- Specifies which servers can send email for your domain
|
|
1218
|
+
- Prevents email spoofing
|
|
1219
|
+
- TXT record listing authorized senders
|
|
1220
|
+
|
|
1221
|
+
DMARC (Domain-based Message Authentication):
|
|
1222
|
+
- Policy for handling authentication failures
|
|
1223
|
+
- Specifies how receivers should treat failed emails
|
|
1224
|
+
- TXT record with policy settings
|
|
1225
|
+
|
|
1226
|
+
OPTIONS
|
|
1227
|
+
--format <format> Output format: json | table (default: table)
|
|
1228
|
+
|
|
1229
|
+
OUTPUT FORMATS
|
|
1230
|
+
--format json Machine-readable JSON output
|
|
1231
|
+
--format table Human-readable table output (default)
|
|
1232
|
+
|
|
1233
|
+
EXAMPLES
|
|
1234
|
+
# List all domains with verification status
|
|
1235
|
+
emailr domains list
|
|
1236
|
+
|
|
1237
|
+
# Add a new domain
|
|
1238
|
+
emailr domains add example.com
|
|
1239
|
+
|
|
1240
|
+
# Add domain with receiving subdomain
|
|
1241
|
+
emailr domains add example.com --receiving-subdomain inbound
|
|
1242
|
+
|
|
1243
|
+
# Check DNS record status
|
|
1244
|
+
emailr domains check-dns dom_abc123
|
|
1245
|
+
|
|
1246
|
+
# Verify domain after DNS configuration
|
|
1247
|
+
emailr domains verify dom_abc123
|
|
1248
|
+
|
|
1249
|
+
# Get domain details
|
|
1250
|
+
emailr domains get dom_abc123
|
|
1251
|
+
|
|
1252
|
+
# Delete a domain
|
|
1253
|
+
emailr domains delete dom_abc123
|
|
1254
|
+
|
|
1255
|
+
# Get JSON output for scripting
|
|
1256
|
+
emailr domains list --format json
|
|
1257
|
+
|
|
1258
|
+
VERIFICATION STATUS
|
|
1259
|
+
pending Domain added, awaiting DNS configuration
|
|
1260
|
+
verifying Verification in progress
|
|
1261
|
+
verified All DNS records verified, ready to send
|
|
1262
|
+
failed Verification failed, check DNS records
|
|
1263
|
+
|
|
1264
|
+
SEE ALSO
|
|
1265
|
+
emailr send Send emails using verified domains
|
|
1266
|
+
emailr webhooks Receive domain verification events`);return i.command("list").description(`List all domains
|
|
1267
|
+
|
|
1268
|
+
USAGE
|
|
1269
|
+
emailr domains list [options]
|
|
1270
|
+
|
|
1271
|
+
DESCRIPTION
|
|
1272
|
+
Retrieves a list of all domains configured for your account.
|
|
1273
|
+
Shows domain name, verification status, and individual DNS record status.
|
|
1274
|
+
|
|
1275
|
+
OPTIONS
|
|
1276
|
+
--format <format> Output format: json | table (default: table)
|
|
1277
|
+
|
|
1278
|
+
OUTPUT FORMATS
|
|
1279
|
+
--format json Machine-readable JSON array of domain objects
|
|
1280
|
+
--format table Human-readable table with columns: ID, Domain, Status, DKIM, SPF, DMARC, Created
|
|
1281
|
+
|
|
1282
|
+
EXAMPLES
|
|
1283
|
+
# List all domains
|
|
1284
|
+
emailr domains list
|
|
1285
|
+
|
|
1286
|
+
# Get JSON output for scripting
|
|
1287
|
+
emailr domains list --format json
|
|
1288
|
+
|
|
1289
|
+
# Filter verified domains with jq
|
|
1290
|
+
emailr domains list --format json | jq '.[] | select(.status == "verified")'
|
|
1291
|
+
|
|
1292
|
+
# Count domains
|
|
1293
|
+
emailr domains list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.list();if(t.format==="json")c(r,"json");else {let o=r.map(n=>({ID:n.id,Domain:n.domain,Status:n.status,DKIM:n.dkim_verified,SPF:n.spf_verified,DMARC:n.dmarc_verified,Created:n.created_at}));c(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list domains"),process.exit(1);}}),i.command("get <domain_id>").description(`Get domain details
|
|
1294
|
+
|
|
1295
|
+
USAGE
|
|
1296
|
+
emailr domains get <domain_id> [options]
|
|
1297
|
+
|
|
1298
|
+
DESCRIPTION
|
|
1299
|
+
Retrieves detailed information about a specific domain including
|
|
1300
|
+
verification status and DNS record configuration.
|
|
1301
|
+
|
|
1302
|
+
ARGUMENTS
|
|
1303
|
+
<domain_id> The unique domain identifier (e.g., dom_abc123)
|
|
1304
|
+
|
|
1305
|
+
OPTIONS
|
|
1306
|
+
--format <format> Output format: json | table (default: table)
|
|
1307
|
+
|
|
1308
|
+
OUTPUT FORMATS
|
|
1309
|
+
--format json Full domain object with all fields and DNS records
|
|
1310
|
+
--format table Summary table with domain details
|
|
1311
|
+
|
|
1312
|
+
EXAMPLES
|
|
1313
|
+
# Get domain details
|
|
1314
|
+
emailr domains get dom_abc123
|
|
1315
|
+
|
|
1316
|
+
# Get full domain data as JSON
|
|
1317
|
+
emailr domains get dom_abc123 --format json
|
|
1318
|
+
|
|
1319
|
+
# Extract DNS records
|
|
1320
|
+
emailr domains get dom_abc123 --format json | jq '.dns_records'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).domains.get(t);c(o,e.format);}catch(a){l(a instanceof Error?a.message:"Failed to get domain"),process.exit(1);}}),i.command("add <domain_name>").description(`Add a new domain
|
|
1321
|
+
|
|
1322
|
+
USAGE
|
|
1323
|
+
emailr domains add <domain_name> [options]
|
|
1324
|
+
|
|
1325
|
+
DESCRIPTION
|
|
1326
|
+
Adds a new sending domain and returns the DNS records required for
|
|
1327
|
+
verification. After adding, configure these records with your DNS
|
|
1328
|
+
provider, then run 'verify' to complete setup.
|
|
1329
|
+
|
|
1330
|
+
ARGUMENTS
|
|
1331
|
+
<domain_name> The domain to add (e.g., example.com)
|
|
1332
|
+
|
|
1333
|
+
OPTIONS
|
|
1334
|
+
--receiving-subdomain <subdomain> Subdomain for receiving emails (optional)
|
|
1335
|
+
--format <format> Output format: json | table (default: table)
|
|
1336
|
+
|
|
1337
|
+
DNS RECORDS RETURNED
|
|
1338
|
+
After adding a domain, you'll receive three DNS records to configure:
|
|
1339
|
+
|
|
1340
|
+
DKIM Record:
|
|
1341
|
+
Type: TXT
|
|
1342
|
+
Name: emailr._domainkey.example.com
|
|
1343
|
+
Value: v=DKIM1; k=rsa; p=<public_key>
|
|
1344
|
+
|
|
1345
|
+
SPF Record:
|
|
1346
|
+
Type: TXT
|
|
1347
|
+
Name: example.com
|
|
1348
|
+
Value: v=spf1 include:_spf.emailr.com ~all
|
|
1349
|
+
|
|
1350
|
+
DMARC Record:
|
|
1351
|
+
Type: TXT
|
|
1352
|
+
Name: _dmarc.example.com
|
|
1353
|
+
Value: v=DMARC1; p=none; rua=mailto:dmarc@example.com
|
|
1354
|
+
|
|
1355
|
+
OUTPUT FORMATS
|
|
1356
|
+
--format json Full domain object with DNS records
|
|
1357
|
+
--format table Summary with DNS records in table format
|
|
1358
|
+
|
|
1359
|
+
EXAMPLES
|
|
1360
|
+
# Add a domain
|
|
1361
|
+
emailr domains add example.com
|
|
1362
|
+
|
|
1363
|
+
# Add domain with receiving subdomain
|
|
1364
|
+
emailr domains add example.com --receiving-subdomain inbound
|
|
1365
|
+
|
|
1366
|
+
# Get JSON output with DNS records
|
|
1367
|
+
emailr domains add example.com --format json
|
|
1368
|
+
|
|
1369
|
+
NEXT STEPS
|
|
1370
|
+
1. Copy the DNS records shown in the output
|
|
1371
|
+
2. Add them to your DNS provider (Cloudflare, Route53, etc.)
|
|
1372
|
+
3. Wait for DNS propagation (up to 48 hours)
|
|
1373
|
+
4. Run: emailr domains verify <domain_id>`).option("--receiving-subdomain <subdomain>","Subdomain for receiving emails").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={domain:t};e.receivingSubdomain&&(o.receivingSubdomain=e.receivingSubdomain);let n=await r.domains.add(o);if(e.format==="json")c(n,"json");else {if(d(`Domain added: ${n.domain}`),u("Add the following DNS records to verify your domain:"),console.log(""),n.dns_records){let s=[{Type:"DKIM",...Q(n.dns_records.dkim)},{Type:"SPF",...Q(n.dns_records.spf)},{Type:"DMARC",...Q(n.dns_records.dmarc)}];c(s,"table");}console.log(""),u(`Run 'emailr domains verify ${n.id}' after adding DNS records`);}}catch(a){l(a instanceof Error?a.message:"Failed to add domain"),process.exit(1);}}),i.command("verify <domain_id>").description(`Verify a domain
|
|
1374
|
+
|
|
1375
|
+
USAGE
|
|
1376
|
+
emailr domains verify <domain_id> [options]
|
|
1377
|
+
|
|
1378
|
+
DESCRIPTION
|
|
1379
|
+
Triggers a verification check for a domain. This queries DNS to confirm
|
|
1380
|
+
that the required DKIM, SPF, and DMARC records are properly configured.
|
|
1381
|
+
Run this after adding DNS records to your domain.
|
|
1382
|
+
|
|
1383
|
+
ARGUMENTS
|
|
1384
|
+
<domain_id> The unique domain identifier (e.g., dom_abc123)
|
|
1385
|
+
|
|
1386
|
+
OPTIONS
|
|
1387
|
+
--format <format> Output format: json | table (default: table)
|
|
1388
|
+
|
|
1389
|
+
VERIFICATION PROCESS
|
|
1390
|
+
1. Emailr queries DNS for your domain's records
|
|
1391
|
+
2. Compares found records against expected values
|
|
1392
|
+
3. Updates verification status for each record type
|
|
1393
|
+
4. Domain is verified when all records pass
|
|
1394
|
+
|
|
1395
|
+
OUTPUT FORMATS
|
|
1396
|
+
--format json Verification result with status details
|
|
1397
|
+
--format table Human-readable verification status
|
|
1398
|
+
|
|
1399
|
+
EXAMPLES
|
|
1400
|
+
# Verify a domain
|
|
1401
|
+
emailr domains verify dom_abc123
|
|
1402
|
+
|
|
1403
|
+
# Get JSON verification result
|
|
1404
|
+
emailr domains verify dom_abc123 --format json
|
|
1405
|
+
|
|
1406
|
+
TROUBLESHOOTING
|
|
1407
|
+
If verification fails:
|
|
1408
|
+
1. Run 'check-dns' to see which records are missing
|
|
1409
|
+
2. Verify records are correctly configured with your DNS provider
|
|
1410
|
+
3. Wait for DNS propagation (can take up to 48 hours)
|
|
1411
|
+
4. Try verification again`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).domains.verify(t);e.format==="json"?c(o,"json"):o.verified?d("Domain verified successfully!"):(u(`Domain status: ${o.status}`),o.dkim_status&&u(`DKIM status: ${o.dkim_status}`));}catch(a){l(a instanceof Error?a.message:"Failed to verify domain"),process.exit(1);}}),i.command("check-dns <domain_id>").description(`Check DNS records for a domain
|
|
1412
|
+
|
|
1413
|
+
USAGE
|
|
1414
|
+
emailr domains check-dns <domain_id> [options]
|
|
1415
|
+
|
|
1416
|
+
DESCRIPTION
|
|
1417
|
+
Queries DNS to check the current status of DKIM, SPF, and DMARC records
|
|
1418
|
+
for a domain. Shows expected vs. found values to help troubleshoot
|
|
1419
|
+
verification issues.
|
|
1420
|
+
|
|
1421
|
+
ARGUMENTS
|
|
1422
|
+
<domain_id> The unique domain identifier (e.g., dom_abc123)
|
|
1423
|
+
|
|
1424
|
+
OPTIONS
|
|
1425
|
+
--format <format> Output format: json | table (default: table)
|
|
1426
|
+
|
|
1427
|
+
DNS RECORD STATUS
|
|
1428
|
+
For each record type (DKIM, SPF, DMARC), shows:
|
|
1429
|
+
- Verified: Whether the record matches expected value
|
|
1430
|
+
- Expected: The value Emailr expects to find
|
|
1431
|
+
- Found: The actual value(s) found in DNS
|
|
1432
|
+
|
|
1433
|
+
OUTPUT FORMATS
|
|
1434
|
+
--format json Detailed DNS check results as JSON
|
|
1435
|
+
--format table Table showing Record, Verified, Expected, Found
|
|
1436
|
+
|
|
1437
|
+
EXAMPLES
|
|
1438
|
+
# Check DNS records
|
|
1439
|
+
emailr domains check-dns dom_abc123
|
|
1440
|
+
|
|
1441
|
+
# Get JSON output for debugging
|
|
1442
|
+
emailr domains check-dns dom_abc123 --format json
|
|
1443
|
+
|
|
1444
|
+
# Check specific record with jq
|
|
1445
|
+
emailr domains check-dns dom_abc123 --format json | jq '.dkim'
|
|
1446
|
+
|
|
1447
|
+
COMMON ISSUES
|
|
1448
|
+
Record not found:
|
|
1449
|
+
- DNS propagation may still be in progress
|
|
1450
|
+
- Record may be misconfigured at DNS provider
|
|
1451
|
+
|
|
1452
|
+
Value mismatch:
|
|
1453
|
+
- Copy the exact value from 'add' command output
|
|
1454
|
+
- Ensure no extra spaces or characters
|
|
1455
|
+
|
|
1456
|
+
Multiple records found:
|
|
1457
|
+
- Remove duplicate TXT records
|
|
1458
|
+
- Keep only the Emailr-specific record`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).domains.checkDns(t);if(e.format==="json")c(o,"json");else {let n=Object.entries(o).map(([s,p])=>({Record:s,Verified:p.verified,Expected:p.expected||"-",Found:p.found?.join(", ")||"-"}));c(n,"table");}}catch(a){l(a instanceof Error?a.message:"Failed to check DNS"),process.exit(1);}}),i.command("delete <domain_id>").description(`Delete a domain
|
|
1459
|
+
|
|
1460
|
+
USAGE
|
|
1461
|
+
emailr domains delete <domain_id> [options]
|
|
1462
|
+
|
|
1463
|
+
DESCRIPTION
|
|
1464
|
+
Permanently deletes a domain from your account. This action cannot be
|
|
1465
|
+
undone. You will no longer be able to send emails from this domain.
|
|
1466
|
+
|
|
1467
|
+
ARGUMENTS
|
|
1468
|
+
<domain_id> The unique domain identifier (e.g., dom_abc123)
|
|
1469
|
+
|
|
1470
|
+
OPTIONS
|
|
1471
|
+
--format <format> Output format: json | table (default: table)
|
|
1472
|
+
|
|
1473
|
+
OUTPUT FORMATS
|
|
1474
|
+
--format json Returns: { success: boolean }
|
|
1475
|
+
--format table Human-readable success message
|
|
1476
|
+
|
|
1477
|
+
EXAMPLES
|
|
1478
|
+
# Delete a domain
|
|
1479
|
+
emailr domains delete dom_abc123
|
|
1480
|
+
|
|
1481
|
+
# Get JSON output
|
|
1482
|
+
emailr domains delete dom_abc123 --format json
|
|
1483
|
+
|
|
1484
|
+
WARNING
|
|
1485
|
+
This action is permanent and cannot be undone.
|
|
1486
|
+
- Emails from this domain will fail to send
|
|
1487
|
+
- DNS records can be removed from your DNS provider
|
|
1488
|
+
- Re-add the domain if you need to restore functionality`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m();await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).domains.delete(t),d(`Domain deleted: ${t}`);}catch(e){l(e instanceof Error?e.message:"Failed to delete domain"),process.exit(1);}}),i}function Q(i){return {"Record Type":i.type,Name:i.name,Value:i.value.length>50?i.value.substring(0,47)+"...":i.value,...i.priority!==void 0&&{Priority:i.priority}}}function Ne(){let i=new Command("config").description(`Manage CLI configuration
|
|
1489
|
+
|
|
1490
|
+
USAGE
|
|
1491
|
+
emailr config <subcommand> [options]
|
|
1492
|
+
|
|
1493
|
+
DESCRIPTION
|
|
1494
|
+
Configure the Emailr CLI with your API credentials and preferences.
|
|
1495
|
+
Settings can be stored in a config file or provided via environment
|
|
1496
|
+
variables. Environment variables take precedence over config file values.
|
|
1497
|
+
|
|
1498
|
+
SUBCOMMANDS
|
|
1499
|
+
set <key> <value> Set a configuration value
|
|
1500
|
+
get <key> Get a configuration value
|
|
1501
|
+
list List all configuration values
|
|
1502
|
+
path Show the configuration file path
|
|
1503
|
+
init Initialize configuration interactively
|
|
1504
|
+
|
|
1505
|
+
CONFIGURABLE SETTINGS
|
|
1506
|
+
api-key Your Emailr API key (required for API access)
|
|
1507
|
+
base-url Custom API base URL (default: https://api.emailr.dev)
|
|
1508
|
+
format Default output format: json | table (default: table)
|
|
1509
|
+
|
|
1510
|
+
ENVIRONMENT VARIABLES
|
|
1511
|
+
EMAILR_API_KEY API key (overrides config file api-key)
|
|
1512
|
+
EMAILR_BASE_URL Base URL (overrides config file base-url)
|
|
1513
|
+
|
|
1514
|
+
Environment variables take precedence over config file values, allowing
|
|
1515
|
+
you to override settings without modifying the config file.
|
|
1516
|
+
|
|
1517
|
+
CONFIG FILE LOCATION
|
|
1518
|
+
~/.emailr/config.json
|
|
1519
|
+
|
|
1520
|
+
The config file is created automatically when you run 'emailr config set'
|
|
1521
|
+
or 'emailr config init'. The file stores settings in JSON format.
|
|
1522
|
+
|
|
1523
|
+
OUTPUT FORMATS
|
|
1524
|
+
--format json Machine-readable JSON output (for list command)
|
|
1525
|
+
--format table Human-readable table output (default)
|
|
1526
|
+
|
|
1527
|
+
EXAMPLES
|
|
1528
|
+
# Set your API key
|
|
1529
|
+
emailr config set api-key re_abc123xyz
|
|
1530
|
+
|
|
1531
|
+
# Set a custom API base URL
|
|
1532
|
+
emailr config set base-url https://custom.api.example.com
|
|
1533
|
+
|
|
1534
|
+
# View current configuration
|
|
1535
|
+
emailr config list
|
|
1536
|
+
|
|
1537
|
+
# Get a specific setting
|
|
1538
|
+
emailr config get api-key
|
|
1539
|
+
|
|
1540
|
+
# Show config file location
|
|
1541
|
+
emailr config path
|
|
1542
|
+
|
|
1543
|
+
# Initialize with API key
|
|
1544
|
+
emailr config init --api-key re_abc123xyz
|
|
1545
|
+
|
|
1546
|
+
# Use environment variable instead of config file
|
|
1547
|
+
export EMAILR_API_KEY=re_abc123xyz
|
|
1548
|
+
emailr templates list
|
|
1549
|
+
|
|
1550
|
+
SEE ALSO
|
|
1551
|
+
emailr --help Show all available commands`);return i.command("set <key> <value>").description(`Set a configuration value
|
|
1552
|
+
|
|
1553
|
+
USAGE
|
|
1554
|
+
emailr config set <key> <value>
|
|
1555
|
+
|
|
1556
|
+
DESCRIPTION
|
|
1557
|
+
Stores a configuration value in the config file (~/.emailr/config.json).
|
|
1558
|
+
The config file is created automatically if it doesn't exist.
|
|
1559
|
+
|
|
1560
|
+
ARGUMENTS
|
|
1561
|
+
<key> Configuration key to set
|
|
1562
|
+
<value> Value to assign to the key
|
|
1563
|
+
|
|
1564
|
+
VALID KEYS
|
|
1565
|
+
api-key Your Emailr API key (masked in output for security)
|
|
1566
|
+
base-url Custom API base URL
|
|
1567
|
+
format Default output format: json | table
|
|
1568
|
+
|
|
1569
|
+
EXAMPLES
|
|
1570
|
+
# Set your API key
|
|
1571
|
+
emailr config set api-key re_abc123xyz
|
|
1572
|
+
|
|
1573
|
+
# Set a custom API base URL
|
|
1574
|
+
emailr config set base-url https://custom.api.example.com
|
|
1575
|
+
|
|
1576
|
+
# Set default output format to JSON
|
|
1577
|
+
emailr config set format json
|
|
1578
|
+
|
|
1579
|
+
NOTE
|
|
1580
|
+
Environment variables (EMAILR_API_KEY, EMAILR_BASE_URL) take precedence
|
|
1581
|
+
over config file values at runtime.`).action(async(t,e)=>{try{let a=["api-key","base-url","format"],r=t.toLowerCase();a.includes(r)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${a.join(", ")}`),process.exit(1));let n={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[r];r==="format"&&!["json","table"].includes(e)&&(l(`Invalid format value: ${e}`),u("Valid formats: json, table"),process.exit(1)),G({[n]:e}),d(`Configuration saved: ${t} = ${r==="api-key"?"***":e}`),u(`Config file: ${I()}`);}catch(a){l(a instanceof Error?a.message:"Failed to save configuration"),process.exit(1);}}),i.command("get <key>").description(`Get a configuration value
|
|
1582
|
+
|
|
1583
|
+
USAGE
|
|
1584
|
+
emailr config get <key>
|
|
1585
|
+
|
|
1586
|
+
DESCRIPTION
|
|
1587
|
+
Retrieves and displays a single configuration value from the config file.
|
|
1588
|
+
API keys are partially masked for security (shows first 8 and last 4 chars).
|
|
1589
|
+
|
|
1590
|
+
ARGUMENTS
|
|
1591
|
+
<key> Configuration key to retrieve
|
|
1592
|
+
|
|
1593
|
+
VALID KEYS
|
|
1594
|
+
api-key Your Emailr API key (partially masked in output)
|
|
1595
|
+
base-url Custom API base URL
|
|
1596
|
+
format Default output format
|
|
1597
|
+
|
|
1598
|
+
EXAMPLES
|
|
1599
|
+
# Get your API key (masked)
|
|
1600
|
+
emailr config get api-key
|
|
1601
|
+
|
|
1602
|
+
# Get the base URL
|
|
1603
|
+
emailr config get base-url
|
|
1604
|
+
|
|
1605
|
+
# Get default output format
|
|
1606
|
+
emailr config get format
|
|
1607
|
+
|
|
1608
|
+
SEE ALSO
|
|
1609
|
+
emailr config list Show all configuration values`).action(async t=>{try{let e=["api-key","base-url","format"],a=t.toLowerCase();e.includes(a)||(l(`Invalid config key: ${t}`),u(`Valid keys: ${e.join(", ")}`),process.exit(1));let o={"api-key":"apiKey","base-url":"baseUrl",format:"format"}[a],n=ne(o);n?console.log(a==="api-key"?n.substring(0,8)+"..."+n.substring(n.length-4):n):u(`${t} is not set`);}catch(e){l(e instanceof Error?e.message:"Failed to get configuration"),process.exit(1);}}),i.command("list").description(`List all configuration values
|
|
1610
|
+
|
|
1611
|
+
USAGE
|
|
1612
|
+
emailr config list [options]
|
|
1613
|
+
|
|
1614
|
+
DESCRIPTION
|
|
1615
|
+
Displays all configuration values from the config file. API keys are
|
|
1616
|
+
partially masked for security. Shows the config file location at the end.
|
|
1617
|
+
|
|
1618
|
+
OPTIONS
|
|
1619
|
+
--format <format> Output format: json | table (default: table)
|
|
1620
|
+
|
|
1621
|
+
OUTPUT FORMATS
|
|
1622
|
+
--format json Machine-readable JSON object with all settings
|
|
1623
|
+
--format table Human-readable table with Key and Value columns
|
|
1624
|
+
|
|
1625
|
+
EXAMPLES
|
|
1626
|
+
# List all settings in table format
|
|
1627
|
+
emailr config list
|
|
1628
|
+
|
|
1629
|
+
# List all settings as JSON
|
|
1630
|
+
emailr config list --format json
|
|
1631
|
+
|
|
1632
|
+
# Pipe JSON to jq for processing
|
|
1633
|
+
emailr config list --format json | jq '.["api-key"]'
|
|
1634
|
+
|
|
1635
|
+
SEE ALSO
|
|
1636
|
+
emailr config get Get a single configuration value
|
|
1637
|
+
emailr config path Show config file location`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),a={"api-key":e.apiKey?e.apiKey.substring(0,8)+"..."+e.apiKey.substring(e.apiKey.length-4):"(not set)","base-url":e.baseUrl||"(default)",format:e.format||"table"};if(t.format==="json")c(a,"json");else {let r=Object.entries(a).map(([o,n])=>({Key:o,Value:n}));c(r,"table");}console.log(""),u(`Config file: ${I()}`);}catch(e){e instanceof Error&&e.message.includes("No API key configured")?(u("No configuration found."),u("Run 'emailr config set api-key <your-api-key>' to get started.")):(l(e instanceof Error?e.message:"Failed to list configuration"),process.exit(1));}}),i.command("path").description(`Show the configuration file path
|
|
1638
|
+
|
|
1639
|
+
USAGE
|
|
1640
|
+
emailr config path
|
|
1641
|
+
|
|
1642
|
+
DESCRIPTION
|
|
1643
|
+
Displays the full path to the configuration file. Useful for debugging
|
|
1644
|
+
or when you need to manually edit or backup the config file.
|
|
1645
|
+
|
|
1646
|
+
CONFIG FILE LOCATION
|
|
1647
|
+
~/.emailr/config.json
|
|
1648
|
+
|
|
1649
|
+
The config file stores settings in JSON format:
|
|
1650
|
+
{
|
|
1651
|
+
"apiKey": "re_abc123...",
|
|
1652
|
+
"baseUrl": "https://api.emailr.dev",
|
|
1653
|
+
"format": "table"
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
EXAMPLES
|
|
1657
|
+
# Show config file path
|
|
1658
|
+
emailr config path
|
|
1659
|
+
|
|
1660
|
+
# Open config file in editor (macOS)
|
|
1661
|
+
open $(emailr config path)
|
|
1662
|
+
|
|
1663
|
+
# View config file contents
|
|
1664
|
+
cat $(emailr config path)`).action(()=>{console.log(I());}),i.command("init").description(`Initialize configuration interactively
|
|
1665
|
+
|
|
1666
|
+
USAGE
|
|
1667
|
+
emailr config init [options]
|
|
1668
|
+
|
|
1669
|
+
DESCRIPTION
|
|
1670
|
+
Sets up the CLI configuration with your API credentials. Creates the
|
|
1671
|
+
config file (~/.emailr/config.json) if it doesn't exist.
|
|
1672
|
+
|
|
1673
|
+
OPTIONS
|
|
1674
|
+
--api-key <api_key> API key to use
|
|
1675
|
+
--base-url <url> Base URL for API (optional)
|
|
1676
|
+
|
|
1677
|
+
EXAMPLES
|
|
1678
|
+
# Initialize with API key
|
|
1679
|
+
emailr config init --api-key re_abc123xyz
|
|
1680
|
+
|
|
1681
|
+
# Initialize with custom base URL
|
|
1682
|
+
emailr config init --api-key re_abc123xyz --base-url https://custom.api.example.com
|
|
1683
|
+
|
|
1684
|
+
# Show initialization instructions
|
|
1685
|
+
emailr config init
|
|
1686
|
+
|
|
1687
|
+
ALTERNATIVE: ENVIRONMENT VARIABLES
|
|
1688
|
+
Instead of using config init, you can set environment variables:
|
|
1689
|
+
|
|
1690
|
+
export EMAILR_API_KEY=re_abc123xyz
|
|
1691
|
+
export EMAILR_BASE_URL=https://api.emailr.dev # optional
|
|
1692
|
+
|
|
1693
|
+
Environment variables take precedence over config file values.
|
|
1694
|
+
|
|
1695
|
+
SEE ALSO
|
|
1696
|
+
emailr config set Set individual configuration values
|
|
1697
|
+
emailr config list View current configuration`).option("--api-key <key>","API key to use").option("--base-url <url>","Base URL for API").action(async t=>{try{t.apiKey?(G({apiKey:t.apiKey,baseUrl:t.baseUrl}),d("Configuration initialized!"),u(`Config file: ${I()}`)):(u("Initialize your Emailr CLI configuration:"),console.log(""),u("Run with --api-key flag:"),console.log(" emailr config init --api-key <your-api-key>"),console.log(""),u("Or set environment variable:"),console.log(" export EMAILR_API_KEY=<your-api-key>"));}catch(e){l(e instanceof Error?e.message:"Failed to initialize configuration"),process.exit(1);}}),i}function _e(){let i=new Command("broadcasts").description(`Manage broadcast campaigns
|
|
1698
|
+
|
|
1699
|
+
USAGE
|
|
1700
|
+
emailr broadcasts <subcommand> [options]
|
|
1701
|
+
|
|
1702
|
+
DESCRIPTION
|
|
1703
|
+
Create, schedule, send, and manage email broadcast campaigns. Broadcasts
|
|
1704
|
+
are bulk email sends to a group of contacts, typically defined by a segment.
|
|
1705
|
+
|
|
1706
|
+
BROADCASTS, TEMPLATES, AND SEGMENTS
|
|
1707
|
+
Broadcasts bring together templates and segments to send targeted emails:
|
|
1708
|
+
- Templates: Define the email content (HTML, subject, variables)
|
|
1709
|
+
- Segments: Define the audience (which contacts receive the email)
|
|
1710
|
+
- Broadcasts: Combine a template with a segment and handle delivery
|
|
1711
|
+
|
|
1712
|
+
A typical workflow:
|
|
1713
|
+
1. Create a template with your email content
|
|
1714
|
+
2. Create a segment to target specific contacts
|
|
1715
|
+
3. Create a broadcast linking the template and segment
|
|
1716
|
+
4. Send immediately or schedule for later
|
|
1717
|
+
|
|
1718
|
+
SUBCOMMANDS
|
|
1719
|
+
list List all broadcasts with optional status filter
|
|
1720
|
+
get <id> Get broadcast details and delivery statistics
|
|
1721
|
+
create Create a new broadcast campaign
|
|
1722
|
+
send <id> Send a broadcast immediately
|
|
1723
|
+
schedule <id> Schedule a broadcast for future delivery
|
|
1724
|
+
cancel <id> Cancel a scheduled broadcast
|
|
1725
|
+
delete <id> Delete a broadcast
|
|
1726
|
+
|
|
1727
|
+
BROADCAST STATUS VALUES
|
|
1728
|
+
draft Broadcast created but not yet sent or scheduled
|
|
1729
|
+
scheduled Broadcast scheduled for future delivery
|
|
1730
|
+
sending Broadcast is currently being sent
|
|
1731
|
+
sent Broadcast has been fully delivered
|
|
1732
|
+
|
|
1733
|
+
OPTIONS
|
|
1734
|
+
--format <format> Output format: json | table (default: table)
|
|
1735
|
+
|
|
1736
|
+
OUTPUT FORMATS
|
|
1737
|
+
--format json Machine-readable JSON output
|
|
1738
|
+
--format table Human-readable table output (default)
|
|
1739
|
+
|
|
1740
|
+
SCHEDULING OPTION
|
|
1741
|
+
--schedule <datetime> Schedule time in ISO 8601 format
|
|
1742
|
+
|
|
1743
|
+
ISO 8601 Datetime Format Examples:
|
|
1744
|
+
2024-12-25T10:00:00Z UTC time
|
|
1745
|
+
2024-12-25T10:00:00-05:00 Eastern Time (UTC-5)
|
|
1746
|
+
2024-12-25T10:00:00+01:00 Central European Time (UTC+1)
|
|
1747
|
+
2024-12-25T15:30:00Z Specific time with minutes
|
|
1748
|
+
|
|
1749
|
+
Note: Always include timezone offset or 'Z' for UTC to avoid ambiguity.
|
|
1750
|
+
|
|
1751
|
+
EXAMPLES
|
|
1752
|
+
# List all broadcasts
|
|
1753
|
+
emailr broadcasts list
|
|
1754
|
+
|
|
1755
|
+
# List only scheduled broadcasts
|
|
1756
|
+
emailr broadcasts list --status scheduled
|
|
1757
|
+
|
|
1758
|
+
# Get broadcast details with delivery stats
|
|
1759
|
+
emailr broadcasts get brd_abc123
|
|
1760
|
+
|
|
1761
|
+
# Create a broadcast using a template and segment
|
|
1762
|
+
emailr broadcasts create --name "Weekly Newsletter" \\
|
|
1763
|
+
--subject "This Week's Updates" \\
|
|
1764
|
+
--from "newsletter@example.com" \\
|
|
1765
|
+
--template tpl_abc123 \\
|
|
1766
|
+
--segment seg_xyz789
|
|
1767
|
+
|
|
1768
|
+
# Create a broadcast with inline HTML
|
|
1769
|
+
emailr broadcasts create --name "Flash Sale" \\
|
|
1770
|
+
--subject "24-Hour Flash Sale!" \\
|
|
1771
|
+
--from "sales@example.com" \\
|
|
1772
|
+
--segment seg_xyz789 \\
|
|
1773
|
+
--html "<h1>Flash Sale</h1><p>Don't miss out!</p>"
|
|
1774
|
+
|
|
1775
|
+
# Create and schedule a broadcast
|
|
1776
|
+
emailr broadcasts create --name "Holiday Promo" \\
|
|
1777
|
+
--subject "Holiday Special" \\
|
|
1778
|
+
--from "promo@example.com" \\
|
|
1779
|
+
--template tpl_abc123 \\
|
|
1780
|
+
--segment seg_xyz789 \\
|
|
1781
|
+
--schedule "2024-12-25T10:00:00Z"
|
|
1782
|
+
|
|
1783
|
+
# Send a draft broadcast immediately
|
|
1784
|
+
emailr broadcasts send brd_abc123
|
|
1785
|
+
|
|
1786
|
+
# Schedule an existing broadcast
|
|
1787
|
+
emailr broadcasts schedule brd_abc123 --at "2024-12-25T10:00:00Z"
|
|
1788
|
+
|
|
1789
|
+
# Cancel a scheduled broadcast
|
|
1790
|
+
emailr broadcasts cancel brd_abc123
|
|
1791
|
+
|
|
1792
|
+
# Delete a broadcast
|
|
1793
|
+
emailr broadcasts delete brd_abc123
|
|
1794
|
+
|
|
1795
|
+
# Get JSON output for scripting
|
|
1796
|
+
emailr broadcasts list --format json
|
|
1797
|
+
|
|
1798
|
+
SEE ALSO
|
|
1799
|
+
emailr templates Manage email templates
|
|
1800
|
+
emailr segments Manage contact segments
|
|
1801
|
+
emailr contacts Manage individual contacts`);return i.command("list").description(`List all broadcasts
|
|
1802
|
+
|
|
1803
|
+
USAGE
|
|
1804
|
+
emailr broadcasts list [options]
|
|
1805
|
+
|
|
1806
|
+
DESCRIPTION
|
|
1807
|
+
Retrieves a list of all broadcast campaigns. Supports filtering by status
|
|
1808
|
+
and limiting the number of results returned.
|
|
1809
|
+
|
|
1810
|
+
OPTIONS
|
|
1811
|
+
--status <status> Filter by status: draft | scheduled | sending | sent
|
|
1812
|
+
--limit <count> Number of broadcasts to return (default: 20)
|
|
1813
|
+
--format <format> Output format: json | table (default: table)
|
|
1814
|
+
|
|
1815
|
+
STATUS VALUES
|
|
1816
|
+
draft Broadcast created but not yet sent or scheduled
|
|
1817
|
+
scheduled Broadcast scheduled for future delivery
|
|
1818
|
+
sending Broadcast is currently being sent
|
|
1819
|
+
sent Broadcast has been fully delivered
|
|
1820
|
+
|
|
1821
|
+
OUTPUT FORMATS
|
|
1822
|
+
--format json Machine-readable JSON array of broadcast objects
|
|
1823
|
+
--format table Human-readable table with columns: ID, Name, Subject, Status, Recipients, Sent, Created
|
|
1824
|
+
|
|
1825
|
+
EXAMPLES
|
|
1826
|
+
# List all broadcasts
|
|
1827
|
+
emailr broadcasts list
|
|
1828
|
+
|
|
1829
|
+
# List only draft broadcasts
|
|
1830
|
+
emailr broadcasts list --status draft
|
|
1831
|
+
|
|
1832
|
+
# List only scheduled broadcasts
|
|
1833
|
+
emailr broadcasts list --status scheduled
|
|
1834
|
+
|
|
1835
|
+
# List sent broadcasts
|
|
1836
|
+
emailr broadcasts list --status sent
|
|
1837
|
+
|
|
1838
|
+
# List more broadcasts
|
|
1839
|
+
emailr broadcasts list --limit 50
|
|
1840
|
+
|
|
1841
|
+
# Get JSON output for scripting
|
|
1842
|
+
emailr broadcasts list --format json
|
|
1843
|
+
|
|
1844
|
+
# Pipe JSON to jq for processing
|
|
1845
|
+
emailr broadcasts list --format json | jq '.[] | select(.status == "scheduled")'`).option("--status <status>","Filter by status (draft, scheduled, sending, sent)").option("--limit <number>","Number of broadcasts to return","20").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).broadcasts.list({status:t.status,limit:parseInt(t.limit)});if(t.format==="json")c(r,"json");else {if(r.length===0){console.log("No broadcasts found.");return}let o=r.map(n=>({ID:n.id,Name:n.name,Subject:n.subject.substring(0,30)+(n.subject.length>30?"...":""),Status:n.status,Recipients:n.total_recipients||0,Sent:n.sent_count||0,Created:new Date(n.created_at).toLocaleDateString()}));c(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list broadcasts"),process.exit(1);}}),i.command("get <broadcast_id>").description(`Get broadcast details
|
|
1846
|
+
|
|
1847
|
+
USAGE
|
|
1848
|
+
emailr broadcasts get <broadcast_id> [options]
|
|
1849
|
+
|
|
1850
|
+
DESCRIPTION
|
|
1851
|
+
Retrieves detailed information about a specific broadcast including
|
|
1852
|
+
delivery statistics (sent, delivered, opened, clicked, bounced).
|
|
1853
|
+
|
|
1854
|
+
ARGUMENTS
|
|
1855
|
+
<broadcast_id> The unique broadcast identifier (e.g., brd_abc123)
|
|
1856
|
+
|
|
1857
|
+
OPTIONS
|
|
1858
|
+
--format <format> Output format: json | table (default: table)
|
|
1859
|
+
|
|
1860
|
+
OUTPUT FORMATS
|
|
1861
|
+
--format json Full broadcast object with all fields and statistics
|
|
1862
|
+
--format table Summary table with broadcast details and delivery stats
|
|
1863
|
+
|
|
1864
|
+
DELIVERY STATISTICS
|
|
1865
|
+
Total Recipients Number of contacts targeted by the broadcast
|
|
1866
|
+
Sent Count Number of emails sent
|
|
1867
|
+
Delivered Number of emails successfully delivered
|
|
1868
|
+
Opened Number of unique opens
|
|
1869
|
+
Clicked Number of unique clicks
|
|
1870
|
+
Bounced Number of bounced emails
|
|
1871
|
+
|
|
1872
|
+
EXAMPLES
|
|
1873
|
+
# Get broadcast details
|
|
1874
|
+
emailr broadcasts get brd_abc123
|
|
1875
|
+
|
|
1876
|
+
# Get full broadcast data as JSON
|
|
1877
|
+
emailr broadcasts get brd_abc123 --format json
|
|
1878
|
+
|
|
1879
|
+
# Pipe JSON to jq for processing
|
|
1880
|
+
emailr broadcasts get brd_abc123 --format json | jq '{sent: .sent_count, opened: .opened_count}'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.get(t);e.format==="json"?c(o,"json"):c({ID:o.id,Name:o.name,Subject:o.subject,"From Email":o.from_email,Status:o.status,"Total Recipients":o.total_recipients||0,"Sent Count":o.sent_count||0,Delivered:o.delivered_count||0,Opened:o.opened_count||0,Clicked:o.clicked_count||0,Bounced:o.bounced_count||0,"Scheduled At":o.scheduled_at||"N/A","Started At":o.started_at||"N/A","Completed At":o.completed_at||"N/A","Created At":o.created_at},"table");}catch(a){l(a instanceof Error?a.message:"Failed to get broadcast"),process.exit(1);}}),i.command("create").description(`Create a new broadcast
|
|
1881
|
+
|
|
1882
|
+
USAGE
|
|
1883
|
+
emailr broadcasts create --name <name> --subject <subject> --from <email> [options]
|
|
1884
|
+
|
|
1885
|
+
DESCRIPTION
|
|
1886
|
+
Creates a new broadcast campaign. You can use a template for content or
|
|
1887
|
+
provide inline HTML/text. Target recipients using a segment ID.
|
|
1888
|
+
|
|
1889
|
+
OPTIONS
|
|
1890
|
+
--name <broadcast_name> Broadcast name for internal reference (required)
|
|
1891
|
+
--subject <subject_line> Email subject line (required)
|
|
1892
|
+
--from <email_address> Sender email address (required)
|
|
1893
|
+
--template <template_id> Template ID to use for content
|
|
1894
|
+
--segment <segment_id> Segment ID to target recipients
|
|
1895
|
+
--html <html_content> Inline HTML content (alternative to --template)
|
|
1896
|
+
--text <text_content> Plain text content
|
|
1897
|
+
--schedule <datetime> Schedule time in ISO 8601 format
|
|
1898
|
+
--format <format> Output format: json | table (default: table)
|
|
1899
|
+
|
|
1900
|
+
CONTENT OPTIONS
|
|
1901
|
+
Use either --template OR --html/--text for content:
|
|
1902
|
+
- --template: Use a pre-built template (recommended for reusable content)
|
|
1903
|
+
- --html/--text: Provide inline content (good for one-off emails)
|
|
1904
|
+
|
|
1905
|
+
SCHEDULING OPTION
|
|
1906
|
+
--schedule <datetime> Schedule time in ISO 8601 format
|
|
1907
|
+
|
|
1908
|
+
ISO 8601 Datetime Format Examples:
|
|
1909
|
+
2024-12-25T10:00:00Z UTC time
|
|
1910
|
+
2024-12-25T10:00:00-05:00 Eastern Time (UTC-5)
|
|
1911
|
+
2024-12-25T10:00:00+01:00 Central European Time (UTC+1)
|
|
1912
|
+
|
|
1913
|
+
If not specified, broadcast is created as draft and can be sent later.
|
|
1914
|
+
|
|
1915
|
+
OUTPUT FORMATS
|
|
1916
|
+
--format json Full broadcast object with all fields
|
|
1917
|
+
--format table Summary table with ID, Name, Status, Scheduled At
|
|
1918
|
+
|
|
1919
|
+
EXAMPLES
|
|
1920
|
+
# Create broadcast with template and segment
|
|
1921
|
+
emailr broadcasts create --name "Weekly Newsletter" \\
|
|
1922
|
+
--subject "This Week's Updates" \\
|
|
1923
|
+
--from "newsletter@example.com" \\
|
|
1924
|
+
--template tpl_abc123 \\
|
|
1925
|
+
--segment seg_xyz789
|
|
1926
|
+
|
|
1927
|
+
# Create broadcast with inline HTML
|
|
1928
|
+
emailr broadcasts create --name "Flash Sale" \\
|
|
1929
|
+
--subject "24-Hour Flash Sale!" \\
|
|
1930
|
+
--from "sales@example.com" \\
|
|
1931
|
+
--segment seg_xyz789 \\
|
|
1932
|
+
--html "<h1>Flash Sale</h1><p>Don't miss out!</p>"
|
|
1933
|
+
|
|
1934
|
+
# Create and schedule broadcast for Christmas morning UTC
|
|
1935
|
+
emailr broadcasts create --name "Holiday Promo" \\
|
|
1936
|
+
--subject "Holiday Special" \\
|
|
1937
|
+
--from "promo@example.com" \\
|
|
1938
|
+
--template tpl_abc123 \\
|
|
1939
|
+
--segment seg_xyz789 \\
|
|
1940
|
+
--schedule "2024-12-25T10:00:00Z"
|
|
1941
|
+
|
|
1942
|
+
# Create broadcast scheduled for 9 AM Eastern
|
|
1943
|
+
emailr broadcasts create --name "Morning Update" \\
|
|
1944
|
+
--subject "Good Morning!" \\
|
|
1945
|
+
--from "updates@example.com" \\
|
|
1946
|
+
--template tpl_abc123 \\
|
|
1947
|
+
--segment seg_xyz789 \\
|
|
1948
|
+
--schedule "2024-06-15T09:00:00-04:00"
|
|
1949
|
+
|
|
1950
|
+
# Get JSON output for scripting
|
|
1951
|
+
emailr broadcasts create --name "Test" --subject "Test" \\
|
|
1952
|
+
--from "test@example.com" --html "<p>Test</p>" --format json
|
|
1953
|
+
|
|
1954
|
+
SEE ALSO
|
|
1955
|
+
emailr templates Create and manage email templates
|
|
1956
|
+
emailr segments Create and manage contact segments`).requiredOption("--name <name>","Broadcast name").requiredOption("--subject <subject>","Email subject").requiredOption("--from <email>","Sender email address").option("--template <id>","Template ID to use").option("--segment <id>","Segment ID to target").option("--html <html>","HTML content").option("--text <text>","Plain text content").option("--schedule <datetime>","Schedule time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).broadcasts.create({name:t.name,subject:t.subject,from_email:t.from,template_id:t.template,segment_id:t.segment,html_content:t.html,text_content:t.text,scheduled_at:t.schedule});t.format==="json"?c(r,"json"):(d("Broadcast created successfully!"),c({ID:r.id,Name:r.name,Status:r.status,"Scheduled At":r.scheduled_at||"Not scheduled"},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create broadcast"),process.exit(1);}}),i.command("send <broadcast_id>").description(`Send a broadcast immediately
|
|
1957
|
+
|
|
1958
|
+
USAGE
|
|
1959
|
+
emailr broadcasts send <broadcast_id> [options]
|
|
1960
|
+
|
|
1961
|
+
DESCRIPTION
|
|
1962
|
+
Sends a broadcast immediately to all recipients in the target segment.
|
|
1963
|
+
The broadcast must be in 'draft' or 'scheduled' status. Once sent,
|
|
1964
|
+
the status changes to 'sending' and then 'sent' when complete.
|
|
1965
|
+
|
|
1966
|
+
ARGUMENTS
|
|
1967
|
+
<broadcast_id> The unique broadcast identifier (e.g., brd_abc123)
|
|
1968
|
+
|
|
1969
|
+
OPTIONS
|
|
1970
|
+
--format <format> Output format: json | table (default: table)
|
|
1971
|
+
|
|
1972
|
+
OUTPUT FORMATS
|
|
1973
|
+
--format json Returns: { success: boolean, sent: number, total: number }
|
|
1974
|
+
--format table Human-readable summary with success status and counts
|
|
1975
|
+
|
|
1976
|
+
EXAMPLES
|
|
1977
|
+
# Send a broadcast immediately
|
|
1978
|
+
emailr broadcasts send brd_abc123
|
|
1979
|
+
|
|
1980
|
+
# Get JSON output for scripting
|
|
1981
|
+
emailr broadcasts send brd_abc123 --format json
|
|
1982
|
+
|
|
1983
|
+
# Extract sent count from JSON
|
|
1984
|
+
emailr broadcasts send brd_abc123 --format json | jq '.sent'
|
|
1985
|
+
|
|
1986
|
+
NOTE
|
|
1987
|
+
Sending is asynchronous. The command returns when sending starts,
|
|
1988
|
+
not when all emails are delivered. Use 'emailr broadcasts get' to
|
|
1989
|
+
check delivery progress.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.send(t);e.format==="json"?c(o,"json"):(d("Broadcast sent successfully!"),c({Success:o.success,Sent:o.sent,Total:o.total},"table"));}catch(a){l(a instanceof Error?a.message:"Failed to send broadcast"),process.exit(1);}}),i.command("schedule <broadcast_id>").description(`Schedule a broadcast for future delivery
|
|
1990
|
+
|
|
1991
|
+
USAGE
|
|
1992
|
+
emailr broadcasts schedule <broadcast_id> --at <datetime> [options]
|
|
1993
|
+
|
|
1994
|
+
DESCRIPTION
|
|
1995
|
+
Schedules a broadcast to be sent at a specific time. The broadcast
|
|
1996
|
+
must be in 'draft' status. After scheduling, the status changes to
|
|
1997
|
+
'scheduled'. Use 'cancel' to unschedule if needed.
|
|
1998
|
+
|
|
1999
|
+
ARGUMENTS
|
|
2000
|
+
<broadcast_id> The unique broadcast identifier (e.g., brd_abc123)
|
|
2001
|
+
|
|
2002
|
+
OPTIONS
|
|
2003
|
+
--at <datetime> Schedule time in ISO 8601 format (required)
|
|
2004
|
+
--format <format> Output format: json | table (default: table)
|
|
2005
|
+
|
|
2006
|
+
ISO 8601 DATETIME FORMAT
|
|
2007
|
+
The --at option requires a datetime in ISO 8601 format with timezone:
|
|
2008
|
+
|
|
2009
|
+
Format: YYYY-MM-DDTHH:MM:SS\xB1HH:MM or YYYY-MM-DDTHH:MM:SSZ
|
|
2010
|
+
|
|
2011
|
+
Examples:
|
|
2012
|
+
2024-12-25T10:00:00Z UTC time (Z = Zulu/UTC)
|
|
2013
|
+
2024-12-25T10:00:00-05:00 Eastern Standard Time (UTC-5)
|
|
2014
|
+
2024-12-25T10:00:00-04:00 Eastern Daylight Time (UTC-4)
|
|
2015
|
+
2024-12-25T10:00:00+01:00 Central European Time (UTC+1)
|
|
2016
|
+
2024-12-25T10:00:00+09:00 Japan Standard Time (UTC+9)
|
|
2017
|
+
2024-06-15T15:30:00Z Specific time with minutes
|
|
2018
|
+
|
|
2019
|
+
Always include timezone to avoid ambiguity.
|
|
2020
|
+
|
|
2021
|
+
OUTPUT FORMATS
|
|
2022
|
+
--format json Full broadcast object with scheduled_at field
|
|
2023
|
+
--format table Summary table with ID, Name, Status, Scheduled At
|
|
2024
|
+
|
|
2025
|
+
EXAMPLES
|
|
2026
|
+
# Schedule for Christmas morning UTC
|
|
2027
|
+
emailr broadcasts schedule brd_abc123 --at "2024-12-25T10:00:00Z"
|
|
2028
|
+
|
|
2029
|
+
# Schedule for 9 AM Eastern Time
|
|
2030
|
+
emailr broadcasts schedule brd_abc123 --at "2024-06-15T09:00:00-04:00"
|
|
2031
|
+
|
|
2032
|
+
# Schedule for noon Central European Time
|
|
2033
|
+
emailr broadcasts schedule brd_abc123 --at "2024-06-15T12:00:00+01:00"
|
|
2034
|
+
|
|
2035
|
+
# Get JSON output for scripting
|
|
2036
|
+
emailr broadcasts schedule brd_abc123 --at "2024-12-25T10:00:00Z" --format json
|
|
2037
|
+
|
|
2038
|
+
SEE ALSO
|
|
2039
|
+
emailr broadcasts cancel Cancel a scheduled broadcast`).requiredOption("--at <datetime>","Schedule time (ISO 8601)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.schedule(t,e.at);e.format==="json"?c(o,"json"):(d("Broadcast scheduled successfully!"),c({ID:o.id,Name:o.name,Status:o.status,"Scheduled At":o.scheduled_at},"table"));}catch(a){l(a instanceof Error?a.message:"Failed to schedule broadcast"),process.exit(1);}}),i.command("cancel <broadcast_id>").description(`Cancel a scheduled broadcast
|
|
2040
|
+
|
|
2041
|
+
USAGE
|
|
2042
|
+
emailr broadcasts cancel <broadcast_id> [options]
|
|
2043
|
+
|
|
2044
|
+
DESCRIPTION
|
|
2045
|
+
Cancels a scheduled broadcast, returning it to 'draft' status.
|
|
2046
|
+
The broadcast can then be rescheduled or sent immediately.
|
|
2047
|
+
Only broadcasts with 'scheduled' status can be cancelled.
|
|
2048
|
+
|
|
2049
|
+
ARGUMENTS
|
|
2050
|
+
<broadcast_id> The unique broadcast identifier (e.g., brd_abc123)
|
|
2051
|
+
|
|
2052
|
+
OPTIONS
|
|
2053
|
+
--format <format> Output format: json | table (default: table)
|
|
2054
|
+
|
|
2055
|
+
OUTPUT FORMATS
|
|
2056
|
+
--format json Returns: { success: boolean }
|
|
2057
|
+
--format table Human-readable success message
|
|
2058
|
+
|
|
2059
|
+
EXAMPLES
|
|
2060
|
+
# Cancel a scheduled broadcast
|
|
2061
|
+
emailr broadcasts cancel brd_abc123
|
|
2062
|
+
|
|
2063
|
+
# Get JSON output for scripting
|
|
2064
|
+
emailr broadcasts cancel brd_abc123 --format json
|
|
2065
|
+
|
|
2066
|
+
NOTE
|
|
2067
|
+
Broadcasts that are already 'sending' or 'sent' cannot be cancelled.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.cancel(t);e.format==="json"?c(o,"json"):d("Broadcast cancelled successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to cancel broadcast"),process.exit(1);}}),i.command("delete <broadcast_id>").description(`Delete a broadcast
|
|
2068
|
+
|
|
2069
|
+
USAGE
|
|
2070
|
+
emailr broadcasts delete <broadcast_id> [options]
|
|
2071
|
+
|
|
2072
|
+
DESCRIPTION
|
|
2073
|
+
Permanently deletes a broadcast. This action cannot be undone.
|
|
2074
|
+
Delivery statistics and history will be lost.
|
|
2075
|
+
|
|
2076
|
+
ARGUMENTS
|
|
2077
|
+
<broadcast_id> The unique broadcast identifier (e.g., brd_abc123)
|
|
2078
|
+
|
|
2079
|
+
OPTIONS
|
|
2080
|
+
--format <format> Output format: json | table (default: table)
|
|
2081
|
+
|
|
2082
|
+
OUTPUT FORMATS
|
|
2083
|
+
--format json Returns: { success: boolean }
|
|
2084
|
+
--format table Human-readable success message
|
|
2085
|
+
|
|
2086
|
+
EXAMPLES
|
|
2087
|
+
# Delete a broadcast
|
|
2088
|
+
emailr broadcasts delete brd_abc123
|
|
2089
|
+
|
|
2090
|
+
# Get JSON output for scripting
|
|
2091
|
+
emailr broadcasts delete brd_abc123 --format json
|
|
2092
|
+
|
|
2093
|
+
WARNING
|
|
2094
|
+
This action is permanent and cannot be undone.
|
|
2095
|
+
All delivery statistics will be lost.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).broadcasts.delete(t);e.format==="json"?c(o,"json"):d("Broadcast deleted successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to delete broadcast"),process.exit(1);}}),i}function Ie(){let i=new Command("webhooks").description(`Manage webhooks
|
|
2096
|
+
|
|
2097
|
+
USAGE
|
|
2098
|
+
emailr webhooks <subcommand> [options]
|
|
2099
|
+
|
|
2100
|
+
DESCRIPTION
|
|
2101
|
+
Create, update, and manage webhook endpoints for receiving real-time
|
|
2102
|
+
notifications about email and contact events. Webhooks allow you to
|
|
2103
|
+
integrate Emailr with your own systems and automate workflows.
|
|
2104
|
+
|
|
2105
|
+
SUBCOMMANDS
|
|
2106
|
+
list List all webhooks
|
|
2107
|
+
get <id> Get webhook details including secret
|
|
2108
|
+
create Create a new webhook endpoint
|
|
2109
|
+
update <id> Update webhook name, URL, or events
|
|
2110
|
+
enable <id> Enable a disabled webhook
|
|
2111
|
+
disable <id> Disable a webhook (stops event delivery)
|
|
2112
|
+
delete <id> Delete a webhook permanently
|
|
2113
|
+
|
|
2114
|
+
AVAILABLE EVENT TYPES
|
|
2115
|
+
Email Events:
|
|
2116
|
+
email.sent Email accepted for delivery
|
|
2117
|
+
email.delivered Email successfully delivered to recipient
|
|
2118
|
+
email.bounced Email permanently rejected by recipient server
|
|
2119
|
+
email.complained Recipient marked email as spam
|
|
2120
|
+
email.opened Recipient opened the email
|
|
2121
|
+
email.clicked Recipient clicked a link in the email
|
|
2122
|
+
email.failed Email delivery failed
|
|
2123
|
+
email.delivery_delayed Email delivery temporarily delayed
|
|
2124
|
+
|
|
2125
|
+
Contact Events:
|
|
2126
|
+
contact.created New contact added to audience
|
|
2127
|
+
contact.updated Contact information updated
|
|
2128
|
+
contact.deleted Contact removed from audience
|
|
2129
|
+
|
|
2130
|
+
Domain Events:
|
|
2131
|
+
domain.verified Domain identity verified
|
|
2132
|
+
domain.verification_failed Domain verification failed
|
|
2133
|
+
domain.mail_from_verified MAIL FROM domain verified
|
|
2134
|
+
domain.mail_from_failed MAIL FROM verification failed
|
|
2135
|
+
domain.deactivated Domain deactivated
|
|
2136
|
+
|
|
2137
|
+
OPTIONS
|
|
2138
|
+
--format <format> Output format: json | table (default: table)
|
|
2139
|
+
|
|
2140
|
+
OUTPUT FORMATS
|
|
2141
|
+
--format json Machine-readable JSON output
|
|
2142
|
+
--format table Human-readable table output (default)
|
|
2143
|
+
|
|
2144
|
+
EVENTS OPTION FORMAT
|
|
2145
|
+
The --events option accepts a comma-separated list of event types:
|
|
2146
|
+
|
|
2147
|
+
Single event:
|
|
2148
|
+
--events "email.sent"
|
|
2149
|
+
|
|
2150
|
+
Multiple events:
|
|
2151
|
+
--events "email.sent,email.delivered,email.bounced"
|
|
2152
|
+
|
|
2153
|
+
All email events:
|
|
2154
|
+
--events "email.sent,email.delivered,email.bounced,email.complained,email.opened,email.clicked,email.failed"
|
|
2155
|
+
|
|
2156
|
+
Contact events:
|
|
2157
|
+
--events "contact.created,contact.updated,contact.deleted"
|
|
2158
|
+
|
|
2159
|
+
Mixed events:
|
|
2160
|
+
--events "email.delivered,email.bounced,contact.created"
|
|
2161
|
+
|
|
2162
|
+
EXAMPLES
|
|
2163
|
+
# List all webhooks
|
|
2164
|
+
emailr webhooks list
|
|
2165
|
+
|
|
2166
|
+
# Get webhook details (includes secret for signature verification)
|
|
2167
|
+
emailr webhooks get whk_abc123
|
|
2168
|
+
|
|
2169
|
+
# Create webhook for email delivery events
|
|
2170
|
+
emailr webhooks create --name "Delivery Tracker" \\
|
|
2171
|
+
--url "https://example.com/webhooks/emailr" \\
|
|
2172
|
+
--events "email.sent,email.delivered,email.bounced"
|
|
2173
|
+
|
|
2174
|
+
# Create webhook for all email tracking events
|
|
2175
|
+
emailr webhooks create --name "Email Analytics" \\
|
|
2176
|
+
--url "https://example.com/webhooks/analytics" \\
|
|
2177
|
+
--events "email.delivered,email.opened,email.clicked,email.bounced"
|
|
2178
|
+
|
|
2179
|
+
# Create webhook for contact changes
|
|
2180
|
+
emailr webhooks create --name "CRM Sync" \\
|
|
2181
|
+
--url "https://example.com/webhooks/crm" \\
|
|
2182
|
+
--events "contact.created,contact.updated,contact.deleted"
|
|
2183
|
+
|
|
2184
|
+
# Update webhook URL
|
|
2185
|
+
emailr webhooks update whk_abc123 --url "https://new-url.com/webhook"
|
|
2186
|
+
|
|
2187
|
+
# Update webhook events
|
|
2188
|
+
emailr webhooks update whk_abc123 --events "email.sent,email.delivered"
|
|
2189
|
+
|
|
2190
|
+
# Disable webhook temporarily
|
|
2191
|
+
emailr webhooks disable whk_abc123
|
|
2192
|
+
|
|
2193
|
+
# Re-enable webhook
|
|
2194
|
+
emailr webhooks enable whk_abc123
|
|
2195
|
+
|
|
2196
|
+
# Delete webhook
|
|
2197
|
+
emailr webhooks delete whk_abc123
|
|
2198
|
+
|
|
2199
|
+
# Get JSON output for scripting
|
|
2200
|
+
emailr webhooks list --format json
|
|
2201
|
+
|
|
2202
|
+
WEBHOOK PAYLOAD
|
|
2203
|
+
Webhooks receive POST requests with JSON payloads:
|
|
2204
|
+
|
|
2205
|
+
{
|
|
2206
|
+
"type": "email.delivered",
|
|
2207
|
+
"created_at": "2024-02-22T23:41:12.126Z",
|
|
2208
|
+
"data": {
|
|
2209
|
+
"email_id": "em_abc123",
|
|
2210
|
+
"to": "recipient@example.com",
|
|
2211
|
+
"subject": "Your order confirmation"
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
SIGNATURE VERIFICATION
|
|
2216
|
+
Each webhook request includes an X-Emailr-Signature header.
|
|
2217
|
+
Use the webhook secret (shown in 'get' command) to verify signatures.
|
|
2218
|
+
|
|
2219
|
+
SEE ALSO
|
|
2220
|
+
emailr domains Manage sending domains
|
|
2221
|
+
emailr send Send emails directly`);return i.command("list").description(`List all webhooks
|
|
2222
|
+
|
|
2223
|
+
USAGE
|
|
2224
|
+
emailr webhooks list [options]
|
|
2225
|
+
|
|
2226
|
+
DESCRIPTION
|
|
2227
|
+
Retrieves a list of all webhook endpoints configured for your account.
|
|
2228
|
+
Shows webhook ID, name, URL, subscribed events, and active status.
|
|
2229
|
+
|
|
2230
|
+
OPTIONS
|
|
2231
|
+
--format <format> Output format: json | table (default: table)
|
|
2232
|
+
|
|
2233
|
+
OUTPUT FORMATS
|
|
2234
|
+
--format json Machine-readable JSON array of webhook objects
|
|
2235
|
+
--format table Human-readable table with columns: ID, Name, URL, Events, Active
|
|
2236
|
+
|
|
2237
|
+
EXAMPLES
|
|
2238
|
+
# List all webhooks
|
|
2239
|
+
emailr webhooks list
|
|
2240
|
+
|
|
2241
|
+
# Get JSON output for scripting
|
|
2242
|
+
emailr webhooks list --format json
|
|
2243
|
+
|
|
2244
|
+
# Filter active webhooks with jq
|
|
2245
|
+
emailr webhooks list --format json | jq '.[] | select(.active == true)'
|
|
2246
|
+
|
|
2247
|
+
# Count webhooks
|
|
2248
|
+
emailr webhooks list --format json | jq 'length'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).webhooks.list();if(t.format==="json")c(r,"json");else {if(r.data.length===0){console.log("No webhooks found.");return}let o=r.data.map(n=>({ID:n.id,Name:n.name,URL:n.url.substring(0,40)+(n.url.length>40?"...":""),Events:n.events.join(", "),Active:n.active?"Yes":"No"}));c(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list webhooks"),process.exit(1);}}),i.command("get <webhook_id>").description(`Get webhook details
|
|
2249
|
+
|
|
2250
|
+
USAGE
|
|
2251
|
+
emailr webhooks get <webhook_id> [options]
|
|
2252
|
+
|
|
2253
|
+
DESCRIPTION
|
|
2254
|
+
Retrieves detailed information about a specific webhook including
|
|
2255
|
+
the webhook secret used for signature verification.
|
|
2256
|
+
|
|
2257
|
+
ARGUMENTS
|
|
2258
|
+
<webhook_id> The unique webhook identifier (e.g., whk_abc123)
|
|
2259
|
+
|
|
2260
|
+
OPTIONS
|
|
2261
|
+
--format <format> Output format: json | table (default: table)
|
|
2262
|
+
|
|
2263
|
+
OUTPUT FORMATS
|
|
2264
|
+
--format json Full webhook object with all fields including secret
|
|
2265
|
+
--format table Summary table with webhook details
|
|
2266
|
+
|
|
2267
|
+
WEBHOOK SECRET
|
|
2268
|
+
The secret is used to verify webhook signatures. Each webhook request
|
|
2269
|
+
includes an X-Emailr-Signature header that you can verify using this secret.
|
|
2270
|
+
|
|
2271
|
+
EXAMPLES
|
|
2272
|
+
# Get webhook details
|
|
2273
|
+
emailr webhooks get whk_abc123
|
|
2274
|
+
|
|
2275
|
+
# Get full webhook data as JSON
|
|
2276
|
+
emailr webhooks get whk_abc123 --format json
|
|
2277
|
+
|
|
2278
|
+
# Extract just the secret
|
|
2279
|
+
emailr webhooks get whk_abc123 --format json | jq -r '.secret'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).webhooks.get(t);e.format==="json"?c(o,"json"):c({ID:o.id,Name:o.name,URL:o.url,Events:o.events.join(", "),Active:o.active?"Yes":"No",Secret:o.secret,"Created At":o.created_at},"table");}catch(a){l(a instanceof Error?a.message:"Failed to get webhook"),process.exit(1);}}),i.command("create").description(`Create a new webhook
|
|
2280
|
+
|
|
2281
|
+
USAGE
|
|
2282
|
+
emailr webhooks create --name <name> --url <url> --events <events> [options]
|
|
2283
|
+
|
|
2284
|
+
DESCRIPTION
|
|
2285
|
+
Creates a new webhook endpoint to receive real-time notifications
|
|
2286
|
+
about email and contact events. The webhook is enabled by default.
|
|
2287
|
+
|
|
2288
|
+
OPTIONS
|
|
2289
|
+
--name <webhook_name> Webhook name for identification (required)
|
|
2290
|
+
--url <endpoint_url> HTTPS URL to receive webhook POST requests (required)
|
|
2291
|
+
--events <event_list> Comma-separated list of event types (required)
|
|
2292
|
+
--format <format> Output format: json | table (default: table)
|
|
2293
|
+
|
|
2294
|
+
AVAILABLE EVENT TYPES
|
|
2295
|
+
Email Events:
|
|
2296
|
+
email.sent Email accepted for delivery
|
|
2297
|
+
email.delivered Email successfully delivered
|
|
2298
|
+
email.bounced Email permanently rejected
|
|
2299
|
+
email.complained Recipient marked as spam
|
|
2300
|
+
email.opened Recipient opened email
|
|
2301
|
+
email.clicked Recipient clicked link
|
|
2302
|
+
email.failed Delivery failed
|
|
2303
|
+
email.delivery_delayed Delivery temporarily delayed
|
|
2304
|
+
|
|
2305
|
+
Contact Events:
|
|
2306
|
+
contact.created New contact added
|
|
2307
|
+
contact.updated Contact updated
|
|
2308
|
+
contact.deleted Contact removed
|
|
2309
|
+
|
|
2310
|
+
Domain Events:
|
|
2311
|
+
domain.verified Domain verified
|
|
2312
|
+
domain.verification_failed Verification failed
|
|
2313
|
+
domain.mail_from_verified MAIL FROM verified
|
|
2314
|
+
domain.mail_from_failed MAIL FROM failed
|
|
2315
|
+
domain.deactivated Domain deactivated
|
|
2316
|
+
|
|
2317
|
+
EVENTS FORMAT
|
|
2318
|
+
Provide events as a comma-separated list (no spaces after commas):
|
|
2319
|
+
--events "email.sent,email.delivered,email.bounced"
|
|
2320
|
+
|
|
2321
|
+
OUTPUT FORMATS
|
|
2322
|
+
--format json Full webhook object including generated secret
|
|
2323
|
+
--format table Summary with ID, Name, URL, and Secret
|
|
2324
|
+
|
|
2325
|
+
EXAMPLES
|
|
2326
|
+
# Create webhook for delivery tracking
|
|
2327
|
+
emailr webhooks create --name "Delivery Tracker" \\
|
|
2328
|
+
--url "https://example.com/webhooks/emailr" \\
|
|
2329
|
+
--events "email.sent,email.delivered,email.bounced"
|
|
2330
|
+
|
|
2331
|
+
# Create webhook for email analytics
|
|
2332
|
+
emailr webhooks create --name "Analytics" \\
|
|
2333
|
+
--url "https://analytics.example.com/hook" \\
|
|
2334
|
+
--events "email.delivered,email.opened,email.clicked"
|
|
2335
|
+
|
|
2336
|
+
# Create webhook for CRM integration
|
|
2337
|
+
emailr webhooks create --name "CRM Sync" \\
|
|
2338
|
+
--url "https://crm.example.com/emailr" \\
|
|
2339
|
+
--events "contact.created,contact.updated,contact.deleted"
|
|
2340
|
+
|
|
2341
|
+
# Create webhook for bounce handling
|
|
2342
|
+
emailr webhooks create --name "Bounce Handler" \\
|
|
2343
|
+
--url "https://example.com/bounces" \\
|
|
2344
|
+
--events "email.bounced,email.complained"
|
|
2345
|
+
|
|
2346
|
+
# Get JSON output with secret
|
|
2347
|
+
emailr webhooks create --name "Test" \\
|
|
2348
|
+
--url "https://example.com/test" \\
|
|
2349
|
+
--events "email.sent" --format json
|
|
2350
|
+
|
|
2351
|
+
NOTE
|
|
2352
|
+
Save the webhook secret returned by this command. You'll need it
|
|
2353
|
+
to verify webhook signatures in your endpoint.`).requiredOption("--name <name>","Webhook name").requiredOption("--url <url>","Webhook URL").requiredOption("--events <events>","Events to subscribe to (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r=t.events.split(",").map(n=>n.trim()),o=await a.webhooks.create({name:t.name,url:t.url,events:r});t.format==="json"?c(o,"json"):(d("Webhook created successfully!"),c({ID:o.id,Name:o.name,URL:o.url,Secret:o.secret},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create webhook"),process.exit(1);}}),i.command("update <webhook_id>").description(`Update a webhook
|
|
2354
|
+
|
|
2355
|
+
USAGE
|
|
2356
|
+
emailr webhooks update <webhook_id> [options]
|
|
2357
|
+
|
|
2358
|
+
DESCRIPTION
|
|
2359
|
+
Updates an existing webhook's name, URL, or subscribed events.
|
|
2360
|
+
Only provide the options you want to change.
|
|
2361
|
+
|
|
2362
|
+
ARGUMENTS
|
|
2363
|
+
<webhook_id> The unique webhook identifier (e.g., whk_abc123)
|
|
2364
|
+
|
|
2365
|
+
OPTIONS
|
|
2366
|
+
--name <webhook_name> New webhook name
|
|
2367
|
+
--url <endpoint_url> New webhook URL
|
|
2368
|
+
--events <event_list> New comma-separated list of events (replaces existing)
|
|
2369
|
+
--format <format> Output format: json | table (default: table)
|
|
2370
|
+
|
|
2371
|
+
EVENTS FORMAT
|
|
2372
|
+
The --events option replaces all existing events:
|
|
2373
|
+
--events "email.sent,email.delivered"
|
|
2374
|
+
|
|
2375
|
+
To add events, first get current events, then include all in update.
|
|
2376
|
+
|
|
2377
|
+
OUTPUT FORMATS
|
|
2378
|
+
--format json Updated webhook object
|
|
2379
|
+
--format table Summary with ID, Name, URL
|
|
2380
|
+
|
|
2381
|
+
EXAMPLES
|
|
2382
|
+
# Update webhook name
|
|
2383
|
+
emailr webhooks update whk_abc123 --name "New Name"
|
|
2384
|
+
|
|
2385
|
+
# Update webhook URL
|
|
2386
|
+
emailr webhooks update whk_abc123 --url "https://new-url.com/webhook"
|
|
2387
|
+
|
|
2388
|
+
# Update subscribed events
|
|
2389
|
+
emailr webhooks update whk_abc123 --events "email.sent,email.delivered,email.bounced"
|
|
2390
|
+
|
|
2391
|
+
# Update multiple fields
|
|
2392
|
+
emailr webhooks update whk_abc123 \\
|
|
2393
|
+
--name "Updated Webhook" \\
|
|
2394
|
+
--url "https://new-url.com/hook" \\
|
|
2395
|
+
--events "email.delivered,email.bounced"
|
|
2396
|
+
|
|
2397
|
+
# Get JSON output
|
|
2398
|
+
emailr webhooks update whk_abc123 --name "New Name" --format json`).option("--name <name>","New webhook name").option("--url <url>","New webhook URL").option("--events <events>","New events (comma-separated)").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={};e.name&&(o.name=e.name),e.url&&(o.url=e.url),e.events&&(o.events=e.events.split(",").map(s=>s.trim()));let n=await r.webhooks.update(t,o);e.format==="json"?c(n,"json"):(d("Webhook updated successfully!"),c({ID:n.id,Name:n.name,URL:n.url},"table"));}catch(a){l(a instanceof Error?a.message:"Failed to update webhook"),process.exit(1);}}),i.command("enable <webhook_id>").description(`Enable a webhook
|
|
2399
|
+
|
|
2400
|
+
USAGE
|
|
2401
|
+
emailr webhooks enable <webhook_id> [options]
|
|
2402
|
+
|
|
2403
|
+
DESCRIPTION
|
|
2404
|
+
Enables a disabled webhook to resume receiving event notifications.
|
|
2405
|
+
Use this after fixing issues with your webhook endpoint.
|
|
2406
|
+
|
|
2407
|
+
ARGUMENTS
|
|
2408
|
+
<webhook_id> The unique webhook identifier (e.g., whk_abc123)
|
|
2409
|
+
|
|
2410
|
+
OPTIONS
|
|
2411
|
+
--format <format> Output format: json | table (default: table)
|
|
2412
|
+
|
|
2413
|
+
OUTPUT FORMATS
|
|
2414
|
+
--format json Returns: { success: boolean, active: boolean }
|
|
2415
|
+
--format table Human-readable success message
|
|
2416
|
+
|
|
2417
|
+
EXAMPLES
|
|
2418
|
+
# Enable a webhook
|
|
2419
|
+
emailr webhooks enable whk_abc123
|
|
2420
|
+
|
|
2421
|
+
# Get JSON output
|
|
2422
|
+
emailr webhooks enable whk_abc123 --format json`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).webhooks.enable(t);e.format==="json"?c(o,"json"):d("Webhook enabled successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to enable webhook"),process.exit(1);}}),i.command("disable <webhook_id>").description(`Disable a webhook
|
|
2423
|
+
|
|
2424
|
+
USAGE
|
|
2425
|
+
emailr webhooks disable <webhook_id> [options]
|
|
2426
|
+
|
|
2427
|
+
DESCRIPTION
|
|
2428
|
+
Disables a webhook to temporarily stop receiving event notifications.
|
|
2429
|
+
Events that occur while disabled are not queued or retried.
|
|
2430
|
+
Use 'enable' to resume receiving events.
|
|
2431
|
+
|
|
2432
|
+
ARGUMENTS
|
|
2433
|
+
<webhook_id> The unique webhook identifier (e.g., whk_abc123)
|
|
2434
|
+
|
|
2435
|
+
OPTIONS
|
|
2436
|
+
--format <format> Output format: json | table (default: table)
|
|
2437
|
+
|
|
2438
|
+
OUTPUT FORMATS
|
|
2439
|
+
--format json Returns: { success: boolean, active: boolean }
|
|
2440
|
+
--format table Human-readable success message
|
|
2441
|
+
|
|
2442
|
+
EXAMPLES
|
|
2443
|
+
# Disable a webhook
|
|
2444
|
+
emailr webhooks disable whk_abc123
|
|
2445
|
+
|
|
2446
|
+
# Get JSON output
|
|
2447
|
+
emailr webhooks disable whk_abc123 --format json
|
|
2448
|
+
|
|
2449
|
+
NOTE
|
|
2450
|
+
Events are not queued while webhook is disabled. If you need to
|
|
2451
|
+
temporarily stop processing, consider handling this in your endpoint.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).webhooks.disable(t);e.format==="json"?c(o,"json"):d("Webhook disabled successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to disable webhook"),process.exit(1);}}),i.command("delete <webhook_id>").description(`Delete a webhook
|
|
2452
|
+
|
|
2453
|
+
USAGE
|
|
2454
|
+
emailr webhooks delete <webhook_id> [options]
|
|
2455
|
+
|
|
2456
|
+
DESCRIPTION
|
|
2457
|
+
Permanently deletes a webhook endpoint. This action cannot be undone.
|
|
2458
|
+
The webhook will immediately stop receiving events.
|
|
2459
|
+
|
|
2460
|
+
ARGUMENTS
|
|
2461
|
+
<webhook_id> The unique webhook identifier (e.g., whk_abc123)
|
|
2462
|
+
|
|
2463
|
+
OPTIONS
|
|
2464
|
+
--format <format> Output format: json | table (default: table)
|
|
2465
|
+
|
|
2466
|
+
OUTPUT FORMATS
|
|
2467
|
+
--format json Returns: { success: boolean }
|
|
2468
|
+
--format table Human-readable success message
|
|
2469
|
+
|
|
2470
|
+
EXAMPLES
|
|
2471
|
+
# Delete a webhook
|
|
2472
|
+
emailr webhooks delete whk_abc123
|
|
2473
|
+
|
|
2474
|
+
# Get JSON output
|
|
2475
|
+
emailr webhooks delete whk_abc123 --format json
|
|
2476
|
+
|
|
2477
|
+
WARNING
|
|
2478
|
+
This action is permanent and cannot be undone.
|
|
2479
|
+
Create a new webhook if you need to restore functionality.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).webhooks.delete(t);e.format==="json"?c(o,"json"):d("Webhook deleted successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to delete webhook"),process.exit(1);}}),i}function ke(){let i=new Command("segments").description(`Manage contact segments
|
|
2480
|
+
|
|
2481
|
+
USAGE
|
|
2482
|
+
emailr segments <subcommand> [options]
|
|
2483
|
+
|
|
2484
|
+
DESCRIPTION
|
|
2485
|
+
Create, update, list, and manage contact segments. Segments are dynamic
|
|
2486
|
+
groups of contacts defined by conditions. Use segments to target specific
|
|
2487
|
+
audiences for broadcasts and campaigns.
|
|
2488
|
+
|
|
2489
|
+
SUBCOMMANDS
|
|
2490
|
+
list List all segments
|
|
2491
|
+
get <id> Get a segment by ID
|
|
2492
|
+
create Create a new segment with conditions
|
|
2493
|
+
update <id> Update an existing segment
|
|
2494
|
+
delete <id> Delete a segment
|
|
2495
|
+
count <id> Get the number of contacts in a segment
|
|
2496
|
+
|
|
2497
|
+
OPTIONS
|
|
2498
|
+
--format <format> Output format: json | table (default: table)
|
|
2499
|
+
|
|
2500
|
+
OUTPUT FORMATS
|
|
2501
|
+
--format json Machine-readable JSON output
|
|
2502
|
+
--format table Human-readable table output (default)
|
|
2503
|
+
|
|
2504
|
+
CONDITIONS OPTION
|
|
2505
|
+
--conditions <json> Segment conditions as JSON array
|
|
2506
|
+
|
|
2507
|
+
Conditions define which contacts belong to a segment. Each condition
|
|
2508
|
+
specifies a field, operator, and value to match against.
|
|
2509
|
+
|
|
2510
|
+
Condition Structure:
|
|
2511
|
+
{
|
|
2512
|
+
"field": "<field_name>",
|
|
2513
|
+
"operator": "<operator>",
|
|
2514
|
+
"value": "<value>"
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
Available Fields:
|
|
2518
|
+
- email Contact email address
|
|
2519
|
+
- first_name Contact first name
|
|
2520
|
+
- last_name Contact last name
|
|
2521
|
+
- subscribed Subscription status (true/false)
|
|
2522
|
+
- created_at Contact creation date
|
|
2523
|
+
- metadata.* Custom metadata fields (e.g., metadata.plan)
|
|
2524
|
+
|
|
2525
|
+
Available Operators:
|
|
2526
|
+
- equals Exact match
|
|
2527
|
+
- not_equals Does not match
|
|
2528
|
+
- contains Contains substring (strings only)
|
|
2529
|
+
- not_contains Does not contain substring
|
|
2530
|
+
- starts_with Starts with prefix
|
|
2531
|
+
- ends_with Ends with suffix
|
|
2532
|
+
- greater_than Greater than (numbers/dates)
|
|
2533
|
+
- less_than Less than (numbers/dates)
|
|
2534
|
+
- is_set Field has a value
|
|
2535
|
+
- is_not_set Field is empty/null
|
|
2536
|
+
|
|
2537
|
+
COMMON SEGMENT CONDITION EXAMPLES
|
|
2538
|
+
|
|
2539
|
+
# Subscribed contacts only
|
|
2540
|
+
'[{"field": "subscribed", "operator": "equals", "value": true}]'
|
|
2541
|
+
|
|
2542
|
+
# Contacts with Gmail addresses
|
|
2543
|
+
'[{"field": "email", "operator": "contains", "value": "@gmail.com"}]'
|
|
2544
|
+
|
|
2545
|
+
# Premium plan users (using metadata)
|
|
2546
|
+
'[{"field": "metadata.plan", "operator": "equals", "value": "premium"}]'
|
|
2547
|
+
|
|
2548
|
+
# Contacts created in the last 30 days
|
|
2549
|
+
'[{"field": "created_at", "operator": "greater_than", "value": "2024-01-01"}]'
|
|
2550
|
+
|
|
2551
|
+
# Multiple conditions (AND logic)
|
|
2552
|
+
'[
|
|
2553
|
+
{"field": "subscribed", "operator": "equals", "value": true},
|
|
2554
|
+
{"field": "metadata.plan", "operator": "equals", "value": "enterprise"}
|
|
2555
|
+
]'
|
|
2556
|
+
|
|
2557
|
+
# VIP customers with high engagement score
|
|
2558
|
+
'[
|
|
2559
|
+
{"field": "metadata.vip", "operator": "equals", "value": true},
|
|
2560
|
+
{"field": "metadata.score", "operator": "greater_than", "value": 80}
|
|
2561
|
+
]'
|
|
2562
|
+
|
|
2563
|
+
EXAMPLES
|
|
2564
|
+
# List all segments
|
|
2565
|
+
emailr segments list
|
|
2566
|
+
|
|
2567
|
+
# Get a specific segment
|
|
2568
|
+
emailr segments get seg_abc123
|
|
2569
|
+
|
|
2570
|
+
# Create a segment for subscribed contacts
|
|
2571
|
+
emailr segments create --name "Active Subscribers" \\
|
|
2572
|
+
--conditions '[{"field": "subscribed", "operator": "equals", "value": true}]'
|
|
2573
|
+
|
|
2574
|
+
# Create a segment for premium users
|
|
2575
|
+
emailr segments create --name "Premium Users" \\
|
|
2576
|
+
--description "Users on premium plan" \\
|
|
2577
|
+
--conditions '[{"field": "metadata.plan", "operator": "equals", "value": "premium"}]'
|
|
2578
|
+
|
|
2579
|
+
# Update segment conditions
|
|
2580
|
+
emailr segments update seg_abc123 \\
|
|
2581
|
+
--conditions '[{"field": "metadata.plan", "operator": "equals", "value": "enterprise"}]'
|
|
2582
|
+
|
|
2583
|
+
# Get contact count in a segment
|
|
2584
|
+
emailr segments count seg_abc123
|
|
2585
|
+
|
|
2586
|
+
# Delete a segment
|
|
2587
|
+
emailr segments delete seg_abc123
|
|
2588
|
+
|
|
2589
|
+
# Get JSON output for scripting
|
|
2590
|
+
emailr segments list --format json
|
|
2591
|
+
|
|
2592
|
+
SEE ALSO
|
|
2593
|
+
emailr contacts Manage individual contacts
|
|
2594
|
+
emailr broadcasts Send emails to segments`);return i.command("list").description(`List all segments
|
|
2595
|
+
|
|
2596
|
+
USAGE
|
|
2597
|
+
emailr segments list [options]
|
|
2598
|
+
|
|
2599
|
+
DESCRIPTION
|
|
2600
|
+
Retrieves a list of all segments in your account. Returns segment ID,
|
|
2601
|
+
name, description, and creation date.
|
|
2602
|
+
|
|
2603
|
+
OPTIONS
|
|
2604
|
+
--format <format> Output format: json | table (default: table)
|
|
2605
|
+
|
|
2606
|
+
OUTPUT FORMATS
|
|
2607
|
+
--format json Machine-readable JSON array of segment objects
|
|
2608
|
+
--format table Human-readable table with columns: ID, Name, Description, Created At
|
|
2609
|
+
|
|
2610
|
+
EXAMPLES
|
|
2611
|
+
# List all segments
|
|
2612
|
+
emailr segments list
|
|
2613
|
+
|
|
2614
|
+
# Get JSON output for scripting
|
|
2615
|
+
emailr segments list --format json
|
|
2616
|
+
|
|
2617
|
+
# Pipe JSON to jq for processing
|
|
2618
|
+
emailr segments list --format json | jq '.[].name'`).option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),r=await new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}).segments.list();if(t.format==="json")c(r,"json");else {if(r.length===0){console.log("No segments found.");return}let o=r.map(n=>({ID:n.id,Name:n.name,Description:(n.description||"").substring(0,30)+((n.description?.length||0)>30?"...":""),"Created At":new Date(n.created_at).toLocaleDateString()}));c(o,"table");}}catch(e){l(e instanceof Error?e.message:"Failed to list segments"),process.exit(1);}}),i.command("get <segment_id>").description(`Get segment details
|
|
2619
|
+
|
|
2620
|
+
USAGE
|
|
2621
|
+
emailr segments get <segment_id> [options]
|
|
2622
|
+
|
|
2623
|
+
DESCRIPTION
|
|
2624
|
+
Retrieves detailed information about a specific segment including
|
|
2625
|
+
its name, description, conditions, and timestamps.
|
|
2626
|
+
|
|
2627
|
+
ARGUMENTS
|
|
2628
|
+
<segment_id> The unique segment identifier (e.g., seg_abc123)
|
|
2629
|
+
|
|
2630
|
+
OPTIONS
|
|
2631
|
+
--format <format> Output format: json | table (default: table)
|
|
2632
|
+
|
|
2633
|
+
OUTPUT FORMATS
|
|
2634
|
+
--format json Full segment object including conditions array
|
|
2635
|
+
--format table Summary table with ID, Name, Description, Conditions, Created At, Updated At
|
|
2636
|
+
|
|
2637
|
+
EXAMPLES
|
|
2638
|
+
# Get segment details
|
|
2639
|
+
emailr segments get seg_abc123
|
|
2640
|
+
|
|
2641
|
+
# Get full segment data as JSON
|
|
2642
|
+
emailr segments get seg_abc123 --format json
|
|
2643
|
+
|
|
2644
|
+
# Pipe JSON to jq to view conditions
|
|
2645
|
+
emailr segments get seg_abc123 --format json | jq '.conditions'`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).segments.get(t);e.format==="json"?c(o,"json"):c({ID:o.id,Name:o.name,Description:o.description||"N/A",Conditions:JSON.stringify(o.conditions),"Created At":o.created_at,"Updated At":o.updated_at},"table");}catch(a){l(a instanceof Error?a.message:"Failed to get segment"),process.exit(1);}}),i.command("create").description(`Create a new segment
|
|
2646
|
+
|
|
2647
|
+
USAGE
|
|
2648
|
+
emailr segments create --name <segment_name> --conditions <json> [options]
|
|
2649
|
+
|
|
2650
|
+
DESCRIPTION
|
|
2651
|
+
Creates a new segment with the specified name and conditions. Conditions
|
|
2652
|
+
define which contacts belong to the segment using field/operator/value rules.
|
|
2653
|
+
|
|
2654
|
+
OPTIONS
|
|
2655
|
+
--name <segment_name> Segment name (required)
|
|
2656
|
+
--conditions <json> Segment conditions as JSON array (required)
|
|
2657
|
+
--description <text> Segment description
|
|
2658
|
+
--format <format> Output format: json | table (default: table)
|
|
2659
|
+
|
|
2660
|
+
CONDITIONS FORMAT
|
|
2661
|
+
Conditions are specified as a JSON array of condition objects:
|
|
2662
|
+
'[{"field": "<field>", "operator": "<op>", "value": "<val>"}]'
|
|
2663
|
+
|
|
2664
|
+
See 'emailr segments --help' for full conditions documentation.
|
|
2665
|
+
|
|
2666
|
+
OUTPUT FORMATS
|
|
2667
|
+
--format json Full segment object with all fields
|
|
2668
|
+
--format table Summary table with ID and Name
|
|
2669
|
+
|
|
2670
|
+
EXAMPLES
|
|
2671
|
+
# Create segment for subscribed contacts
|
|
2672
|
+
emailr segments create --name "Active Subscribers" \\
|
|
2673
|
+
--conditions '[{"field": "subscribed", "operator": "equals", "value": true}]'
|
|
2674
|
+
|
|
2675
|
+
# Create segment with description
|
|
2676
|
+
emailr segments create --name "Premium Users" \\
|
|
2677
|
+
--description "Users on premium plan" \\
|
|
2678
|
+
--conditions '[{"field": "metadata.plan", "operator": "equals", "value": "premium"}]'
|
|
2679
|
+
|
|
2680
|
+
# Create segment with multiple conditions
|
|
2681
|
+
emailr segments create --name "Engaged Premium" \\
|
|
2682
|
+
--conditions '[
|
|
2683
|
+
{"field": "subscribed", "operator": "equals", "value": true},
|
|
2684
|
+
{"field": "metadata.plan", "operator": "equals", "value": "premium"}
|
|
2685
|
+
]'
|
|
2686
|
+
|
|
2687
|
+
# Create segment for Gmail users
|
|
2688
|
+
emailr segments create --name "Gmail Users" \\
|
|
2689
|
+
--conditions '[{"field": "email", "operator": "contains", "value": "@gmail.com"}]'
|
|
2690
|
+
|
|
2691
|
+
# Get JSON output for scripting
|
|
2692
|
+
emailr segments create --name "Test" \\
|
|
2693
|
+
--conditions '[{"field": "subscribed", "operator": "equals", "value": true}]' \\
|
|
2694
|
+
--format json`).requiredOption("--name <name>","Segment name").requiredOption("--conditions <json>","Segment conditions as JSON").option("--description <description>","Segment description").option("--format <format>","Output format (json|table)","table").action(async t=>{try{let e=m(),a=new Emailr({apiKey:e.apiKey,baseUrl:e.baseUrl}),r;try{r=JSON.parse(t.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let o=await a.segments.create({name:t.name,description:t.description,conditions:r});t.format==="json"?c(o,"json"):(d("Segment created successfully!"),c({ID:o.id,Name:o.name},"table"));}catch(e){l(e instanceof Error?e.message:"Failed to create segment"),process.exit(1);}}),i.command("update <segment_id>").description(`Update a segment
|
|
2695
|
+
|
|
2696
|
+
USAGE
|
|
2697
|
+
emailr segments update <segment_id> [options]
|
|
2698
|
+
|
|
2699
|
+
DESCRIPTION
|
|
2700
|
+
Updates an existing segment with new values. Only specified fields
|
|
2701
|
+
are updated; omitted fields remain unchanged.
|
|
2702
|
+
|
|
2703
|
+
ARGUMENTS
|
|
2704
|
+
<segment_id> The unique segment identifier (e.g., seg_abc123)
|
|
2705
|
+
|
|
2706
|
+
OPTIONS
|
|
2707
|
+
--name <segment_name> New segment name
|
|
2708
|
+
--description <text> New description
|
|
2709
|
+
--conditions <json> New conditions as JSON array (replaces existing)
|
|
2710
|
+
--format <format> Output format: json | table (default: table)
|
|
2711
|
+
|
|
2712
|
+
CONDITIONS FORMAT
|
|
2713
|
+
Conditions are specified as a JSON array of condition objects.
|
|
2714
|
+
Note: This replaces all existing conditions, not individual ones.
|
|
2715
|
+
See 'emailr segments --help' for full conditions documentation.
|
|
2716
|
+
|
|
2717
|
+
OUTPUT FORMATS
|
|
2718
|
+
--format json Full updated segment object
|
|
2719
|
+
--format table Summary table with ID and Name
|
|
2720
|
+
|
|
2721
|
+
EXAMPLES
|
|
2722
|
+
# Update segment name
|
|
2723
|
+
emailr segments update seg_abc123 --name "New Segment Name"
|
|
2724
|
+
|
|
2725
|
+
# Update segment description
|
|
2726
|
+
emailr segments update seg_abc123 --description "Updated description"
|
|
2727
|
+
|
|
2728
|
+
# Update segment conditions
|
|
2729
|
+
emailr segments update seg_abc123 \\
|
|
2730
|
+
--conditions '[{"field": "metadata.plan", "operator": "equals", "value": "enterprise"}]'
|
|
2731
|
+
|
|
2732
|
+
# Update multiple fields
|
|
2733
|
+
emailr segments update seg_abc123 \\
|
|
2734
|
+
--name "Enterprise Users" \\
|
|
2735
|
+
--description "Users on enterprise plan" \\
|
|
2736
|
+
--conditions '[{"field": "metadata.plan", "operator": "equals", "value": "enterprise"}]'
|
|
2737
|
+
|
|
2738
|
+
# Get JSON output
|
|
2739
|
+
emailr segments update seg_abc123 --name "Updated" --format json`).option("--name <name>","New segment name").option("--description <description>","New description").option("--conditions <json>","New conditions as JSON").option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),r=new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}),o={};if(e.name&&(o.name=e.name),e.description&&(o.description=e.description),e.conditions)try{o.conditions=JSON.parse(e.conditions);}catch{l("Invalid JSON for --conditions"),process.exit(1);}let n=await r.segments.update(t,o);e.format==="json"?c(n,"json"):(d("Segment updated successfully!"),c({ID:n.id,Name:n.name},"table"));}catch(a){l(a instanceof Error?a.message:"Failed to update segment"),process.exit(1);}}),i.command("delete <segment_id>").description(`Delete a segment
|
|
2740
|
+
|
|
2741
|
+
USAGE
|
|
2742
|
+
emailr segments delete <segment_id>
|
|
2743
|
+
|
|
2744
|
+
DESCRIPTION
|
|
2745
|
+
Permanently deletes a segment. This action cannot be undone.
|
|
2746
|
+
Contacts in the segment are NOT deleted, only the segment definition.
|
|
2747
|
+
|
|
2748
|
+
ARGUMENTS
|
|
2749
|
+
<segment_id> The unique segment identifier (e.g., seg_abc123)
|
|
2750
|
+
|
|
2751
|
+
OPTIONS
|
|
2752
|
+
--format <format> Output format: json | table (default: table)
|
|
2753
|
+
|
|
2754
|
+
OUTPUT FORMATS
|
|
2755
|
+
--format json Returns: { success: boolean }
|
|
2756
|
+
--format table Human-readable success message
|
|
2757
|
+
|
|
2758
|
+
EXAMPLES
|
|
2759
|
+
# Delete a segment
|
|
2760
|
+
emailr segments delete seg_abc123
|
|
2761
|
+
|
|
2762
|
+
WARNING
|
|
2763
|
+
This action is permanent and cannot be undone.
|
|
2764
|
+
Any broadcasts targeting this segment will need to be updated.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).segments.delete(t);e.format==="json"?c(o,"json"):d("Segment deleted successfully!");}catch(a){l(a instanceof Error?a.message:"Failed to delete segment"),process.exit(1);}}),i.command("count <segment_id>").description(`Get the number of contacts in a segment
|
|
2765
|
+
|
|
2766
|
+
USAGE
|
|
2767
|
+
emailr segments count <segment_id> [options]
|
|
2768
|
+
|
|
2769
|
+
DESCRIPTION
|
|
2770
|
+
Returns the count of contacts that match the segment's conditions.
|
|
2771
|
+
Useful for verifying segment conditions before sending broadcasts.
|
|
2772
|
+
|
|
2773
|
+
ARGUMENTS
|
|
2774
|
+
<segment_id> The unique segment identifier (e.g., seg_abc123)
|
|
2775
|
+
|
|
2776
|
+
OPTIONS
|
|
2777
|
+
--format <format> Output format: json | table (default: table)
|
|
2778
|
+
|
|
2779
|
+
OUTPUT FORMATS
|
|
2780
|
+
--format json Returns: { count: <number> }
|
|
2781
|
+
--format table Human-readable message with contact count
|
|
2782
|
+
|
|
2783
|
+
EXAMPLES
|
|
2784
|
+
# Get contact count in a segment
|
|
2785
|
+
emailr segments count seg_abc123
|
|
2786
|
+
|
|
2787
|
+
# Get JSON output for scripting
|
|
2788
|
+
emailr segments count seg_abc123 --format json
|
|
2789
|
+
|
|
2790
|
+
# Extract just the count value
|
|
2791
|
+
emailr segments count seg_abc123 --format json | jq '.count'
|
|
2792
|
+
|
|
2793
|
+
TIP
|
|
2794
|
+
Use this before sending broadcasts to verify your segment
|
|
2795
|
+
targets the expected number of contacts.`).option("--format <format>","Output format (json|table)","table").action(async(t,e)=>{try{let a=m(),o=await new Emailr({apiKey:a.apiKey,baseUrl:a.baseUrl}).segments.getContactsCount(t);e.format==="json"?c(o,"json"):console.log(`Segment contains ${o.count} contacts.`);}catch(a){l(a instanceof Error?a.message:"Failed to get segment count"),process.exit(1);}}),i}function st(i){try{let t=i.startsWith("http")?i:`http://localhost${i}`,e=new URL(t);return {key:e.searchParams.get("key")??void 0,code:e.searchParams.get("code")??void 0,state:e.searchParams.get("state")??void 0,error:e.searchParams.get("error")??void 0,message:e.searchParams.get("message")??void 0}}catch{return {}}}function lt(){return `<!DOCTYPE html>
|
|
2796
|
+
<html lang="en">
|
|
2797
|
+
<head>
|
|
2798
|
+
<meta charset="UTF-8">
|
|
2799
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2800
|
+
<title>Login Successful - Emailr CLI</title>
|
|
2801
|
+
<style>
|
|
2802
|
+
body {
|
|
2803
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
2804
|
+
display: flex;
|
|
2805
|
+
justify-content: center;
|
|
2806
|
+
align-items: center;
|
|
2807
|
+
min-height: 100vh;
|
|
2808
|
+
margin: 0;
|
|
2809
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
2810
|
+
color: #fff;
|
|
2811
|
+
}
|
|
2812
|
+
.container {
|
|
2813
|
+
text-align: center;
|
|
2814
|
+
padding: 2rem;
|
|
2815
|
+
background: rgba(255, 255, 255, 0.1);
|
|
2816
|
+
border-radius: 16px;
|
|
2817
|
+
backdrop-filter: blur(10px);
|
|
2818
|
+
max-width: 400px;
|
|
2819
|
+
}
|
|
2820
|
+
.icon {
|
|
2821
|
+
font-size: 4rem;
|
|
2822
|
+
margin-bottom: 1rem;
|
|
2823
|
+
}
|
|
2824
|
+
h1 {
|
|
2825
|
+
margin: 0 0 1rem 0;
|
|
2826
|
+
font-size: 1.5rem;
|
|
2827
|
+
}
|
|
2828
|
+
p {
|
|
2829
|
+
margin: 0;
|
|
2830
|
+
opacity: 0.9;
|
|
2831
|
+
}
|
|
2832
|
+
</style>
|
|
2833
|
+
</head>
|
|
2834
|
+
<body>
|
|
2835
|
+
<div class="container">
|
|
2836
|
+
<div class="icon">\u2713</div>
|
|
2837
|
+
<h1>Login Successful!</h1>
|
|
2838
|
+
<p>You can close this window and return to your terminal.</p>
|
|
2839
|
+
</div>
|
|
2840
|
+
</body>
|
|
2841
|
+
</html>`}function ee(i){return `<!DOCTYPE html>
|
|
2842
|
+
<html lang="en">
|
|
2843
|
+
<head>
|
|
2844
|
+
<meta charset="UTF-8">
|
|
2845
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2846
|
+
<title>Login Failed - Emailr CLI</title>
|
|
402
2847
|
<style>
|
|
403
2848
|
body {
|
|
404
2849
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
@@ -444,11 +2889,11 @@ When done:`),console.log(` emailr templates create --name "Template Name" --sub
|
|
|
444
2889
|
<div class="icon">\u2717</div>
|
|
445
2890
|
<h1>Login Failed</h1>
|
|
446
2891
|
<p>Something went wrong during authentication.</p>
|
|
447
|
-
<div class="error-message">${
|
|
2892
|
+
<div class="error-message">${i.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}</div>
|
|
448
2893
|
<p style="margin-top: 1rem;">Please close this window and try again.</p>
|
|
449
2894
|
</div>
|
|
450
2895
|
</body>
|
|
451
|
-
</html>`}function
|
|
2896
|
+
</html>`}function je(){let i=null,t=0,e=null,a=null,r=null;return {async start(){return new Promise((o,n)=>{i=Je.createServer((s,p)=>{if(s.method!=="GET"||!s.url?.startsWith("/callback")){p.writeHead(404,{"Content-Type":"text/plain"}),p.end("Not Found");return}let b=st(s.url);if(r&&b.state!==r){p.writeHead(400,{"Content-Type":"text/html"}),p.end(ee("Security verification failed. State parameter mismatch.")),e&&e({success:false,error:"State parameter mismatch"});return}if(b.error){let h=b.message||b.error;p.writeHead(200,{"Content-Type":"text/html"}),p.end(ee(h)),e&&e({success:false,error:h});return}let T=b.key||b.code;if(T){p.writeHead(200,{"Content-Type":"text/html"}),p.end(lt()),e&&e({success:true,apiKey:T});return}p.writeHead(400,{"Content-Type":"text/html"}),p.end(ee("Invalid callback: missing required parameters.")),e&&e({success:false,error:"Invalid callback: missing required parameters"});}),i.listen(0,"127.0.0.1",()=>{let s=i.address();s&&typeof s=="object"?(t=s.port,o({port:t,url:`http://127.0.0.1:${t}/callback`})):n(new Error("Failed to get server address"));}),i.on("error",s=>{n(new Error(`Failed to start callback server: ${s.message}`));});})},async waitForCallback(o,n){return r=o,new Promise(s=>{e=s,a=setTimeout(()=>{e&&e({success:false,error:"Login timed out. Please try again."});},n);})},async stop(){if(a&&(clearTimeout(a),a=null),i)return new Promise(o=>{i.close(()=>{i=null,o();});})}}}function Ce(){return ct.randomBytes(32).toString("hex")}function Ae(i){return new Promise(t=>{let e=process.platform,a;switch(e){case "darwin":a=`open "${i}"`;break;case "win32":a=`start "" "${i}"`;break;default:a=`xdg-open "${i}"`;break}exec(a,r=>{t(!r);});})}var $=120,pt=process.env.EMAILR_WEB_URL||"https://app.emailr.dev";function ut(i,t){let e=`http://127.0.0.1:${t}/callback`,a=new URLSearchParams({state:i,callback_url:e});return `${pt}/consent/authorize?${a.toString()}`}function Pe(){return new Command("login").description("Log in to Emailr via browser authentication").option("-t, --timeout <seconds>","Timeout in seconds",String($)).option("--no-browser","Don't automatically open the browser").action(async t=>{await ft({timeout:parseInt(t.timeout,10)||$,noBrowser:t.browser===false});})}async function ft(i){let t=je(),e=(i.timeout||$)*1e3;try{u("Starting authentication server...");let{port:a,url:r}=await t.start(),o=Ce(),n=ut(o,a);console.log(""),u("Authorization URL:"),console.log(` ${n}`),console.log(""),i.noBrowser?u("Please open the URL above in your browser to continue."):await Ae(n)?u("Browser opened. Please complete authentication in your browser."):(k("Could not open browser automatically."),u("Please open the URL above in your browser to continue.")),console.log(""),u(`Waiting for authentication (timeout: ${i.timeout||$}s)...`);let s=await t.waitForCallback(o,e);s.success&&s.apiKey?(G({apiKey:s.apiKey}),console.log(""),d("Login successful!"),u(`API key saved to: ${I()}`),u("You can now use the Emailr CLI.")):(console.log(""),l(s.error||"Authentication failed."),u("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1));}catch(a){console.log(""),l(a instanceof Error?a.message:"An unexpected error occurred."),u("Please try again or use manual configuration:"),console.log(" emailr config set api-key <your-api-key>"),process.exit(1);}finally{await t.stop();}}var ae=N.join(W.homedir(),".config","opencode","skills","emailr-cli"),wt=`---
|
|
452
2897
|
name: emailr-cli
|
|
453
2898
|
description: Operate the Emailr CLI to send emails, manage contacts, templates, domains, broadcasts, webhooks, and segments. Includes LIVE PREVIEW editing for templates with hot-reload.
|
|
454
2899
|
---
|
|
@@ -488,7 +2933,7 @@ IMPORTANT: Always use \`--background\` flag so the command returns immediately a
|
|
|
488
2933
|
- Create: \`emailr templates create --name "Name" --subject "Subject" --html-file ./template.html\`
|
|
489
2934
|
- Update: \`emailr templates update <id> --html-file ./template.html\`
|
|
490
2935
|
- Delete: \`emailr templates delete <id>\`
|
|
491
|
-
`;function yt(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function
|
|
2936
|
+
`;function yt(){try{return execSync("which opencode",{stdio:"ignore"}),!0}catch{return false}}function St(){console.log("Installing OpenCode AI agent...");try{return execSync("npm install -g opencode-ai@latest",{stdio:"inherit"}),!0}catch{if(process.platform==="darwin")try{return execSync("brew install anomalyco/tap/opencode",{stdio:"inherit"}),!0}catch{return false}return false}}function vt(){v.existsSync(ae)||v.mkdirSync(ae,{recursive:true});let i=N.join(ae,"SKILL.md");v.writeFileSync(i,wt,"utf-8");}function xe(){return new Command("agent").description(`Launch an AI agent with Emailr CLI expertise
|
|
492
2937
|
|
|
493
2938
|
This opens OpenCode (opencode.ai), an AI coding agent that knows how to use all Emailr CLI commands.
|
|
494
2939
|
The agent can help you:
|
|
@@ -496,7 +2941,93 @@ The agent can help you:
|
|
|
496
2941
|
- Manage contacts, broadcasts, and segments
|
|
497
2942
|
- Configure domains and webhooks
|
|
498
2943
|
|
|
499
|
-
The agent runs in your terminal with full access to the emailr CLI.`).option("--install","Install OpenCode if not already installed").option("--model <model>","Model to use (e.g., anthropic/claude-sonnet-4)").action(async t=>{yt()||(t.install?
|
|
500
|
-
Install it with one of:`),console.log(" emailr agent --install"),console.log(" npm install -g opencode-ai@latest"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)));try{vt(),d("Emailr CLI skill loaded");}catch(
|
|
2944
|
+
The agent runs in your terminal with full access to the emailr CLI.`).option("--install","Install OpenCode if not already installed").option("--model <model>","Model to use (e.g., anthropic/claude-sonnet-4)").action(async t=>{yt()||(t.install?St()||(l("Failed to install OpenCode. Please install manually:"),console.log(" npm install -g opencode-ai@latest"),console.log(" # or"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)):(l("OpenCode AI agent is not installed."),console.log(`
|
|
2945
|
+
Install it with one of:`),console.log(" emailr agent --install"),console.log(" npm install -g opencode-ai@latest"),console.log(" brew install anomalyco/tap/opencode"),process.exit(1)));try{vt(),d("Emailr CLI skill loaded");}catch(r){k(`Could not install skill: ${r instanceof Error?r.message:String(r)}`);}let e=[];t.model&&e.push("--model",t.model),console.log(`
|
|
501
2946
|
Starting Emailr AI Agent...`),console.log("The agent has the emailr-cli skill loaded."),console.log(`Ask it to help with emails, templates, contacts, or broadcasts.
|
|
502
|
-
`);let
|
|
2947
|
+
`);let a=spawn("opencode",e,{stdio:"inherit",env:process.env});a.on("error",r=>{l(`Failed to start agent: ${r.message}`),process.exit(1);}),a.on("exit",r=>{process.exit(r??0);});})}var g=new Command;g.name("emailr").description(`Emailr CLI - Send emails and manage your email infrastructure
|
|
2948
|
+
|
|
2949
|
+
USAGE
|
|
2950
|
+
emailr <command> [subcommand] [options]
|
|
2951
|
+
|
|
2952
|
+
DESCRIPTION
|
|
2953
|
+
The Emailr CLI provides a complete interface for sending emails and managing
|
|
2954
|
+
your email infrastructure. All commands support both human-readable table
|
|
2955
|
+
output and machine-readable JSON output for scripting and AI agent workflows.
|
|
2956
|
+
|
|
2957
|
+
COMMANDS
|
|
2958
|
+
send Send individual emails with HTML/text content or templates
|
|
2959
|
+
templates Create, edit, preview, and publish email templates
|
|
2960
|
+
contacts Manage email contacts and their metadata
|
|
2961
|
+
segments Create and manage contact segments with conditions
|
|
2962
|
+
broadcasts Send bulk email campaigns to segments
|
|
2963
|
+
webhooks Configure webhook endpoints for email events
|
|
2964
|
+
domains Add and verify sending domains (DKIM, SPF, DMARC)
|
|
2965
|
+
config Configure CLI settings (API key, base URL)
|
|
2966
|
+
login Authenticate with your Emailr account
|
|
2967
|
+
agent Run the AI agent for automated email tasks
|
|
2968
|
+
|
|
2969
|
+
GETTING STARTED
|
|
2970
|
+
1. Set your API key:
|
|
2971
|
+
emailr config set api-key <your_api_key>
|
|
2972
|
+
|
|
2973
|
+
2. Verify your configuration:
|
|
2974
|
+
emailr config list
|
|
2975
|
+
|
|
2976
|
+
3. Send your first email:
|
|
2977
|
+
emailr send --to user@example.com --subject "Hello" --html "<p>Hi!</p>"
|
|
2978
|
+
|
|
2979
|
+
OUTPUT FORMATS
|
|
2980
|
+
Most commands support --format option:
|
|
2981
|
+
--format table Human-readable table output (default)
|
|
2982
|
+
--format json Machine-readable JSON output for scripting
|
|
2983
|
+
|
|
2984
|
+
ENVIRONMENT VARIABLES
|
|
2985
|
+
EMAILR_API_KEY API key (overrides config file)
|
|
2986
|
+
EMAILR_BASE_URL Custom API base URL (overrides config file)
|
|
2987
|
+
|
|
2988
|
+
EXAMPLES
|
|
2989
|
+
# Send a simple email
|
|
2990
|
+
emailr send --to user@example.com --subject "Hello" --html "<p>Hi!</p>"
|
|
2991
|
+
|
|
2992
|
+
# Send using a template
|
|
2993
|
+
emailr send --to user@example.com --template tpl_abc123 \\
|
|
2994
|
+
--template-data '{"name": "John"}'
|
|
2995
|
+
|
|
2996
|
+
# List all templates
|
|
2997
|
+
emailr templates list
|
|
2998
|
+
|
|
2999
|
+
# Create a new contact
|
|
3000
|
+
emailr contacts create --email user@example.com --name "John Doe"
|
|
3001
|
+
|
|
3002
|
+
# Create a segment for active subscribers
|
|
3003
|
+
emailr segments create --name "Active" \\
|
|
3004
|
+
--conditions '[{"field": "subscribed", "operator": "equals", "value": true}]'
|
|
3005
|
+
|
|
3006
|
+
# Send a broadcast to a segment
|
|
3007
|
+
emailr broadcasts create --name "Newsletter" --template tpl_abc123 \\
|
|
3008
|
+
--segment seg_xyz789
|
|
3009
|
+
|
|
3010
|
+
# Add and verify a sending domain
|
|
3011
|
+
emailr domains add example.com
|
|
3012
|
+
emailr domains verify example.com
|
|
3013
|
+
|
|
3014
|
+
# Configure a webhook for delivery events
|
|
3015
|
+
emailr webhooks create --name "Deliveries" --url "https://..." \\
|
|
3016
|
+
--events "email.delivered,email.bounced"
|
|
3017
|
+
|
|
3018
|
+
# Get JSON output for scripting
|
|
3019
|
+
emailr contacts list --format json | jq '.[].email'
|
|
3020
|
+
|
|
3021
|
+
AGENTIC WORKFLOW
|
|
3022
|
+
For collaborating with AI agents on email templates:
|
|
3023
|
+
|
|
3024
|
+
1. Fetch template: emailr templates fetch <id> --output template.html
|
|
3025
|
+
2. Edit locally or with AI assistance
|
|
3026
|
+
3. Push preview: emailr templates push-preview <id> --html-file template.html
|
|
3027
|
+
4. Share preview URL with AI agent for feedback
|
|
3028
|
+
5. Iterate until satisfied
|
|
3029
|
+
6. Publish: emailr templates update <id> --html-file template.html
|
|
3030
|
+
|
|
3031
|
+
MORE INFORMATION
|
|
3032
|
+
Run 'emailr <command> --help' for detailed help on any command.
|
|
3033
|
+
Run 'emailr <command> <subcommand> --help' for subcommand details.`).version("1.5.4");g.addCommand(ce());g.addCommand(me());g.addCommand(Oe());g.addCommand(Ee());g.addCommand(_e());g.addCommand(Ie());g.addCommand(ke());g.addCommand(Ne());g.addCommand(Pe());g.addCommand(xe());g.parse();
|