shipmail-cli 0.6.7 → 0.6.8
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/README.md +13 -1
- package/dist/cli.js +278 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -69,6 +69,11 @@ shipmail messages list --client-reference crm-123
|
|
|
69
69
|
shipmail messages analytics --updated-after 2026-07-01T00:00:00Z --limit 100
|
|
70
70
|
shipmail messages reply msg_123 --to user@example.com --text "Thanks"
|
|
71
71
|
|
|
72
|
+
# SMTP credentials for a transactional sender (the password is printed once)
|
|
73
|
+
shipmail smtp-credentials create --from notifications@notify.example.com --name "Supabase Auth"
|
|
74
|
+
shipmail smtp-credentials list --from notifications@notify.example.com
|
|
75
|
+
shipmail smtp-credentials revoke smtpc_123
|
|
76
|
+
|
|
72
77
|
# Stage a raw file, then pass the returned sat_... ID when sending
|
|
73
78
|
shipmail mailboxes stage-attachment mbx_123 ./invoice.pdf --content-type application/pdf
|
|
74
79
|
|
|
@@ -149,6 +154,11 @@ shipmail audiences welcome add-step aud_123 --subject "Welcome" --markdown @welc
|
|
|
149
154
|
shipmail audiences welcome add-step aud_123 --subject "Our best posts" --markdown @day-3.md --wait-minutes 4320
|
|
150
155
|
shipmail audiences welcome enable aud_123
|
|
151
156
|
|
|
157
|
+
# Save a group of subscribers, then target it with segment_id
|
|
158
|
+
shipmail audiences segments create aud_123 --name "Founders" \
|
|
159
|
+
--definition '{"match":"all","rules":[{"kind":"merge_field","field":"role","operator":"equals","value":"founder"}]}'
|
|
160
|
+
shipmail audiences segments list aud_123
|
|
161
|
+
|
|
152
162
|
# Store a template, publish it, then send it by ID
|
|
153
163
|
shipmail templates create \
|
|
154
164
|
--name "Welcome" \
|
|
@@ -219,12 +229,14 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
|
|
|
219
229
|
- `scheduled-messages list|get|update|cancel`
|
|
220
230
|
- `mailboxes sandbox-inbound`
|
|
221
231
|
- `webhooks list|create|update|test|deliveries|replay-delivery`
|
|
232
|
+
- `smtp-credentials list|create|revoke`
|
|
222
233
|
- `suppressions add|list|remove`
|
|
223
234
|
- `templates list|create|get|update|delete|publish|render|versions|version|restore` (`messages send --template` sends one)
|
|
224
235
|
- `audiences list|create|get|update|delete`
|
|
225
236
|
- `audiences feed get|update|rotate|revoke`
|
|
226
237
|
- `audiences welcome get|enable|disable|add-step|update-step|remove-step`
|
|
227
|
-
- `audiences
|
|
238
|
+
- `audiences segments list|create|get|update|delete`
|
|
239
|
+
- `audiences subscribers add|list|get|by-email|update|unsubscribe|resubscribe|resend-confirmation|remove`
|
|
228
240
|
- `newsletter-sender-identities list`
|
|
229
241
|
- `newsletter-domains list`
|
|
230
242
|
- `newsletters create|update|preview|preflight|test-send|schedule|cancel|resume`
|
package/dist/cli.js
CHANGED
|
@@ -652,7 +652,7 @@ async function login(options) {
|
|
|
652
652
|
}
|
|
653
653
|
|
|
654
654
|
// src/version.ts
|
|
655
|
-
var VERSION = "0.6.
|
|
655
|
+
var VERSION = "0.6.8";
|
|
656
656
|
|
|
657
657
|
// src/cli.ts
|
|
658
658
|
var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
|
|
@@ -1035,6 +1035,18 @@ function parseStylingMode(options) {
|
|
|
1035
1035
|
if (value === "styled" || value === "plain") return value;
|
|
1036
1036
|
throw new CliUsageError("--styling-mode must be styled or plain.");
|
|
1037
1037
|
}
|
|
1038
|
+
function isSegmentDefinition(value) {
|
|
1039
|
+
return isJsonObject(value) && (value["match"] === "all" || value["match"] === "any") && Array.isArray(value["rules"]) && value["rules"].length > 0 && value["rules"].every((rule) => isJsonObject(rule) && typeof rule["kind"] === "string");
|
|
1040
|
+
}
|
|
1041
|
+
async function segmentDefinition(value) {
|
|
1042
|
+
const parsed = await readJsonInput(value);
|
|
1043
|
+
if (!isSegmentDefinition(parsed)) {
|
|
1044
|
+
throw new CliUsageError(
|
|
1045
|
+
'--definition must be a JSON object with match ("all" or "any") and a non-empty rules array.'
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
return parsed;
|
|
1049
|
+
}
|
|
1038
1050
|
async function automationDefinition(value) {
|
|
1039
1051
|
const parsed = await readJsonInput(value);
|
|
1040
1052
|
if (!isAutomationDefinition(parsed)) {
|
|
@@ -1288,16 +1300,17 @@ Usage:
|
|
|
1288
1300
|
shipmail inbox update-draft <mailbox-id> <thread-id> <draft-id> (--text "Hello" | --html "<p>Hello</p>") (the new body replaces the old one)
|
|
1289
1301
|
shipmail inbox delete-draft <mailbox-id> <thread-id> <draft-id>
|
|
1290
1302
|
shipmail inbox send-draft <mailbox-id> <thread-id> <draft-id> [--expected-content-sha256 hash]
|
|
1291
|
-
shipmail inbox reply-message <mailbox-id> <message-id> --to user@example.com [--cc ...] [--bcc ...] (--text "Hello" | --html "<p>Hello</p>") [--track-reply true|false]
|
|
1292
|
-
shipmail inbox reply-thread <mailbox-id> <thread-id> --to user@example.com [--cc ...] [--bcc ...] (--text "Hello" | --html "<p>Hello</p>") [--track-reply true|false]
|
|
1303
|
+
shipmail inbox reply-message <mailbox-id> <message-id> --to user@example.com [--cc ...] [--bcc ...] (--text "Hello" | --html "<p>Hello</p>") [--track-reply true|false] [--add-signature]
|
|
1304
|
+
shipmail inbox reply-thread <mailbox-id> <thread-id> --to user@example.com [--cc ...] [--bcc ...] (--text "Hello" | --html "<p>Hello</p>") [--track-reply true|false] [--add-signature]
|
|
1293
1305
|
shipmail inbox update <mailbox-id> <message-id> [--read true] [--starred false]
|
|
1294
1306
|
shipmail inbox move <mailbox-id> <message-id> (--target-role archive | --target-folder-id fld)
|
|
1295
|
-
shipmail messages send (--mailbox-id mbx | --from 'Acme Support <hello@example.com>') --to user@example.com --subject "Hi" --text "Hello" [--track-reply true|false] [--sandbox-outcome delivered|bounced|complained] [--client-reference crm-123] [--metadata JSON|@file] [--source-rfc-message-id '<crm-123@example.com>'] [--header 'X-CRM-ID: 123']
|
|
1307
|
+
shipmail messages send (--mailbox-id mbx | --from 'Acme Support <hello@example.com>') --to user@example.com --subject "Hi" --text "Hello" [--track-reply true|false] [--add-signature] [--sandbox-outcome delivered|bounced|complained] [--client-reference crm-123] [--metadata JSON|@file] [--source-rfc-message-id '<crm-123@example.com>'] [--header 'X-CRM-ID: 123']
|
|
1296
1308
|
shipmail messages send (--mailbox-id mbx | --from hello@example.com) --to user@example.com --template tpl_... [--template-version 2] [--var name=Ada ...]
|
|
1297
1309
|
shipmail messages list (--mailbox-id mbx | --client-reference crm-123) [--limit 25] [--cursor cur]
|
|
1298
1310
|
shipmail messages analytics [--updated-after ISO] [--updated-before ISO] [--limit 100] [--cursor cur]
|
|
1299
1311
|
shipmail messages get <message-id>
|
|
1300
|
-
shipmail messages reply <message-id> --to user@example.com [--cc ...] [--bcc ...] (--text "Hello" | --html "<p>Hello</p>") [--track-reply true|false] [--client-reference crm-reply-123]
|
|
1312
|
+
shipmail messages reply <message-id> --to user@example.com [--cc ...] [--bcc ...] (--text "Hello" | --html "<p>Hello</p>") [--track-reply true|false] [--add-signature] [--client-reference crm-reply-123]
|
|
1313
|
+
shipmail messages invitation respond <message-id> --response accepted|tentative|declined
|
|
1301
1314
|
shipmail scheduled-messages list [--include-held] [--search text] [--limit 100]
|
|
1302
1315
|
shipmail scheduled-messages get <message-id>
|
|
1303
1316
|
shipmail scheduled-messages update <message-id> --to user@example.com --subject "Hi" --scheduled-at ISO (--text "Hello" | --html "<p>Hello</p>") [--staged-attachment-id sat_...]
|
|
@@ -1309,9 +1322,20 @@ Usage:
|
|
|
1309
1322
|
shipmail webhooks create --url https://example.com/hook --events message.received,message.bounced [--authorization "Bearer key"] [--standard-webhooks-secret whsec_...] [--include-reply-text] [--authenticated-senders-only] [--allowed-sender jane@customer.com --allowed-sender customer.com]
|
|
1310
1323
|
shipmail webhooks update <webhook-id> [--url https://example.com/hook] [--events message.received] [--description text] [--active true|false] [--authorization "Bearer key" | --clear-authorization] [--standard-webhooks-secret whsec_... | --clear-standard-webhooks-secret] [--include-reply-text true|false] [--authenticated-senders-only true|false] [--allowed-sender customer.com ... | --clear-allowed-senders]
|
|
1311
1324
|
shipmail webhooks replay-delivery <webhook-id> <delivery-id> [--idempotency-key key]
|
|
1325
|
+
shipmail smtp-credentials list [--from notifications@notify.example.com]
|
|
1326
|
+
shipmail smtp-credentials create --from notifications@notify.example.com --name "Supabase Auth" (prints the password once)
|
|
1327
|
+
shipmail smtp-credentials revoke <smtp-credential-id>
|
|
1312
1328
|
shipmail suppressions add <email> [--scope all|marketing]
|
|
1313
|
-
shipmail suppressions list [--scope all|marketing] [--limit 25] [--cursor cur]
|
|
1329
|
+
shipmail suppressions list [--scope all|marketing|transactional|mailbox] [--limit 25] [--cursor cur]
|
|
1314
1330
|
shipmail suppressions remove <email>
|
|
1331
|
+
shipmail customers list [--email jane@acme.com | --domain acme.com | --external-source hubspot --external-id 42 | --q text] [--tag vip] [--limit 25] [--cursor cur]
|
|
1332
|
+
shipmail customers get <customer-id>
|
|
1333
|
+
shipmail customers create [--name "Jane Doe"] [--company Acme] [--notes text] [--service-level-id svl_...] [--email jane@acme.com ...] [--domain acme.com ...] [--fields JSON|@file] [--tag vip ...]
|
|
1334
|
+
shipmail customers update <customer-id> [--name ...] [--company ...] [--notes ...] [--service-level-id svl_... | --clear-service-level] [--email ... --domain ...] [--fields JSON|@file] [--tag vip ... | --clear-tags] (--email and --domain replace all identifiers; --tag replaces all tags)
|
|
1335
|
+
shipmail customers upsert <source> <external-id> [--url https://crm.example.com/42] [same flags as update] (creates or updates the customer that system knows by that id)
|
|
1336
|
+
shipmail customers delete <customer-id>
|
|
1337
|
+
shipmail customers reply-times
|
|
1338
|
+
shipmail customers fields
|
|
1315
1339
|
shipmail templates list [--limit 25] [--cursor cur]
|
|
1316
1340
|
shipmail templates create --name "Welcome" --subject "Welcome, {{name}}" (--html "<p>Hi {{name}}</p>" | --text "Hi {{name}}") [--fallback name=there ...] [--publish]
|
|
1317
1341
|
shipmail templates get <template-id>
|
|
@@ -1356,6 +1380,11 @@ Usage:
|
|
|
1356
1380
|
shipmail audiences welcome add-step <audience-id> --subject "Welcome" (--markdown @welcome.md | --blocks @blocks.json) [--wait-minutes 0] [--preview-text "..."] [--sender-identity-id nwsid_456]
|
|
1357
1381
|
shipmail audiences welcome update-step <audience-id> <step-id> [--subject "..."] [--markdown @welcome.md | --blocks @blocks.json] [--wait-minutes 1440]
|
|
1358
1382
|
shipmail audiences welcome remove-step <audience-id> <step-id>
|
|
1383
|
+
shipmail audiences segments list <audience-id>
|
|
1384
|
+
shipmail audiences segments create <audience-id> --name "Founders" --definition JSON|@segment.json [--idempotency-key key]
|
|
1385
|
+
shipmail audiences segments get <audience-id> <segment-id>
|
|
1386
|
+
shipmail audiences segments update <audience-id> <segment-id> [--name "..."] [--definition JSON|@segment.json]
|
|
1387
|
+
shipmail audiences segments delete <audience-id> <segment-id>
|
|
1359
1388
|
shipmail audiences subscribers add <audience-id> <email> [--name "Jane"] [--consent-source "..."]
|
|
1360
1389
|
shipmail audiences subscribers list <audience-id> [--status subscribed] [--email jane@x.com] [--limit 25]
|
|
1361
1390
|
shipmail audiences subscribers get <audience-id> <subscriber-id>
|
|
@@ -1363,6 +1392,7 @@ Usage:
|
|
|
1363
1392
|
shipmail audiences subscribers update <audience-id> <subscriber-id> [--name "..."]
|
|
1364
1393
|
shipmail audiences subscribers unsubscribe <audience-id> <subscriber-id>
|
|
1365
1394
|
shipmail audiences subscribers resubscribe <audience-id> <subscriber-id> --consent-source "Signup form"
|
|
1395
|
+
shipmail audiences subscribers resend-confirmation <audience-id> [--subscriber <subscriber-id> ...]
|
|
1366
1396
|
shipmail audiences subscribers remove <audience-id> <subscriber-id>
|
|
1367
1397
|
shipmail calendar events list --mailbox hello@example.com --from 2026-08-01T00:00:00Z --to 2026-08-08T00:00:00Z [--expand true] [--limit 100]
|
|
1368
1398
|
shipmail calendar events create --mailbox hello@example.com --title "Sync" --start 2026-08-01T14:00:00 [--timezone Europe/Paris] [--duration PT30M] [--invitation-language en|fr|es] [--attendees JSON|@file] [--reminders JSON|@file] [--operation-key key] [--send-invitations true|false]
|
|
@@ -1405,7 +1435,8 @@ Templates:
|
|
|
1405
1435
|
version into the draft without publishing it. render previews the draft, or --template-version.
|
|
1406
1436
|
messages send --template <id> sends the published version, or --template-version. --var
|
|
1407
1437
|
key=value sets a variable (repeat it; values are text). --subject, --html and --text are refused
|
|
1408
|
-
with --template.
|
|
1438
|
+
with --template. A transactional sender refuses --staged-attachment-id, and an email it sends can
|
|
1439
|
+
be up to 10 MB, encoding included.
|
|
1409
1440
|
|
|
1410
1441
|
Conversation IDs:
|
|
1411
1442
|
<thread-id> accepts a conversation ID (thd_...) or the mail-server thread ID. Store the
|
|
@@ -1445,6 +1476,10 @@ List cleanup:
|
|
|
1445
1476
|
Newsletter block prose:
|
|
1446
1477
|
Paragraph, quote, callout, list-item, and column bodies accept bare text or sanitized inline HTML. Use <p> and <br> for line breaks.
|
|
1447
1478
|
|
|
1479
|
+
Audience segments:
|
|
1480
|
+
--definition <JSON|@file> {"match":"all"|"any","rules":[...]} with 1 to 10 rules, for example {"kind":"merge_field","field":"role","operator":"equals","value":"founder"}. Rule kinds: merge_field, join_date, engagement_tier, last_sign_of_life, issues_received.
|
|
1481
|
+
A segment's id is the segment_id a newsletter block's visibility takes. Delete is refused while a newsletter sends to the segment.
|
|
1482
|
+
|
|
1448
1483
|
Newsletter feeds:
|
|
1449
1484
|
--feed-entry-url <url> Link this newsletter's Atom feed title to an http(s) URL instead of its Shipmail archive.
|
|
1450
1485
|
|
|
@@ -1648,6 +1683,10 @@ async function sendMessageParams(options) {
|
|
|
1648
1683
|
const trackReply = getBoolean(options, "track-reply");
|
|
1649
1684
|
return trackReply === void 0 ? {} : { track_reply: trackReply };
|
|
1650
1685
|
})(),
|
|
1686
|
+
...(() => {
|
|
1687
|
+
const addSignature = getBoolean(options, "add-signature");
|
|
1688
|
+
return addSignature === void 0 ? {} : { add_signature: addSignature };
|
|
1689
|
+
})(),
|
|
1651
1690
|
...getString(options, "sandbox-outcome") ? {
|
|
1652
1691
|
sandbox_outcome: getEnum(options, "sandbox-outcome", [
|
|
1653
1692
|
"delivered",
|
|
@@ -1715,6 +1754,10 @@ async function replyToMessageParams(options) {
|
|
|
1715
1754
|
const trackReply = getBoolean(options, "track-reply");
|
|
1716
1755
|
return trackReply === void 0 ? {} : { track_reply: trackReply };
|
|
1717
1756
|
})(),
|
|
1757
|
+
...(() => {
|
|
1758
|
+
const addSignature = getBoolean(options, "add-signature");
|
|
1759
|
+
return addSignature === void 0 ? {} : { add_signature: addSignature };
|
|
1760
|
+
})(),
|
|
1718
1761
|
...getString(options, "sandbox-outcome") ? {
|
|
1719
1762
|
sandbox_outcome: getEnum(options, "sandbox-outcome", [
|
|
1720
1763
|
"delivered",
|
|
@@ -2467,6 +2510,24 @@ async function runMessages(client, parsed, out) {
|
|
|
2467
2510
|
);
|
|
2468
2511
|
return;
|
|
2469
2512
|
}
|
|
2513
|
+
if (subcommand === "invitation" && parsed.positional[2] === "respond") {
|
|
2514
|
+
const response = getEnum(parsed.options, "response", [
|
|
2515
|
+
"accepted",
|
|
2516
|
+
"tentative",
|
|
2517
|
+
"declined"
|
|
2518
|
+
]);
|
|
2519
|
+
if (!response) {
|
|
2520
|
+
throw new CliUsageError("--response must be one of: accepted, tentative, declined.");
|
|
2521
|
+
}
|
|
2522
|
+
writeJson(
|
|
2523
|
+
out,
|
|
2524
|
+
await client.messages.respondToInvitation(
|
|
2525
|
+
requirePositional(parsed.positional, 3, "message id"),
|
|
2526
|
+
{ response }
|
|
2527
|
+
)
|
|
2528
|
+
);
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2470
2531
|
if (subcommand === "get") {
|
|
2471
2532
|
writeJson(
|
|
2472
2533
|
out,
|
|
@@ -2676,11 +2737,36 @@ function inviteMemberParams(parsed) {
|
|
|
2676
2737
|
...newsletterEditor ? { newsletter_role: "editor" } : {}
|
|
2677
2738
|
};
|
|
2678
2739
|
}
|
|
2679
|
-
|
|
2740
|
+
async function runSmtpCredentials(client, parsed, out) {
|
|
2741
|
+
const subcommand = parsed.positional[1];
|
|
2742
|
+
if (subcommand === "list") {
|
|
2743
|
+
writeJson(out, await client.smtpCredentials.list({ from: getString(parsed.options, "from") }));
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
if (subcommand === "create") {
|
|
2747
|
+
writeJson(
|
|
2748
|
+
out,
|
|
2749
|
+
await client.smtpCredentials.create({
|
|
2750
|
+
from: getRequiredString(parsed.options, "from"),
|
|
2751
|
+
name: getRequiredString(parsed.options, "name")
|
|
2752
|
+
})
|
|
2753
|
+
);
|
|
2754
|
+
return;
|
|
2755
|
+
}
|
|
2756
|
+
if (subcommand === "revoke") {
|
|
2757
|
+
const id = requirePositional(parsed.positional, 2, "SMTP credential id");
|
|
2758
|
+
await client.smtpCredentials.revoke(id);
|
|
2759
|
+
writeJson(out, { ok: true, id });
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
throw new CliUsageError("Unknown smtp-credentials command.");
|
|
2763
|
+
}
|
|
2764
|
+
var SUPPRESSION_SCOPES = ["all", "marketing", "transactional", "mailbox"];
|
|
2765
|
+
var CREATE_SUPPRESSION_SCOPES = ["all", "marketing"];
|
|
2680
2766
|
async function runSuppressions(client, parsed, out) {
|
|
2681
2767
|
const subcommand = parsed.positional[1];
|
|
2682
2768
|
if (subcommand === "add") {
|
|
2683
|
-
const scope = getEnum(parsed.options, "scope",
|
|
2769
|
+
const scope = getEnum(parsed.options, "scope", CREATE_SUPPRESSION_SCOPES);
|
|
2684
2770
|
writeJson(
|
|
2685
2771
|
out,
|
|
2686
2772
|
await client.suppressions.create({
|
|
@@ -2709,6 +2795,116 @@ async function runSuppressions(client, parsed, out) {
|
|
|
2709
2795
|
}
|
|
2710
2796
|
throw new CliUsageError("Unknown suppressions command.");
|
|
2711
2797
|
}
|
|
2798
|
+
function isCustomerFields(value) {
|
|
2799
|
+
return isJsonObject(value) && Object.values(value).every(
|
|
2800
|
+
(item) => typeof item === "string" || typeof item === "number" || typeof item === "boolean"
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
async function customerProfileParams(options) {
|
|
2804
|
+
const emails = getStringList(options, "email");
|
|
2805
|
+
const domains = getStringList(options, "domain");
|
|
2806
|
+
const rawFields = getString(options, "fields");
|
|
2807
|
+
const fields = rawFields === void 0 ? void 0 : await readJsonInput(rawFields);
|
|
2808
|
+
if (fields !== void 0 && !isCustomerFields(fields)) {
|
|
2809
|
+
throw new CliUsageError(
|
|
2810
|
+
"--fields must be a JSON object of string, number or boolean values, or @path/to/file.json."
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2813
|
+
const serviceLevelId = getString(options, "service-level-id");
|
|
2814
|
+
const clearServiceLevel = getBoolean(options, "clear-service-level") === true;
|
|
2815
|
+
if (serviceLevelId !== void 0 && clearServiceLevel) {
|
|
2816
|
+
throw new CliUsageError("Use either --service-level-id or --clear-service-level, not both.");
|
|
2817
|
+
}
|
|
2818
|
+
const tagFlag = optionValue(options, "tag");
|
|
2819
|
+
if (typeof tagFlag === "boolean") throw new CliUsageError("Missing value for --tag.");
|
|
2820
|
+
const tags = tagFlag === void 0 ? void 0 : getStringList(options, "tag");
|
|
2821
|
+
const clearTags = getBoolean(options, "clear-tags") === true;
|
|
2822
|
+
if (tags !== void 0 && clearTags) {
|
|
2823
|
+
throw new CliUsageError("Use --tag or --clear-tags, not both.");
|
|
2824
|
+
}
|
|
2825
|
+
const name = getString(options, "name");
|
|
2826
|
+
const company = getString(options, "company");
|
|
2827
|
+
const notes = await readTextInput(getString(options, "notes"));
|
|
2828
|
+
return {
|
|
2829
|
+
...name !== void 0 ? { name } : {},
|
|
2830
|
+
...company !== void 0 ? { company } : {},
|
|
2831
|
+
...notes !== void 0 ? { notes } : {},
|
|
2832
|
+
...serviceLevelId !== void 0 ? { service_level_id: serviceLevelId } : {},
|
|
2833
|
+
...clearServiceLevel ? { service_level_id: null } : {},
|
|
2834
|
+
...fields !== void 0 ? { fields } : {},
|
|
2835
|
+
...tags !== void 0 ? { tags } : {},
|
|
2836
|
+
...clearTags ? { tags: [] } : {},
|
|
2837
|
+
...emails.length > 0 || domains.length > 0 ? {
|
|
2838
|
+
identifiers: [
|
|
2839
|
+
...emails.map((value) => ({ kind: "email", value })),
|
|
2840
|
+
...domains.map((value) => ({ kind: "domain", value }))
|
|
2841
|
+
]
|
|
2842
|
+
} : {}
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
async function runCustomers(client, parsed, out) {
|
|
2846
|
+
const subcommand = parsed.positional[1];
|
|
2847
|
+
if (subcommand === "list") {
|
|
2848
|
+
writeJson(
|
|
2849
|
+
out,
|
|
2850
|
+
await client.customers.list({
|
|
2851
|
+
cursor: getString(parsed.options, "cursor"),
|
|
2852
|
+
limit: getNumber(parsed.options, "limit"),
|
|
2853
|
+
email: getString(parsed.options, "email"),
|
|
2854
|
+
domain: getString(parsed.options, "domain"),
|
|
2855
|
+
external_source: getString(parsed.options, "external-source"),
|
|
2856
|
+
external_id: getString(parsed.options, "external-id"),
|
|
2857
|
+
q: getString(parsed.options, "q"),
|
|
2858
|
+
tag: getString(parsed.options, "tag")
|
|
2859
|
+
})
|
|
2860
|
+
);
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
if (subcommand === "get") {
|
|
2864
|
+
writeJson(
|
|
2865
|
+
out,
|
|
2866
|
+
await client.customers.get(requirePositional(parsed.positional, 2, "customer-id"))
|
|
2867
|
+
);
|
|
2868
|
+
return;
|
|
2869
|
+
}
|
|
2870
|
+
if (subcommand === "create") {
|
|
2871
|
+
writeJson(out, await client.customers.create(await customerProfileParams(parsed.options)));
|
|
2872
|
+
return;
|
|
2873
|
+
}
|
|
2874
|
+
if (subcommand === "update") {
|
|
2875
|
+
const id = requirePositional(parsed.positional, 2, "customer-id");
|
|
2876
|
+
writeJson(out, await client.customers.update(id, await customerProfileParams(parsed.options)));
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
if (subcommand === "upsert") {
|
|
2880
|
+
const source = requirePositional(parsed.positional, 2, "source");
|
|
2881
|
+
const externalId = requirePositional(parsed.positional, 3, "external-id");
|
|
2882
|
+
const url = getString(parsed.options, "url");
|
|
2883
|
+
writeJson(
|
|
2884
|
+
out,
|
|
2885
|
+
await client.customers.upsertByExternalId(source, externalId, {
|
|
2886
|
+
...await customerProfileParams(parsed.options),
|
|
2887
|
+
...url !== void 0 ? { url } : {}
|
|
2888
|
+
})
|
|
2889
|
+
);
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
if (subcommand === "delete") {
|
|
2893
|
+
const id = requirePositional(parsed.positional, 2, "customer-id");
|
|
2894
|
+
await client.customers.remove(id);
|
|
2895
|
+
writeJson(out, { ok: true, id });
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
if (subcommand === "reply-times") {
|
|
2899
|
+
writeJson(out, await client.customers.listServiceLevels());
|
|
2900
|
+
return;
|
|
2901
|
+
}
|
|
2902
|
+
if (subcommand === "fields") {
|
|
2903
|
+
writeJson(out, await client.customers.listFields());
|
|
2904
|
+
return;
|
|
2905
|
+
}
|
|
2906
|
+
throw new CliUsageError("Unknown customers command.");
|
|
2907
|
+
}
|
|
2712
2908
|
async function templateCreateParams(options) {
|
|
2713
2909
|
const html = await readTextInput(getString(options, "html"));
|
|
2714
2910
|
const text = await readTextInput(getString(options, "text"));
|
|
@@ -2935,6 +3131,17 @@ async function runSubscribers(client, parsed, out) {
|
|
|
2935
3131
|
);
|
|
2936
3132
|
return;
|
|
2937
3133
|
}
|
|
3134
|
+
if (action === "resend-confirmation") {
|
|
3135
|
+
const subscriberIds = getStringList(parsed.options, "subscriber");
|
|
3136
|
+
writeJson(
|
|
3137
|
+
out,
|
|
3138
|
+
await client.audiences.subscribers.resendConfirmation(
|
|
3139
|
+
audienceId,
|
|
3140
|
+
subscriberIds.length > 0 ? { subscriber_ids: subscriberIds } : {}
|
|
3141
|
+
)
|
|
3142
|
+
);
|
|
3143
|
+
return;
|
|
3144
|
+
}
|
|
2938
3145
|
if (action === "remove") {
|
|
2939
3146
|
const subscriberId = requirePositional(parsed.positional, 4, "subscriber id");
|
|
2940
3147
|
await client.audiences.subscribers.remove(audienceId, subscriberId);
|
|
@@ -3082,6 +3289,62 @@ async function runAudienceWelcome(client, parsed, out) {
|
|
|
3082
3289
|
}
|
|
3083
3290
|
throw new CliUsageError("Unknown audiences welcome command.");
|
|
3084
3291
|
}
|
|
3292
|
+
async function runAudienceSegments(client, parsed, out) {
|
|
3293
|
+
const action = parsed.positional[2];
|
|
3294
|
+
const audienceId = requirePositional(parsed.positional, 3, "audience id");
|
|
3295
|
+
if (action === "list") {
|
|
3296
|
+
writeJson(out, await client.audiences.segments.list(audienceId));
|
|
3297
|
+
return;
|
|
3298
|
+
}
|
|
3299
|
+
const idempotencyKey = getString(parsed.options, "idempotency-key");
|
|
3300
|
+
const methodOptions = idempotencyKey ? { idempotencyKey } : void 0;
|
|
3301
|
+
if (action === "create") {
|
|
3302
|
+
writeJson(
|
|
3303
|
+
out,
|
|
3304
|
+
await client.audiences.segments.create(
|
|
3305
|
+
audienceId,
|
|
3306
|
+
{
|
|
3307
|
+
name: getRequiredString(parsed.options, "name"),
|
|
3308
|
+
definition: await segmentDefinition(getRequiredString(parsed.options, "definition"))
|
|
3309
|
+
},
|
|
3310
|
+
methodOptions
|
|
3311
|
+
)
|
|
3312
|
+
);
|
|
3313
|
+
return;
|
|
3314
|
+
}
|
|
3315
|
+
const segmentId = requirePositional(parsed.positional, 4, "segment id");
|
|
3316
|
+
if (action === "get") {
|
|
3317
|
+
writeJson(out, await client.audiences.segments.get(audienceId, segmentId));
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3320
|
+
if (action === "update") {
|
|
3321
|
+
const name = getString(parsed.options, "name");
|
|
3322
|
+
const definitionInput = getString(parsed.options, "definition");
|
|
3323
|
+
if (name === void 0 && definitionInput === void 0) {
|
|
3324
|
+
throw new CliUsageError("Provide --name, --definition, or both.");
|
|
3325
|
+
}
|
|
3326
|
+
const definition = definitionInput === void 0 ? void 0 : await segmentDefinition(definitionInput);
|
|
3327
|
+
writeJson(
|
|
3328
|
+
out,
|
|
3329
|
+
await client.audiences.segments.update(
|
|
3330
|
+
audienceId,
|
|
3331
|
+
segmentId,
|
|
3332
|
+
{
|
|
3333
|
+
...name !== void 0 ? { name } : {},
|
|
3334
|
+
...definition ? { definition } : {}
|
|
3335
|
+
},
|
|
3336
|
+
methodOptions
|
|
3337
|
+
)
|
|
3338
|
+
);
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
3341
|
+
if (action === "delete") {
|
|
3342
|
+
await client.audiences.segments.delete(audienceId, segmentId);
|
|
3343
|
+
writeJson(out, { ok: true, id: segmentId });
|
|
3344
|
+
return;
|
|
3345
|
+
}
|
|
3346
|
+
throw new CliUsageError("Unknown audiences segments command.");
|
|
3347
|
+
}
|
|
3085
3348
|
async function runAudiences(client, parsed, out) {
|
|
3086
3349
|
const subcommand = parsed.positional[1];
|
|
3087
3350
|
if (subcommand === "subscribers") {
|
|
@@ -3096,6 +3359,10 @@ async function runAudiences(client, parsed, out) {
|
|
|
3096
3359
|
await runAudienceWelcome(client, parsed, out);
|
|
3097
3360
|
return;
|
|
3098
3361
|
}
|
|
3362
|
+
if (subcommand === "segments") {
|
|
3363
|
+
await runAudienceSegments(client, parsed, out);
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3099
3366
|
if (subcommand === "list") {
|
|
3100
3367
|
writeJson(
|
|
3101
3368
|
out,
|
|
@@ -3871,7 +4138,9 @@ async function runCli(argv, options = {}) {
|
|
|
3871
4138
|
else if (command === "scheduled-messages") await runScheduledMessages(client, parsed, out);
|
|
3872
4139
|
else if (command === "reply-scans") await runReplyScans(client, parsed, out);
|
|
3873
4140
|
else if (command === "webhooks") await runWebhooks(client, parsed, out);
|
|
4141
|
+
else if (command === "smtp-credentials") await runSmtpCredentials(client, parsed, out);
|
|
3874
4142
|
else if (command === "suppressions") await runSuppressions(client, parsed, out);
|
|
4143
|
+
else if (command === "customers") await runCustomers(client, parsed, out);
|
|
3875
4144
|
else if (command === "templates") await runTemplates(client, parsed, out);
|
|
3876
4145
|
else if (command === "newsletters") await runNewsletters(client, parsed, out);
|
|
3877
4146
|
else if (command === "newsletter-assets")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shipmail-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "Official CLI for the Shipmail API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"shipmail": "0.6.
|
|
47
|
+
"shipmail": "0.6.9",
|
|
48
48
|
"zod": "^4.6.5"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|