Skip to main content

Sequenzy CLI

The Sequenzy CLI lets you manage subscribers, run campaigns, send emails, manage your team, triage inbox replies, operate outbound webhooks, and view analytics directly from your terminal. It also lets you inspect, supply, and AI-sync localized template variants.

Installation

Use the scoped package name for one-off runs: npx @sequenzy/cli ... or bunx @sequenzy/cli .... The unscoped sequenzy package is the TypeScript API library and does not expose a CLI binary.

Using the CLI with AI agents

If an AI agent will run the CLI for you, also install the Sequenzy skill. It gives your agent a versioned workflow guide for operating the CLI safely:
Learn more in the Skills documentation.

Authentication

Login

This prints a login URL and approval code in the terminal. Open the link in any browser, approve the session, and keep the terminal open until the CLI stores your API key. This flow also works in headless or agent-driven environments where the CLI cannot launch a browser for you.

Check login status

whoami checks local credential state only. To validate the credential that would actually reach Sequenzy—including a SEQUENZY_API_KEY environment override—and inspect its non-secret identity and permissions, run:

Logout

Commands

Destructive commands (campaigns delete, ab-tests delete, ab-tests delete-variant, lists delete, segments delete, tags delete, webhooks delete, and team cancel-invitation) ask for confirmation before running. Pass --yes (or -y) to skip the prompt. When stdin is not interactive - in scripts, CI, or agent-driven runs - --yes is required and the command fails without it.

Subscribers

subscribers tags add and subscribers tags remove only touch contacts that already exist: unknown emails, external IDs, and subscriber IDs are reported, never created. Tag automations stay off unless you pass --trigger-automations on add, which is what historical backfills want. The CLI chunks targets into 500-contact API requests and reports combined totals, so a multi-thousand-row reconciliation is a single command. Subscriber imports accept columns such as email, full/first/last name, external ID, phone, status, and tags. Additional CSV columns become typed custom attributes. Imports run asynchronously; a completed import may still contain row failures, so check import-status --json. Use --opt-in-mode confirmed only when the source CRM contains verified consent.

Lists

Use --private to omit a list from individual controls on the hosted subscriber email preferences/unsubscribe page. Use --no-private to make its name and description subscriber-facing there. List privacy does not override a subscriber’s global unsubscribe. The CLI splits large files into API-safe batches automatically. CSV files can use an email, e-mail, email address, or mail header; if no email header exists, the first column is used. Use this command instead of looping over subscribers add; standard API rate limits are 100 requests per minute per API key and 20 requests per second burst. lists remove-subscribers accepts the same email inputs as add-subscribers (--email, --emails-json, or --emails-file) and only removes list memberships; the subscribers stay in your audience. Emails that do not match a subscriber are reported as not found. Deleting a list removes all of its memberships but keeps the subscribers.

Send Email

Send transactional emails to single recipients:
--template takes a transactional API slug and maps it to the API’s slug field. Direct --html and --html-file content maps to the API’s canonical body field and requires --subject. Template and direct-content flags cannot be combined. If the recipient email matches a stored subscriber, saved first and last names fill omitted name variables automatically. Explicit --var or --vars-json values take precedence. --no-track-clicks disables click-link rewriting for a single send, which keeps iOS and Android universal links working. --no-track-opens removes the open-tracking pixel for a single send. Both flags are opt-out only: they cannot enable tracking your account has disabled.

Statistics

Sequence stats include a Current Enrollments section with live active and waiting enrollment-run counts grouped by current node. These live counts are not constrained by --period, --start, or --end; use --json for the complete enrollmentCounts.byCurrentNode array. Use sequenzy email-sends list --search "Welcome" --status opened to discover recent deliveries by subject/title, recipient, status, type, or source. Then use sequenzy email-sends get <emailSendId> to inspect one delivery’s open/click timestamps and complete event timeline. Add --all to retrieve every matching page. Export RFC 4180 CSV to stdout with --csv, or write a complete file with sequenzy email-sends list --all --csv email-sends.csv. List and detail output include dashboard URLs, and the command prints a concrete follow-up using the first returned delivery ID. The CSV includes subscriberId, automationNodeId, and abTestVariantId alongside recipientEmail, subject, and the sent/delivered/opened/clicked timestamps, so one command produces a recipient-level delivery matrix for a whole sequence:
Transactional template listing supports the dashboard filters and metrics:
Campaign and saved transactional-email stats include a Clicked Links section with the top destination URLs, click counts, and each link’s share of all link clicks; saved transactional stats also show complaint, reply, bounce-type, and human-versus-machine engagement breakdowns. Add --json to receive the structured fields.

