shipmail-cli 0.5.4 → 0.6.1
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 +6 -12
- package/dist/cli.js +23 -119
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Most commands require an API key. Create one in the Shipmail dashboard and expor
|
|
|
14
14
|
export SHIPMAIL_API_KEY="sm_live_..."
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
You can also pass `--api-key` to any command. Use `SHIPMAIL_BASE_URL` or `--base-url` when targeting a non-production API.
|
|
17
|
+
You can also pass `--api-key` to any command. Use `SHIPMAIL_BASE_URL` or `--base-url` when targeting a non-production API.
|
|
18
18
|
|
|
19
19
|
## Common Commands
|
|
20
20
|
|
|
@@ -125,14 +125,6 @@ shipmail webhooks create \
|
|
|
125
125
|
--events message.received,message.bounced
|
|
126
126
|
shipmail webhooks deliveries whk_123 --status failed
|
|
127
127
|
shipmail webhooks replay-delivery whk_123 dlv_123 --idempotency-key replay-123
|
|
128
|
-
|
|
129
|
-
# Partner beta lifecycle and delegated infrastructure
|
|
130
|
-
shipmail partner organizations create --name "Operator" --external-reference operator_123 --owner-email owner@example.com --mailbox-limit 3 --data-classification internal_test
|
|
131
|
-
shipmail partner usage
|
|
132
|
-
shipmail partner credential-grants list
|
|
133
|
-
shipmail partner credential-grants consume grant_123 --name "Embedded webmail"
|
|
134
|
-
shipmail domains list --organization-id 00000000-0000-4000-8000-000000000123
|
|
135
|
-
shipmail mailboxes create --organization-id 00000000-0000-4000-8000-000000000123 --domain-id dom_123 --address support --generate-password
|
|
136
128
|
```
|
|
137
129
|
|
|
138
130
|
Paragraph, quote, callout, list-item, and column bodies in `--blocks` accept bare text or
|
|
@@ -160,6 +152,7 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
|
|
|
160
152
|
- `api <METHOD> <PATH>`
|
|
161
153
|
- `domains list|search|create|get|verify|delete`
|
|
162
154
|
- `mailboxes list|create|get|suspend|resume|forwarding|forwarding-add|forwarding-remove|app-passwords|folders|identities|stage-attachment|prepare-attachment-upload`
|
|
155
|
+
- `members list|get`
|
|
163
156
|
- `inbox list|message|threads|thread|thread-state|draft|send-draft|reply-message|reply-thread|update|move`
|
|
164
157
|
- `reply-scans create|status|results`
|
|
165
158
|
- `messages send|list|analytics|get|reply` (test keys also support `--sandbox-outcome`)
|
|
@@ -174,9 +167,10 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
|
|
|
174
167
|
- `newsletter-domains list`
|
|
175
168
|
- `newsletters create|update|preview|preflight|test-send|schedule|cancel|resume`
|
|
176
169
|
- `newsletter-assets list|upload|register-url`
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
170
|
+
|
|
171
|
+
Member output uses the roles `owner`, `admin`, and `member`. Its `mailbox_access.scope` is `all` or
|
|
172
|
+
`selected`; `mailbox_ids` is empty for `all`, while selected IDs may be limited to mailboxes the
|
|
173
|
+
API key can reach.
|
|
180
174
|
|
|
181
175
|
Configure a publisher-owned Atom self URL and the number of recent issues:
|
|
182
176
|
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "shipmail";
|
|
16
16
|
|
|
17
17
|
// src/version.ts
|
|
18
|
-
var VERSION = "0.
|
|
18
|
+
var VERSION = "0.6.1";
|
|
19
19
|
|
|
20
20
|
// src/cli.ts
|
|
21
21
|
var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
|
|
@@ -166,9 +166,6 @@ function baseUrl(parsed, env) {
|
|
|
166
166
|
if (!raw) return DEFAULT_BASE_URL;
|
|
167
167
|
return validateBaseUrl(raw, env["SHIPMAIL_ALLOW_INSECURE_BASE_URL"] === "1");
|
|
168
168
|
}
|
|
169
|
-
function organizationId(parsed, env) {
|
|
170
|
-
return getString(parsed.options, "organization-id") ?? env["SHIPMAIL_ORGANIZATION_ID"];
|
|
171
|
-
}
|
|
172
169
|
function requireApiKey(parsed, env) {
|
|
173
170
|
const key = apiKey(parsed, env);
|
|
174
171
|
if (!key) {
|
|
@@ -181,7 +178,6 @@ function createClient(parsed, options) {
|
|
|
181
178
|
const config = {
|
|
182
179
|
apiKey: requireApiKey(parsed, env),
|
|
183
180
|
baseUrl: baseUrl(parsed, env),
|
|
184
|
-
organizationId: organizationId(parsed, env),
|
|
185
181
|
...options.fetch ? { fetch: options.fetch } : {}
|
|
186
182
|
};
|
|
187
183
|
return new ShipmailClient(config);
|
|
@@ -281,6 +277,7 @@ async function newsletterCreateParams(options) {
|
|
|
281
277
|
const archive_visibility = parseArchiveVisibility(options);
|
|
282
278
|
const feed_entry_url = getString(options, "feed-entry-url");
|
|
283
279
|
const styling_mode = parseStylingMode(options);
|
|
280
|
+
const track_engagement = getBoolean(options, "track-engagement");
|
|
284
281
|
const preview_text = getString(options, "preview-text");
|
|
285
282
|
return {
|
|
286
283
|
audience_id,
|
|
@@ -294,7 +291,8 @@ async function newsletterCreateParams(options) {
|
|
|
294
291
|
...send_window_hours !== void 0 ? { send_window_hours } : {},
|
|
295
292
|
...archive_visibility !== void 0 ? { archive_visibility } : {},
|
|
296
293
|
...feed_entry_url !== void 0 ? { feed_entry_url } : {},
|
|
297
|
-
...styling_mode !== void 0 ? { styling_mode } : {}
|
|
294
|
+
...styling_mode !== void 0 ? { styling_mode } : {},
|
|
295
|
+
...track_engagement !== void 0 ? { track_engagement } : {}
|
|
298
296
|
};
|
|
299
297
|
}
|
|
300
298
|
async function newsletterUpdateParams(options) {
|
|
@@ -307,6 +305,7 @@ async function newsletterUpdateParams(options) {
|
|
|
307
305
|
const archive_visibility = parseArchiveVisibility(options);
|
|
308
306
|
const feed_entry_url = getString(options, "feed-entry-url");
|
|
309
307
|
const styling_mode = parseStylingMode(options);
|
|
308
|
+
const track_engagement = getBoolean(options, "track-engagement");
|
|
310
309
|
const blocksInput = getString(options, "blocks");
|
|
311
310
|
const bodyHtmlInput = getString(options, "body-html");
|
|
312
311
|
const bodyTextInput = getString(options, "body-text");
|
|
@@ -325,7 +324,8 @@ async function newsletterUpdateParams(options) {
|
|
|
325
324
|
...send_window_hours !== void 0 ? { send_window_hours } : {},
|
|
326
325
|
...archive_visibility !== void 0 ? { archive_visibility } : {},
|
|
327
326
|
...feed_entry_url !== void 0 ? { feed_entry_url } : {},
|
|
328
|
-
...styling_mode !== void 0 ? { styling_mode } : {}
|
|
327
|
+
...styling_mode !== void 0 ? { styling_mode } : {},
|
|
328
|
+
...track_engagement !== void 0 ? { track_engagement } : {}
|
|
329
329
|
};
|
|
330
330
|
if (Object.keys(params).length === 0) {
|
|
331
331
|
throw new CliUsageError("Provide at least one field to update.");
|
|
@@ -358,10 +358,6 @@ async function rawApiRequest(parsed, runOptions, method, path, body, authRequire
|
|
|
358
358
|
const key = apiKey(parsed, env);
|
|
359
359
|
if (key) headers["Authorization"] = `Bearer ${key}`;
|
|
360
360
|
}
|
|
361
|
-
const targetOrganizationId = organizationId(parsed, env);
|
|
362
|
-
if (targetOrganizationId) {
|
|
363
|
-
headers["X-Shipmail-Organization-Id"] = targetOrganizationId;
|
|
364
|
-
}
|
|
365
361
|
let requestBody;
|
|
366
362
|
if (body !== void 0) {
|
|
367
363
|
headers["Content-Type"] = "application/json";
|
|
@@ -479,8 +475,8 @@ Usage:
|
|
|
479
475
|
--name "July changelog" \\
|
|
480
476
|
--subject "What shipped in July" \\
|
|
481
477
|
--blocks @blocks.json \\
|
|
482
|
-
[--feed-entry-url https://example.com/post] [--styling-mode styled|plain]
|
|
483
|
-
shipmail newsletters update nws_789 --subject "Updated subject" --blocks @blocks-v2.json [--feed-entry-url https://example.com/post] [--styling-mode styled|plain]
|
|
478
|
+
[--feed-entry-url https://example.com/post] [--styling-mode styled|plain] [--track-engagement true|false]
|
|
479
|
+
shipmail newsletters update nws_789 --subject "Updated subject" --blocks @blocks-v2.json [--feed-entry-url https://example.com/post] [--styling-mode styled|plain] [--track-engagement true|false]
|
|
484
480
|
shipmail newsletters preview <id>
|
|
485
481
|
shipmail newsletters preflight <id>
|
|
486
482
|
shipmail newsletters test-send <id> --recipient-email owner@example.com
|
|
@@ -510,9 +506,9 @@ Usage:
|
|
|
510
506
|
shipmail audiences subscribers resubscribe <audience-id> <subscriber-id> --consent-source "Signup form"
|
|
511
507
|
shipmail audiences subscribers remove <audience-id> <subscriber-id>
|
|
512
508
|
shipmail calendar events list --mailbox hello@example.com --from 2026-08-01T00:00:00Z --to 2026-08-08T00:00:00Z [--expand true] [--limit 100]
|
|
513
|
-
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]
|
|
509
|
+
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]
|
|
514
510
|
shipmail calendar events get <event-id> --mailbox hello@example.com
|
|
515
|
-
shipmail calendar events update <event-id> --mailbox hello@example.com [--title "..."] [--start ...] [--invitation-language en|fr|es]
|
|
511
|
+
shipmail calendar events update <event-id> --mailbox hello@example.com [--title "..."] [--start ...] [--invitation-language en|fr|es] [--operation-key key] [--expected-revision sha256] [--send-guest-updates true|false]
|
|
516
512
|
shipmail calendar events delete <event-id> --mailbox hello@example.com
|
|
517
513
|
shipmail calendar availability --mailbox hello@example.com --from 2026-08-03T00:00:00Z --to 2026-08-04T00:00:00Z [--duration 30] [--days 1,2,3,4,5] [--window-start 540] [--window-end 1020]
|
|
518
514
|
shipmail booking-pages list [--limit 25] [--cursor cur]
|
|
@@ -520,17 +516,6 @@ Usage:
|
|
|
520
516
|
shipmail booking-pages get <id>
|
|
521
517
|
shipmail booking-pages update <id> [--name "..."] [--conferencing-provider zoom|google_meet|none] [--active false]
|
|
522
518
|
shipmail booking-pages delete <id>
|
|
523
|
-
shipmail partner organizations list
|
|
524
|
-
shipmail partner organizations create --name "Operator" --external-reference op_123 --owner-email owner@example.com [--mailbox-limit 3] [--data-classification internal_test|customer]
|
|
525
|
-
shipmail partner organizations get <id>
|
|
526
|
-
shipmail partner organizations update <id> [--name "Operator"] [--mailbox-limit 5]
|
|
527
|
-
shipmail partner organizations invite <id> [--owner-email owner@example.com]
|
|
528
|
-
shipmail partner organizations suspend <id>
|
|
529
|
-
shipmail partner organizations resume <id>
|
|
530
|
-
shipmail partner organizations offboard <id>
|
|
531
|
-
shipmail partner credential-grants list
|
|
532
|
-
shipmail partner credential-grants consume <grant-id> [--name "Marketstead webmail"] [--expires-at 2026-10-01T00:00:00Z] [--allowed-cidr 203.0.113.0/24]
|
|
533
|
-
shipmail partner usage
|
|
534
519
|
|
|
535
520
|
Booking page conferencing:
|
|
536
521
|
--conferencing-provider <zoom|google_meet> Create a meeting for every booking.
|
|
@@ -540,9 +525,16 @@ Conversation IDs:
|
|
|
540
525
|
<thread-id> accepts a conversation ID (thd_...) or the mail-server thread ID. Store the
|
|
541
526
|
conversation_id every response carries. thread_id is deprecated and unchanged.
|
|
542
527
|
|
|
528
|
+
Member access output:
|
|
529
|
+
role is owner, admin, or member. mailbox_access.scope is all or selected. mailbox_ids is empty
|
|
530
|
+
for all; selected IDs may be limited to mailboxes the API key can reach.
|
|
531
|
+
|
|
543
532
|
Newsletter styling:
|
|
544
533
|
--styling-mode <styled|plain> styled applies Shipmail's email theme. plain sends your HTML without injected styles, width, or centering, so the reader's email client styles it.
|
|
545
534
|
|
|
535
|
+
Newsletter tracking:
|
|
536
|
+
--track-engagement <true|false> Track opens and clicks for this issue. Off by default. Changes only while the issue is a draft or scheduled for later.
|
|
537
|
+
|
|
546
538
|
Newsletter block prose:
|
|
547
539
|
Paragraph, quote, callout, list-item, and column bodies accept bare text or sanitized inline HTML. Use <p> and <br> for line breaks.
|
|
548
540
|
|
|
@@ -552,7 +544,6 @@ Newsletter feeds:
|
|
|
552
544
|
Global options:
|
|
553
545
|
--api-key <key> API key. Defaults to SHIPMAIL_API_KEY.
|
|
554
546
|
--base-url <url> API base URL. Defaults to SHIPMAIL_BASE_URL or ${DEFAULT_BASE_URL}.
|
|
555
|
-
--organization-id Delegated child organization for supported infrastructure commands.
|
|
556
547
|
--header "K: V" Extra header for the generic api command. Repeatable.
|
|
557
548
|
|
|
558
549
|
Environment:
|
|
@@ -560,7 +551,6 @@ Environment:
|
|
|
560
551
|
SHIPMAIL_BASE_URL Optional API base URL. Must be https on shipmail.to.
|
|
561
552
|
SHIPMAIL_ALLOW_INSECURE_BASE_URL=1
|
|
562
553
|
Permit a non-https or non-shipmail.to base URL (development only).
|
|
563
|
-
SHIPMAIL_ORGANIZATION_ID Optional delegated child organization ID.
|
|
564
554
|
`;
|
|
565
555
|
}
|
|
566
556
|
function requirePositional(positional, index, label) {
|
|
@@ -2024,6 +2014,8 @@ async function runCalendarEvents(client, parsed, out) {
|
|
|
2024
2014
|
title: getRequiredString(options, "title"),
|
|
2025
2015
|
start: getRequiredString(options, "start"),
|
|
2026
2016
|
calendar_id: getString(options, "calendar-id"),
|
|
2017
|
+
operation_key: getString(options, "operation-key"),
|
|
2018
|
+
send_invitations: getBoolean(options, "send-invitations"),
|
|
2027
2019
|
...await calendarEventFields(options)
|
|
2028
2020
|
};
|
|
2029
2021
|
writeJson(out, await client.calendar.events.create(params));
|
|
@@ -2043,6 +2035,9 @@ async function runCalendarEvents(client, parsed, out) {
|
|
|
2043
2035
|
mailbox: getRequiredString(options, "mailbox"),
|
|
2044
2036
|
title: getString(options, "title"),
|
|
2045
2037
|
start: getString(options, "start"),
|
|
2038
|
+
operation_key: getString(options, "operation-key"),
|
|
2039
|
+
expected_revision: getString(options, "expected-revision"),
|
|
2040
|
+
send_guest_updates: getBoolean(options, "send-guest-updates"),
|
|
2046
2041
|
...await calendarEventFields(options)
|
|
2047
2042
|
};
|
|
2048
2043
|
writeJson(out, await client.calendar.events.update(id, params));
|
|
@@ -2200,96 +2195,6 @@ async function runAutomations(client, parsed, out) {
|
|
|
2200
2195
|
}
|
|
2201
2196
|
throw new CliUsageError("Unknown automations command.");
|
|
2202
2197
|
}
|
|
2203
|
-
async function runPartner(client, parsed, out) {
|
|
2204
|
-
const group = parsed.positional[1];
|
|
2205
|
-
if (group === "usage") {
|
|
2206
|
-
writeJson(out, await client.partner.usage());
|
|
2207
|
-
return;
|
|
2208
|
-
}
|
|
2209
|
-
if (group === "credential-grants") {
|
|
2210
|
-
const command2 = requirePositional(parsed.positional, 2, "credential grant command");
|
|
2211
|
-
if (command2 === "list") {
|
|
2212
|
-
writeJson(out, await client.partner.listMailboxCredentialGrants());
|
|
2213
|
-
return;
|
|
2214
|
-
}
|
|
2215
|
-
if (command2 !== "consume")
|
|
2216
|
-
throw new CliUsageError("Unknown partner credential-grants command.");
|
|
2217
|
-
const grantId = requirePositional(parsed.positional, 3, "credential grant id");
|
|
2218
|
-
const name = getString(parsed.options, "name");
|
|
2219
|
-
const expiresAt = getString(parsed.options, "expires-at");
|
|
2220
|
-
const allowedCidrs = getStringList(parsed.options, "allowed-cidr");
|
|
2221
|
-
writeJson(
|
|
2222
|
-
out,
|
|
2223
|
-
await client.partner.consumeMailboxCredentialGrant(grantId, {
|
|
2224
|
-
...name ? { name } : {},
|
|
2225
|
-
...expiresAt ? { expires_at: expiresAt } : {},
|
|
2226
|
-
...allowedCidrs.length > 0 ? { allowed_cidrs: allowedCidrs } : {}
|
|
2227
|
-
})
|
|
2228
|
-
);
|
|
2229
|
-
return;
|
|
2230
|
-
}
|
|
2231
|
-
if (group !== "organizations") throw new CliUsageError("Unknown partner command.");
|
|
2232
|
-
const command = parsed.positional[2];
|
|
2233
|
-
if (command === "list") {
|
|
2234
|
-
writeJson(out, await client.partner.listOrganizations());
|
|
2235
|
-
return;
|
|
2236
|
-
}
|
|
2237
|
-
if (command === "create") {
|
|
2238
|
-
const classification = getString(parsed.options, "data-classification") ?? "customer";
|
|
2239
|
-
if (classification !== "internal_test" && classification !== "customer") {
|
|
2240
|
-
throw new CliUsageError("--data-classification must be internal_test or customer.");
|
|
2241
|
-
}
|
|
2242
|
-
const params = {
|
|
2243
|
-
name: getRequiredString(parsed.options, "name"),
|
|
2244
|
-
external_reference: getRequiredString(parsed.options, "external-reference"),
|
|
2245
|
-
owner_email: getRequiredString(parsed.options, "owner-email"),
|
|
2246
|
-
mailbox_limit: getNumber(parsed.options, "mailbox-limit") ?? 3,
|
|
2247
|
-
data_classification: classification
|
|
2248
|
-
};
|
|
2249
|
-
writeJson(out, await client.partner.createOrganization(params));
|
|
2250
|
-
return;
|
|
2251
|
-
}
|
|
2252
|
-
const id = requirePositional(parsed.positional, 3, "partner organization id");
|
|
2253
|
-
if (command === "get") {
|
|
2254
|
-
writeJson(out, await client.partner.getOrganization(id));
|
|
2255
|
-
return;
|
|
2256
|
-
}
|
|
2257
|
-
if (command === "update") {
|
|
2258
|
-
const name = getString(parsed.options, "name");
|
|
2259
|
-
const mailboxLimit = getNumber(parsed.options, "mailbox-limit");
|
|
2260
|
-
if (name === void 0 && mailboxLimit === void 0) {
|
|
2261
|
-
throw new CliUsageError("Provide --name or --mailbox-limit.");
|
|
2262
|
-
}
|
|
2263
|
-
const params = {
|
|
2264
|
-
...name !== void 0 ? { name } : {},
|
|
2265
|
-
...mailboxLimit !== void 0 ? { mailbox_limit: mailboxLimit } : {}
|
|
2266
|
-
};
|
|
2267
|
-
writeJson(out, await client.partner.updateOrganization(id, params));
|
|
2268
|
-
return;
|
|
2269
|
-
}
|
|
2270
|
-
if (command === "invite") {
|
|
2271
|
-
writeJson(
|
|
2272
|
-
out,
|
|
2273
|
-
await client.partner.resendOwnershipInvitation(id, {
|
|
2274
|
-
owner_email: getString(parsed.options, "owner-email")
|
|
2275
|
-
})
|
|
2276
|
-
);
|
|
2277
|
-
return;
|
|
2278
|
-
}
|
|
2279
|
-
if (command === "suspend") {
|
|
2280
|
-
writeJson(out, await client.partner.suspendOrganization(id));
|
|
2281
|
-
return;
|
|
2282
|
-
}
|
|
2283
|
-
if (command === "resume") {
|
|
2284
|
-
writeJson(out, await client.partner.resumeOrganization(id));
|
|
2285
|
-
return;
|
|
2286
|
-
}
|
|
2287
|
-
if (command === "offboard") {
|
|
2288
|
-
writeJson(out, await client.partner.offboardOrganization(id));
|
|
2289
|
-
return;
|
|
2290
|
-
}
|
|
2291
|
-
throw new CliUsageError("Unknown partner organizations command.");
|
|
2292
|
-
}
|
|
2293
2198
|
function writeError(err, errOut) {
|
|
2294
2199
|
if (err instanceof CliUsageError) {
|
|
2295
2200
|
errOut.write(`Error: ${err.message}
|
|
@@ -2375,7 +2280,6 @@ async function runCli(argv, options = {}) {
|
|
|
2375
2280
|
else if (command === "audiences") await runAudiences(client, parsed, out);
|
|
2376
2281
|
else if (command === "calendar") await runCalendar(client, parsed, out);
|
|
2377
2282
|
else if (command === "booking-pages") await runBookingPages(client, parsed, out);
|
|
2378
|
-
else if (command === "partner") await runPartner(client, parsed, out);
|
|
2379
2283
|
else throw new CliUsageError(`Unknown command: ${command}.`);
|
|
2380
2284
|
return 0;
|
|
2381
2285
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shipmail-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Official CLI for the Shipmail API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"shipmail": "0.
|
|
44
|
+
"shipmail": "0.6.2"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"tsup": "^8.4.0",
|