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.
Files changed (3) hide show
  1. package/README.md +13 -1
  2. package/dist/cli.js +278 -9
  3. 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 subscribers add|list|get|by-email|update|unsubscribe|resubscribe|remove`
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.7";
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
- var SUPPRESSION_SCOPES = ["all", "marketing"];
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", SUPPRESSION_SCOPES);
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.7",
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.8",
47
+ "shipmail": "0.6.9",
48
48
  "zod": "^4.6.5"
49
49
  },
50
50
  "devDependencies": {