Campaigns

campaigns list is paginated. The default page size is 50 and --limit is capped at 100, so a single call is not guaranteed to return every campaign. The header line reads Campaigns (<returned> of <total>), and when more results remain the command prints the exact --offset to pass next. With --json the response carries a pagination object (limit, offset, count, total, hasMore); keep advancing --offset while hasMore is true to enumerate the full set. sequences list accepts the same --limit and --offset flags, and returns every sequence when both are omitted. Rejected campaign list and detail output include the reviewer feedback when it was provided. The same value is available as rejectionComment with --json.
Scheduling requires a future ISO timestamp and a verified sending domain.
campaigns schedule can report waiting_approval instead of scheduled. That means the campaign was held for safety review, which is most common on new accounts and recently registered sending domains. It is a successful result, not an error: nothing sends while the campaign is held, and running campaigns schedule again will not clear it. Scripts should read status from --json output and poll campaigns get <id> rather than retry. With --repeat weekly or --repeat monthly, the campaign becomes a recurring template: each run is duplicated and sent automatically at the cadence, re-evaluating list or segment membership every time. Use campaigns unschedule to stop the series and return the template to an editable draft, or re-schedule without --repeat to keep a one-shot send. campaigns create accepts --prompt to generate the campaign email, including subject, preview text, and blocks. Use --preview-text or --preheader-text to set the inbox preview text yourself; with --prompt, that explicit value overrides the generated preview. campaigns create and campaigns update also accept raw HTML or Sequenzy block JSON via --blocks-json or --blocks-file. Use block input when you need editor-compatible content, conditional blocks, repeat blocks, or Poll and NPS survey blocks. Campaign stats render answer distributions and NPS scores automatically; add --json to receive the complete top-level polls array. campaigns create can also save the audience on the draft: pass --segment <id> to target one saved segment, or --target-lists-json/--target-lists-file for any targeting shape, such as {"type":"all"}, {"type":"lists","listIds":["list_abc123"]}, {"type":"segment","segmentId":"seg_abc123"}, or {"type":"rules","include":[{"type":"lists","listIds":["list_abc123"]}],"exclude":[{"type":"segments","segmentIds":["seg_abc123"]}]}. --segment and the target list flags are mutually exclusive. campaigns update accepts the same three flags to retarget a draft, plus --clear-target-lists to remove saved targeting again, and campaigns schedule accepts them to choose the audience as you schedule; scheduling without them reuses the saved targeting, or falls back to all active subscribers when none was saved. campaigns create, campaigns update, and campaigns get all print the saved audience on the Audience line and return it as targetLists with --json. campaigns unschedule removes a scheduled send and any recurrence, then returns the campaign to draft so it can be edited and scheduled again. campaigns cancel stops scheduled, paused, waiting-approval, and actively sending campaigns immediately. It skips the confirmation prompt on purpose so you can stop a bad send fast, and it cannot be undone. Only sending campaigns can be paused, and only paused campaigns can be resumed; pass --spread-over-hours (1-72) to spread the remaining delivery over a longer window. Sending, scheduled, and paused campaigns must be cancelled before they can be deleted. campaigns duplicate defaults to --mode campaign, which copies the campaign and its email. Use --mode ab_test to also copy the campaign’s A/B test with all variants, or --mode variant with --variant-id to copy a single variant’s content as the new campaign email. campaigns render, sequences render, and templates render return the exact email-safe HTML that would be sent, so you can embed a true visual preview in your own dashboard or email builder. The render applies your email theme and font, company branding and footer, localization, block visibility conditions, product recommendation blocks, and merge tags. Personalize with --subscriber <id> for a stored subscriber, or --email you@example.com plus optional --first-name/--last-name for an ad-hoc contact. Give that ad-hoc contact custom attributes with repeatable --attr key=value flags, or --attrs-json for values that need to be numbers, booleans, or nested objects, so {{subscriber.*}} tags resolve against it. Add merge variables with repeatable --var key=value flags, or --vars-json in the same way. Only a stored subscriber can resolve subscriber-scoped content such as block conditions, product recommendations, and a working unsubscribe link. With no contact at all, the email renders for a sample contact and contact-specific merge tags resolve to empty values. Rendering itself needs only a read scope on the campaign, sequence, or template. --subscriber puts that subscriber’s details into the HTML, so it additionally requires the subscribers:read scope. Use --out <file> to write the HTML to disk, or --out - to stream the raw document to stdout for piping. Without --out, the command prints a summary (subject, preview text, locale, personalization, tracking, byte size). --json returns the full payload including html. Because an email never shows a raw {{TAG}} to a recipient, a tag that does not exist and a tag that is merely blank both come out as an empty string. The summary calls out both, and --json returns them as unresolvedMergeTags: reason unknown means nothing provides that name and it will be empty for every recipient (usually a typo, or a tag carried over from another platform), while no_value means the name is recognized but blank for the previewed contact. A name is only called unknown when the render had a source to check it against. Without the contact’s attributes nothing is checkable at all - a bare {{plan}} reads the same attribute map as {{subscriber.plan}} - so pass --subscriber, or --email together with --attr/--attrs-json, to catch typos. Beyond that, {{event.*}} needs sample event properties, which you pass as --vars-json '{"event": {"itemCount": 3}}', since a real send fills those from the enrolling event, and {{recommendedProducts.*}} needs --subscriber and a catalog with something to recommend. templates render on a transactional email needs --var/--vars-json, because its tags are filled from the variables of each send call and nothing marks them as such. Without those, the tags come back as no_value rather than being reported as typos. An optional attribute the previewed contact never had set is kept off the typo list by checking the names other contacts in your account carry, which needs the subscribers:read scope on the key you are logged in with.
sequences render on a step downstream of a create-discount step fills {{discount.code}} and the other {{discount.*}} tags from that step’s real configuration, with a placeholder TEST-CODE for the code itself, so discount copy can be checked before anyone is enrolled. Links stay clean by default. Pass --tracking to apply your company’s auto-UTM decoration as a real send would. Per-send click redirects and the open pixel are added at send time against a real email send record, so they never appear in a render. campaigns resend-to-non-openers creates a draft that targets the same audience as a sent campaign plus a “didn’t open this campaign” rule, so only people who haven’t opened it yet receive the resend. It’s available 6 hours after the campaign finishes sending (to give opens time to register), and the response includes an estimate of how many subscribers haven’t opened the original. The draft is not sent automatically - review it, then schedule or send it.

Landing Pages

landing-pages create, update, publish, and unpublish can accept full builder JSON with --content-json or --content-file. landing-pages duplicate copies an existing page into a new draft with its own slug and stats. Built-in template keys include from-scratch, waitlist, lead-magnet, launch, demo-request, webinar, newsletter, product-hunt, pricing-offer, agency-lead-gen, and feature-announcement. Custom landing page domains need a CNAME record pointing to pages.sequenzydns.com; run update-domain-settings --verify after DNS propagates.

A/B Tests

Use get first to inspect the effective settings object and discover variant IDs. Campaign settings are editable only in draft. Sequence settings can be updated later with --confirm-live-change when the test is active or has recorded activity. ab-tests create accepts either a campaign ID or --automation-node. Campaigns must be in draft or rejected status; an automation node must be an email step and is converted to a dashboard A/B-test node. Add --confirm-live-change when converting a node in an active sequence. Control variant A is created automatically from the current email; sequence variants receive independent email templates. Extra variants come from --variants-json or --variants-file (each entry takes subject, optional previewText, and optional blocks). Campaign tests use audience percentage and duration; sequence tests use --test-type, --winner-threshold, and --winner-criteria. An explicit sequence winner criterion overrides the test-type default. Variants can only be added or removed while the test is in draft status, variant A is the control and cannot be deleted, and running tests cannot be deleted. When a sequence test’s parent sequence is active, add-variant and delete-variant also require --confirm-live-change because they immediately change the live rotation.

Sequences

sequences list, sequences get, and sequences update print the stored status together with the resolved run state, because active alone is ambiguous: a sequence with paused enrollment still emails its current recipients but takes nobody new. The four run states you will see are active (running), active (new enrollments paused, existing recipients continue), paused (recipients frozen, nobody new enters), and draft/archived (not running). enable, disable, pause-enrollments, and resume-enrollments print the resulting state on a State: line. With --json the same information is available as effectiveStatus, acceptsNewEnrollments, processesExistingEnrollments, and effectiveStatusSummary.
sequences enrollments is the contact-level view behind the counts in stats --sequence. It defaults to active and waiting enrollments and prints each contact’s email, name, current node, and scheduled resume time. Filter with --node-id, --status, --subscriber-id, or --email, sort with --sort wait_until_asc to see who resumes next, and use --all --csv <file> to export every match. Node IDs come from sequences get or from enrollmentCounts.byCurrentNode in stats --sequence. sequences cancel-enrollments takes exactly one target: --all, --subscriber-id, --subscriber-ids, or --field-values. Use --all to fully stop a live sequence, including segment-triggered contacts that share no entry field value - pausing enrollment only blocks new entrants, it does not remove contacts already mid-flight. Without --apply the command reports matches and changes nothing. With --apply it keeps calling the API until every match is drained, printing running progress, because each request cancels at most 1,000 enrollments. sequences enroll accepts subscriber IDs with --subscriber-ids, the same email inputs as lists add-subscribers (--email, --emails-json, or --emails-file), or both. Each API request is capped at 500 total targets across emails and subscriber IDs. Only active subscribers are enrolled: unknown emails are reported as not found, and inactive, unavailable, or already enrolled subscribers are skipped. Enrollment starts at the first step after the trigger unless you pass --target-node-id, and the sequence must be accepting entrants (enabled and not paused for enrollment). sequences test sends only the selected saved email step. Use sequences get to find its nodeId, then pass one to ten internal addresses after --to. The JSON response includes one durable emailSendId per recipient for delivery inspection. For sequences update, use sequences get first to find nodeId and emailId values. Metadata flags include --description, repeatable --label (or --clear-labels to remove every label), --user-cancellable/--not-user-cancellable, and sequence or per-step sender/reply identity. Trigger replacement uses --trigger plus its typed trigger flags and is atomic; on an active sequence it requires --confirm-live-change. --steps-file and --emails-file edit existing email steps by nodeId, emailId, or array order; items can set transactional mode and CC/BCC recipients. An email item can set emailPreset to minimal or branded for native Sequenzy blocks, including emails that contain supported custom HTML blocks, using the same format transformation as the dashboard. An email stored entirely as one standalone raw HTML block does not support emailPreset, and emailPreset cannot be combined with html or htmlContent. --insert-steps-file inserts new linear steps with an optional afterNodeId, including delay, tag/list, outbound webhook, condition, discount, subscriber-update, SMS, and logic_wait_for_event nodes with typed config. Tag, list, wait, condition, and webhook steps put their node fields in config: action_add_tag/action_remove_tag take tagName (or tagId when you have the real tag ID), action_add_to_list/action_remove_from_list take listId, logic_wait_for_event takes eventName with timeoutDays/timeoutAction, logic_condition takes conditionType plus its resource field, and action_webhook takes url with optional method/headers. SMS, discount, and delay steps keep using their step-level fields. Use --branch-file for a new if/else node; each path can provide new steps, an existing targetNodeId, or both, while elseTargetNodeId wires the fallback. Do not combine branch insertion, linear insertion, or a graph edit in the same command. When the target sequence is active, structural edits such as --insert-steps-*, --branch-*, or --graph-edit-* require --confirm-structural-change after you confirm the live-flow impact. Content-only --steps-* and --emails-* edits do not require that flag. Deleting a step with --graph-edit-* immediately moves parked recipients to its unique surviving successor, or completes them when no successor remains. The command reports both affected counts in human output and under sequence.migratedRecipientCount / sequence.completedRecipientCount with --json.

Templates

templates list includes localization sync status by locale. templates get includes the full localized variants and the effective company localization config. templates localizations set requires one enabled non-primary locale, a subject, and exactly one HTML or blocks source. templates localizations sync works even when automatic on-save localization is disabled. When you pass blocks JSON, each block can include a conditions array for recipient-specific content. Use field: "variable" to branch on a value passed in the send’s variables or an automation event payload:
In a variable condition, the text before the colon in value is a merge-tag path (without {{ }}), including nested paths like order.total or event.plan, and the text after it is the comparison value. field can also be attribute (same name:value form) or email / firstName / lastName. Operators: is, is_not, contains, not_contains, gt, gte, lt, lte, is_empty, is_not_empty. For if/else, add a conditional-group block with ifBranch.children and elseBranch.children.

Companies

companies get includes product info, brand colors, AI writing context, the account-wide From and Reply-To defaults, and the effective email localization settings for that company. companies update edits the product info that AI uses for generated emails, including primaryColor, companyContext, toneVoice, value props, testimonials, and related brand fields. --email-theme-json / --email-theme-file edit the company’s default email theme (presetId, colors, typography, layout); updates are partial, and passing null resets the theme to the platform default. --from-email / --reply-to set the account-wide sending defaults, creating the profile when one does not exist yet (a From address needs a verified sending domain). --from-name and --reply-to-name on their own rename the display name of the current default profile without changing its address, so campaigns and sequences pinned to that profile pick up the new name. One address can carry several display names, so pass --sender-profile-id (or --reply-profile-id) to select exactly which profile becomes the default and which one --from-name renames. Run sequenzy sender-profiles list to see the IDs. Renaming with --from-email on an address that has several display names is rejected, since there is no way to tell which identity you meant.

Segments

For Stripe product segments, use the Stripe product ID, not the product name. The CLI maps:
  • bought -> field: "stripeProduct", operator: "is", value: "prod_xxx"
  • didnt-buy -> field: "stripeProduct", operator: "is_not", value: "prod_xxx"
  • at-least -> field: "stripeProduct", operator: "at_least", value: "prod_xxx:3"
  • less-than -> field: "stripeProduct", operator: "less_than_count", value: "prod_xxx:3"
For purchased-product segments (--commerce-product), use provider:productId where the provider is shopify, woocommerce, or api and the product ID is the one your orders carry in their line items. Product IDs are provider-scoped (Shopify product 42 and WooCommerce product 42 are different products); a bare ID without a provider prefix matches the ID on any provider. The CLI maps:
  • bought -> field: "commerceProduct", operator: "is", value: "shopify:42"
  • didnt-buy -> field: "commerceProduct", operator: "is_not", value: "shopify:42"
  • at-least -> field: "commerceProduct", operator: "at_least", value: "shopify:42:2" (use --orders)
  • less-than -> field: "commerceProduct", operator: "less_than_count", value: "shopify:42:2" (use --orders)
For purchased-collection segments (--commerce-collection), pass a collection ID or handle (for example skincare). The filter matches anyone whose orders contain any product currently in that collection, so it is the practical way to segment a large catalog without listing products one by one. You can prefix a provider (shopify:skincare) to scope the lookup. The CLI maps:
  • bought -> field: "commerceCollection", operator: "is", value: "skincare"
  • didnt-buy -> field: "commerceCollection", operator: "is_not", value: "skincare"
  • at-least -> field: "commerceCollection", operator: "at_least", value: "skincare:2" (use --orders)
  • less-than -> field: "commerceCollection", operator: "less_than_count", value: "skincare:2" (use --orders)
Collection membership comes from your synced catalog as it stands right now, not as it stood when the order was placed - moving a product out of a collection removes its past buyers from the segment. Shopify collections and WooCommerce categories are synced automatically; products pushed through the Commerce API do not carry collections yet, so this filter does not match them. For trial timing, pass --filter-json directly:
  • {"field":"stripeTrialProduct","operator":"is","value":"prod_xxx:is_canceled"}
  • {"field":"stripeTrialProduct","operator":"gte","value":"prod_xxx:start_at:7 days ago"}
  • {"field":"stripeTrialProduct","operator":"is","value":"prod_xxx:end_at:2026-05-26"}
Engagement filters (emailSent, emailOpened, emailClicked, emailBounced, emailComplained) accept a rolling time window (7d, 30d, 90d, 180d, all), a specific sent campaign (campaign:<campaign_id>), or - with the at_least / less_than_count operators - a count with a time window (count:timeRange, like 10:30d or 10:all). For example, {"field":"emailClicked","operator":"at_least","value":"10:all"} matches subscribers who clicked 10 or more times ever. Combine two campaign-specific filters with the default --match all to express rules like “bounced campaign A but not campaign B”. Look up campaign IDs with sequenzy campaigns list. --filter-json accepts the same filter shape used by the API and MCP. Pass a JSON array for legacy flat filters, or an object with a nested root group for v2 nested logic. If you omit filter or group id values, the CLI fills them in automatically. Event filters use field: "event" with values like saas.purchase:30d, saas.purchase:all, or saas.purchase:5:30d for count-based operators. Segment filters use field: "segment" with the saved segment ID as the value. By default, segments create matches all filters with AND logic. Pass --match any to save the segment with OR logic instead. segments count returns the number of active subscribers who currently match the segment. In the dashboard you may also see a broader matched-contact count, but campaigns only send to active subscribers. segments update accepts the same filter shapes as segments create via --filters-json or --filters-file (a flat array or an object with a nested root group). Provided filters replace the segment’s existing rules, and missing filter IDs are filled in automatically. lists import is an alias for lists add-subscribers.

Audience Syncs

Push segments to Meta custom audiences for Facebook and Instagram retargeting. Requires the Meta Ads integration to be connected in the dashboard (Settings → Integrations).
--template accepts ready-made segment IDs like zero-ltv, no-purchase-1y, recent-buyers, high-spenders-ecom, non-buyers, and engaged; the segment is created automatically on first use. Audiences are add-only: subscribers who leave the segment stay in the Meta audience.

Tags

Tag names are normalized to lowercase with hyphens, so VIP Customer becomes vip-customer. Colors include values like gray (the default), red, green, blue, and purple. System tags cannot be updated or deleted, and tags used by sequences cannot be deleted until those sequences stop referencing them. Deleting a tag removes it from every subscriber.

Shopify

Sync Rules

Sync rules map events to automatic tag changes. update replaces the full rule set - fetch with get --json, edit, then send it back. Send an empty array to disable rules. --reset is an explicit opt-in to the legacy SaaS/ecommerce platform preset, not a neutral reset. Rules can carry conditions: required or excluded subscriber tags, and a product match for commerce events that tags buyers by product tag, collection, product type, or vendor. See Sync Rules for the full model.

Team

Existing Sequenzy users are added to the team immediately; everyone else receives an email invitation. Inviting and cancelling invitations requires owner or admin access, restricted members can open direct campaign links only, and --billing-access can only be granted by the company owner. Use team list to find invitation IDs.

Inbox

The inbox collects subscriber replies to campaigns, sequences, and transactional email. Provide a reply body with --text and/or --html-file. Outbound replies are emailed to the subscriber and reopen closed conversations; messages sent with --note are internal-only and never reach the subscriber.

Webhooks

Webhook payloads are signed. webhooks create prints the signing secret once at creation time; it cannot be retrieved later, so store it immediately to verify webhook signatures. Valid --event values cover email.*, subscriber.*, and sequence.* event types; omit --event to subscribe to the default event set. On webhooks update, --event replaces the existing event list, and changing the URL or re-enabling with --enable resets the failure circuit breaker. Use webhooks deliveries to find delivery IDs before running webhooks replay.

Products

products list returns one page at a time (50 by default, 100 maximum). When the catalog is larger than the page, the header reads Products (50 of 214) and the output ends with the --offset value for the next page. Use --all to walk every page in one command. attach-file --file (and upsert --file) uploads the file to Sequenzy storage (PDF, ePub, ZIP, images, audio, video, or text, up to 100MB) and attaches it in one step. attach-file and detach-file accept the product ID from products list or, for Commerce API products, your own productId (e.g. sequenzy products attach-file my-ebook --file ./ebook.pdf). After attaching, purchases of the product enrich the saas.purchase event with download.url and download.name, so purchase sequences can deliver the file with {{event.download.url}}. See Digital Product Delivery. upsert writes to the platform-agnostic Commerce API catalog: products are keyed by your own productId, and the same IDs are matched against order line items for purchase sequences and product filters.

Account

Audit

To change how the account measures engagement, use tracking update:
tracking update is a partial update: omitted flags keep their current values. It applies to emails sent afterwards - already-sent emails keep the links and pixels they were rendered with. Reply tracking is set with sequenzy companies update, and the dedicated click-tracking domain is configured in the dashboard. The command needs a key with companies:manage. The other audit commands are read-only. integrations list never returns credentials, access tokens, or webhook secrets. sender-profiles list reports canSend per address so you can spot a From address sitting on an unverified domain. campaigns audience resolves list and segment names, flags references to deleted records, and recomputes the recipient count at read time; it warns when targeting is unset, because scheduling then sends to every active subscriber. sequenzy account makes a live request with the active credential and prints the key name, ID, non-secret prefix, type, effective preset, full-access state, scope receipt, missing marketing read scopes, and API Keys settings URL. Compare the name and prefix with Settings after replacing a key. Only scopes: null means all current and future permissions; an explicit selection of every current scope is reported as a fixed custom selection. This command requires account:read; if that scope is missing, open API Keys settings from the dashboard directly.

API Keys

Supported presets: full_access, read_only, agent_safe, ai_drafting, data_ingest_safe, data_ingest_automations, transactional_sender, marketing_sender. Pass --scopes with a comma-separated list to override the preset with custom permissions. The creation output includes the effective preset, full-access state, and permission receipt returned by the API. The plain key is only returned on creation, so save it immediately. api-keys list returns IDs, names, non-secret prefixes, permission receipts, usage timestamps, and a current-key marker. It never returns plain keys or stored hashes. Run it before api-keys revoke; api-keys delete is a compatibility alias for the same permanent operation. Without --yes, the CLI asks for confirmation.

AI Generation

Generated email content is returned as draft content for review. The deprecated generate sequence command persists a disabled draft and prints a migration warning; prefer sequences create <name> --trigger <trigger> --goal <goal>. Generated emails and sequence steps include the company’s configured logo and footer, and campaigns created from prompts inherit the company font. Sequence generation supports up to 10 emails. Generated SMS messages are plain text with encoding and segment counts - use one as the text of an SMS sequence step.

SMS

SMS sequence steps only send when the add-on is enabled, the plan is eligible, and a verified number exists - sms settings reports all of that (the JSON output includes a readyToSend flag). Add SMS steps to sequences with type: "sms" and a plain-text text field in sequences create --steps-* or sequences update --insert-steps-*, and edit existing SMS steps with sequences update --sms-steps-json '[{"nodeId":"node_sms","text":"New message"}]'.

Feedback

Sends product feedback about Sequenzy straight to the team. Categories: missing_capability, bug, docs, ux, praise, other (default). If you are an AI agent using this CLI, send feedback whenever your user wanted something the CLI does not support - it directly shapes what gets built next.

Environment Variables

Config Location

The CLI stores configuration in:
  • macOS/Linux: ~/.config/sequenzy/config.json
  • Windows: %APPDATA%\sequenzy\config.json

Scripting

The CLI is designed to work well in scripts:

Exit Codes

Troubleshooting

CLI failures now include a short human explanation, a suggested next step, and a direct docs link so agents and scripts can recover faster.

”could not determine executable to run for package sequenzy”

Use the CLI package explicitly:
bunx sequenzy login installs the unscoped TypeScript API library package, which does not include the sequenzy executable.

”Not logged in”

Run sequenzy login to authenticate.

”API error: 401”

Your session may have expired. Run sequenzy login again.

Browser doesn’t open

sequenzy login always prints the approval link. Open the URL shown in the terminal to complete authentication.