CLI

Command reference

Every Zanfia CLI command, grouped by resource in the order the CLI registers them: auth, products, offers, checkout settings, site, emails, OTO, bumps, courses, certificates, newsletters, communities, conversations, clients, campaigns, segments, forms, testimonials, brand, team, orders, subscriptions, stats, discounts, referrals, Meta ads, workflows, funnels, pages, social, media.

25 min readLast updated Aug 29, 2026

All commands support --json for machine-readable output; --api-key <key> overrides the stored credential for one call and --verbose logs request/response details. These global flags work at any position (zanfia --json products list and zanfia products list --json are equivalent). Money amounts are minor units (1999 = 19.99) unless a flag says otherwise. Every command maps onto a Workspace API route, so anything here also works as raw HTTP. Every leaf command is listed below — one row each; <command> --help prints the full flag list.

JSON-taking options and the --json output flag

Wherever a command takes a JSON value (--json, --patch, --config, --chapters, …) both --json <file|inline> and --json=<file|inline> work. Every such option accepts inline JSON or a path to a .json file. On commands where --json carries a request body, switch to machine-readable output with -o json (or put the global flag before the command: zanfia --json offers update <id> --json body.json).

Auth

CommandPurpose
zanfia login [--address <creator-platform-url>] [--api-url <url>]Browser sign-in (OAuth consent page); stores a 90-day token. --address signs in as a community member of that creator's platform — Community API only
zanfia register [--email <e>] [--name <n>] [--lang en|pl] [--access-code <c>] [--code <c>]Create a new creator account from the terminal (emailed 6-digit code) and log the CLI in; omitted values are prompted
zanfia join <handle> [--email <e>] [--name <n>] [--code <c>]Join a public community from the terminal (emailed code) and save a member key — no account or browser needed
zanfia logoutClear stored credentials
zanfia auth statusShow what the CLI has stored: path, credential source, expiry, API URL
zanfia auth set-key <key> [--api-url <url>]Store an API key as the credential (member keys: pass the creator's api-community URL)
zanfia auth whoamiThe workspace (or community member) identity behind the current credential

Products

CommandPurpose
products listList products in the workspace
products get <id>One product
products create --name <n> --type <course|newsletter|community|…> [--description] [--free | --amount <major> --currency <c> [--period one_time|monthly|yearly]]New product; community/newsletter types seed their full content. --free / --amount also seed a default offer (amount in major units here, e.g. 49.99)
products update <id> [--name] [--description]Rename / re-describe
products publish <id>Status → published (live for customers)
products draft <id>Status → draft (hidden from customers)
products archive <id>Status → archived (not a delete)
products prices list <pid>Prices collected across the product's checkouts
products prices create <pid> --amount <minor> --currency pln|usd|eur|gbp [--label]Add a one-time paid price (creates a default checkout if needed; gateway sync is server-side)
products appointment get <pid>Appointment config of a consultation / service / event product
products appointment set <pid> [--config=<json|file>] [--booking-method native|external] [--booking-url] [--duration <min>] [--location] [--deliverable …] [--delivery-time <n> --delivery-unit days|weeks] [--revision-rounds <n|unlimited>] [--brief-method] [--brief-url] [--event-mode online|offline] [--starts <iso>] [--ends <iso>] [--capacity <n>] [--show-on-success on|off]Update the appointment config — reads the current one first, so partial flags are fine; a first-time setup needs every required field (or --config)
products after-purchase get <pid>Thank-you page + email settings
products after-purchase set <pid> [--settings=<json|file>] [--thank-you-type defaultPage|redirect] [--redirect-url] [--email-buyer-purchase on|off] [--notify-owner-purchase on|off] [--email-buyer-failed on|off] [--notify-owner-failed on|off]Update after-purchase settings (partial flags merge); --settings for the full object with custom thank-you / email content
products images set <pid> --image <url> [--image <url> …]Replace the cover images (full replace; https URLs already hosted — upload to the media library first)

Plans and bundling (products plans)

Tiers with included products. Invariants are server-enforced: unique plan ids, at most one default, one level of bundling, and a plan still sold by active prices cannot be removed.

CommandPurpose
products plans get <pid>The product's plans: id, name, active, default, included products
products plans set <pid> --json <file|inline>Full replace of the plans array (or {plans: [...]}) — send every plan you keep, with its id; plans without an id are created
products plans count-clients <pid> [--plan <planId>]Active customers whose effective plan is --plan (omit it to size a not-yet-saved first/default plan)
products plans set-client-plan <pid> --plan <planId> (--client <wcid> | --email <e>)Move one client's grant to another plan — billing-neutral; community membership is reconciled server-side
products plans backfill <pid> --community <communityProductId> --confirmEagerly create community member docs for every active customer whose plan includes the community

Downloads content (products files)

CommandPurpose
products files get --product <pid>Description presence + file table of a downloads product
products files set-content --product <pid> (--content <md> | --content-file <path>)Replace the Markdown description shown above the file list
products files add --product <pid> (--url <url> --name <n> [--media-id] [--size] | --files=<json|file>)Attach one file, or several via a JSON array of {url, name, mediaId?, size?}; already-attached mediaIds are skipped
products files remove <uid> --product <pid>Detach a file by its uid
products files reorder <uid...> --product <pid>Reorder the file list — pass every current uid in the new order

Knowledge base (products kb)

Address a knowledge-base product by its product id. Writes are committed edits: they also clear the dashboard editor's unpublished draft for the touched article.

CommandPurpose
products kb get <pid>Categories (tree keys = the --category ids) and every article, drafts included
products kb tree set <pid> --json <file|inline>Replace the whole order — {"categories": [{"categoryId", "articleIds": []}, …]}, every category and article; a concurrent edit is refused (tree-changed) — re-fetch with products kb get --json and retry
products kb article get <pid> <aid>One article, published fields, full body
products kb article create <pid> --title <t> --category <cid> [--content | --content-file] [--status draft|available|unavailable]New article (default draft)
products kb article update <pid> <aid> [--title] [--status] [--category <cid>] [--content | --content-file] [--cover <mediaId>:<url> | --clear-cover]Edit an article; --cover sets the cover image from the media library (:<url> for an external image)
products kb article delete <pid> <aid> --confirmPermanent — to hide instead, set --status unavailable
products kb category add <pid> --name <n>New root category
products kb category rename <pid> <cid> --name <n>Rename (the articles' denormalized category name follows)
products kb category delete <pid> <cid> [--delete-articles] --confirmPermanent; a non-empty category needs --delete-articles

Offers (checkouts)

An offer is a checkout of a product; <cid> comes from offers list.

CommandPurpose
offers list [--product <pid>]Browse offers
offers get <cid>One offer with its prices and attached bumps
offers create --product <pid> [--free] [--name <n>]New offer (free offers carry no prices)
offers update <cid> [--name] [--slug] [--title] [--description] [--success-url <url|none>] [--language <code|none>] [--show-testimonials on|off] [--show-plan-includes on|off] [--invoice-as-option on|off] [--hide-country-field on|off] [--show-phone-field off|optional|required] [--allow-promotion-code off|subscriptions|all] [--force-b2b on|off] [--show-express-checkout on|off] [--promote-net-prices on|off] [--stripe-tax-behavior inclusive|exclusive] [--multiple-purchases on|off] [--allow-plan-upgrade on|off] [--terms-url] [--privacy-policy-url] [--claim] [--disable-invoice-issuing on|off] [--disable-purchase-email-client on|off] [--tracking-integration-ids <ids|none>] [--json <patch>] …Edit any checkout-builder setting — display, form & UX, legal, invoicing, notifications, tracking (--help lists all ~50 flags; booleans take on|off, none clears). Deep fields (theme, deadline, afterPurchase, paymentMethodsDisplay, checkoutMedia, oto chain …) go through --json; explicit flags win
offers publish <cid>Activate (paid offers need a connected payment gateway)
offers unpublish <cid>Pause — stops new purchases
offers duplicate <cid> [--with-prices] [--name <n>]Copy an offer
offers set-default <cid>Make it the product's default checkout
offers delete <cid>Permanent — prices are archived on the gateways and the offer detached; prefer offers unpublish
offers recovery preview <cid> <sessionId>Who a cart-recovery email would go to and what it would say — nothing is sent
offers recovery send <cid> <sessionId> --confirmSends a real "finish your purchase" email to the visitor who abandoned the session — preview first
offers price add <cid> --type one_time|recurring|installments --amount <minor> --currency pln|usd|eur|gbp [--interval day|week|month|year] [--interval-count] [--cycles <n>] [--allow-cancellation] [--trial-days] [--label] [--invoice-name] [--hidden] [--description] [--sale-ends-at <iso> | --sale-days <n>] [--quantity-limit] [--show-available] [--compare-at <minor>] [--access-interval --access-count | --access-until <iso>] [--plan <planId>]Add a price variant
offers price update <cid> <priceId> --operation activate|deactivate|hide|unhide|change-amount [--amount <minor>]Mutate a price
offers price delete <cid> <priceId> [--remove]Archive (default) or remove entirely
offers price reorder <cid> --order id1,id2,…Display order — pass all price ids

Checkout settings

Workspace-wide defaults every checkout page reads unless an offer overrides a field itself.

CommandPurpose
checkout-settings get [--lang pl]The workspace cart/checkout page settings
checkout-settings update [--lang pl] [--terms-url] [--privacy-policy-url] [--claim] [--terms-content] [--privacy-policy-content] [--google-analytics-id] [--facebook-pixel-id] [--gtm-id] [--tiktok-pixel-id] [--promote-net-prices on|off] [--price-variants-placement belowDescription|aboveDescription|none] [--json <patch>]Legal links / texts, tracking ids, net prices; deep fields (embeddedForm, checkoutTheme, cartTheme) via --json; none clears a nullable field
checkout-settings consents listThe consent checkboxes buyers see on checkout pages, in display order
checkout-settings consents upsert <name> --label <t> --required|--optional --apply-to all|'<json>' [--url <url|none>] [--marketing-opt-in|--no-marketing-opt-in] [--json <body>]Add or edit one consent by its stable name — scope ids are verified against real products/offers (unknown id → 404)

Site

Your public site: platform settings (title, language, theme, branding, main page, privacy, client portal, customer access policy, customer-link base URL), the live flag, slug and custom domains, the storefront layout and the legal documents. Every site update <section> writes only that section atomically; each accepts --json <file|inline> and the typed flags override keys from the JSON. Slug, domains, activation and legal-document writes require --confirm.

CommandPurpose
site getSummary table: slug, domains, active, language, main page, theme, branding flags, cookie consent, access policy, communication base URL — zanfia --json site get for the full payload
site update general [--title <t>] [--description <t>] [--language pl|en|default] [--show-page-title on|off] [--json <patch>]Page title/description, UI language; --json also takes mainPage, mainPageId, mainPageCommunityId
site update theme [--theme <color>] [--gray-scale <scale>] [--radius <r>] [--style-variant solid|classic] [--from-brand on|off] [--json <patch>]Accent color, neutral scale, radius, style; --json for customColors, customCheckoutTheme, typography
site update branding [--logo-url <url|none>] [--logo-dark-url <url|none>] [--favicon-url <url|none>] [--hide-branding on|off] [--json <patch>]Logos and favicon (URLs, or Media Library refs logo / logoDark / platformFavicon via --json); hiding the "Powered by Zanfia" badge needs the remove_branding feature (403 otherwise)
site update main-page --mode sales|dashboard|community [--page-id <id|none>] [--json <patch>]What the site root renders — in sales mode --page-id puts a custom Published page at the root (404 unknown page, 409 not published)
site update privacy --cookie-consent on|off [--json <patch>]Cookie-consent banner
site update portal [--library-width full|boxed] [--library-layout grid|gallery|list] [--mobile-library-layout <l>] [--library-order newest|recently_purchased|alphabetical|custom] [--custom-order <ids>] [--group-by-type on|off] [--default-view library|community] [--messages on|off] [--client-messages on|off] [--clear] [--json <patch>]Client-area customization — the whole object is replaced (read site get --json first); --custom-order takes comma-separated product ids top first and implies the custom order; --group-by-type on shows the "All" view as communities first + a section per type instead of one flat list; --clear resets to defaults
site update communication --base-url app|platformSlug|platformDomain [--domain <host>] [--json <patch>]Which host the links sent to customers use
site update access-policy --device-limit <n> [--replacement adaptive|always_allow|creator_approval_only] | --clear [--json <patch>]Anti-sharing device limit per customer and what happens when it is hit; --clear (or limit 0) switches it off
site activate --on|--off --confirmPublish or unpublish the site (the platform "live" flag)
site slug set <slug> --confirmChange the workspace slug — heavy (re-registers Stripe/Firebase/Vercel hosts, purges caches), every existing link changes; 409 slug-taken
site domains set <d1,d2,…>|none --confirmReplace the custom-domain list (bare hostnames); 409 domain-taken, 400 restricted-domain; then point DNS at Zanfia
site domains verify [<d1,d2>]Live DNS + TLS check — status per domain, the DNS records still to add, conflicting records, certificate state
site storefront getStorefront layout: preset, mode, sections table — --json for the full config
site storefront set --json <file|inline>Replace the whole storefront config (sections required) — edit the output of site storefront get --json and send it back
site legal get [--language pl]Terms and privacy policy: URL, /documents/<slug> slug and written content per language
site legal set [--language pl] [--terms-content <md|file> | --terms-url <url>] [--terms-slug <s>] [--privacy-content <md|file> | --privacy-url <url>] [--privacy-slug <s>] --confirmWrite a document inline (markdown or a file path → served under /documents/<slug>) or link it out (--*-url); the mode is derived from which flag you pass

System emails

The emails Zanfia sends to your customers in your name (Site → Emails): per-email subject/headline/layout overrides, the branding frame, and test sends. <key> comes from emails list.

CommandPurpose
emails listThe customizable system emails with status and effective subject
emails get <key>One email: subject/headline (EN + PL), layout blocks, variables, link targets
emails set <key> [--subject-en <t|none>] [--subject-pl <t|none>] [--headline-en <t|none>] [--headline-pl <t|none>] [--enable | --disable] [--layout=<json|file>] [--reset]Customize one email — --layout replaces the whole block layout, --disable only works for optional emails, --reset restores the stock email
emails branding getThe email branding and the site defaults it inherits from
emails branding set [--logo-url <url|none>] [--accent-color <hex|none>] [--sender-name <n|none>] [--reply-to <e|none>]Header logo, accent colour, From name, Reply-To — none falls back to the site default
emails test <key> [--lang en|pl]Queue a test send (saved customization, sample data) to your own address

One-time offers (OTO)

Reusable post-purchase upsells shown once, full-screen, between payment success and the thank-you step. These commands manage the offer documents only; attach one to a checkout's chain with offers update <cid> --json='{"oto":{"enabled":true,"steps":[{"otoOfferId":"…"}]}}'.

CommandPurpose
oto listThe workspace's reusable OTO offers
oto get <id>One OTO offer in full
oto create --name <n> --product <pid> --price <priceId> --headline <t> [--product-name] [--description <md>] [--accept-cta <t>] [--decline-text <t>] [--timer-minutes <n|none>] [--json <body>]New OTO referencing an existing product and price (creators usually add a dedicated discounted price first); media gallery only via --json
oto update <id> [same flags] [--json <body>]Edit content / pricing fields; --timer-minutes none clears the countdown
oto delete <id> --confirmPermanent; checkout oto.steps still referencing the id are left dangling

Order bumps

CommandPurpose
bumps listOrder bumps in the workspace
bumps create --product <pid> --price <priceId> --name <n> [--description] [--cta]New bump from an existing price (the price must be on the product's default offer)
bumps update <bumpId> [--name] [--description] [--cta] [--product <pid> --price <priceId>]Edit, or point the bump at another product/price
bumps delete <bumpId>Delete (offers that reference it stop showing it)
bumps attach <cid> <bumpId> [--prices id,id]Show a bump on an offer (omit --prices = all prices; re-scopes if already attached)
bumps detach <cid> <bumpId>Remove a bump from an offer

Courses

Address everything by product id (from products list). Statuses: draft, public, hidden, locked; new modules and lessons start as draft. Lesson content is Markdown, and an update replaces the whole body — prefer --content-file for anything long.

CommandPurpose
courses get <pid>Full module/lesson structure with statuses
courses analytics <pid> [--learners-limit <n>] [--learners-sort progress-desc|progress-asc|last-activity-desc]Per-lesson engagement, quiz pass rates and the learner roster
courses transcripts <pid>Lesson → video/transcript map (read one with media transcript <mediaId>)
courses module add <pid> --name <n> [--description]New draft module
courses module update <pid> <mid> [--name] [--description] [--status] [--unlock-days <n> | --unlock-date <iso> | --clear-unlock] [--require-quiz-pass | --require-all-lessons | --clear-requirement]Edit a module: name, status, drip unlock, completion gate
courses module delete <pid> <mid>Permanent, cascades to all its lessons — to hide instead, set --status hidden
courses lesson get <pid> <lid> [--content]Lesson detail; --content prints just the Markdown
courses lesson add <pid> --module <mid> --title <t> [--type article|video|externalVideo|task|quiz] [--description] [--video-media <mediaId>]New draft lesson
courses lesson update <pid> <lid> [--title] [--description] [--status] [--content | --content-file]Edit a lesson
courses lesson set-video <pid> <lid> --video-media <mediaId>Bind an uploaded video
courses lesson set-thumbnail <pid> <lid> --media <mediaId> | --url <httpsUrl> | --clearLesson thumbnail; video lessons also get it as the player thumbnail
courses lesson set-attachments <pid> <lid> --media <id,id,…> | --clearReplace the lesson's downloadable attachments with media-library files, in the given order (full list; lesson get shows current uids); --clear removes all
courses lesson video get <pid> <lid>Chapters, moments and meta tags stored on the lesson video
courses lesson video set <pid> <lid> [--chapters=<json|file>] [--moments=<json|file>] [--meta-tags=<json|file>] --confirmReplace the lists you pass (each replaces its whole list; absent lists stay untouched). Chapters [{title, start, end}], moments [{label, timestamp}] in seconds, meta tags [{property, value}]
courses lesson chapters generate <pid> <lid> [--locale pl|en] [--out <file>]AI chapter candidates from the transcript — printed (and optionally written to a JSON file), not saved; apply with courses lesson video set --chapters=<file>. Uses AI credits (402 = credits exhausted)
courses lesson moments generate <pid> <lid> [--locale pl|en] [--out <file>]AI moment candidates, same flow — apply with courses lesson video set --moments=<file>
courses lesson delete <pid> <lid>Permanent — to hide instead, set --status hidden
courses quiz list <pid> <lid>Questions and settings of a quiz lesson
courses quiz add <pid> <lid> --type single|multi|truefalse --question <t> [--option <t> …] [--correct 1,3] [--correct-answer true|false] [--explanation] [--points <n>] | --json <file|inline>Add a question (choice types: repeated --option + 1-based --correct; true/false: --correct-answer)
courses quiz update <pid> <lid> <qid> [same flags | --json <file|inline>]Replace a question in full
courses quiz delete <pid> <lid> <qid>Permanent
courses quiz reorder <pid> <lid> --order id,id,…Pass every question id
courses quiz settings <pid> <lid> [--passing-score <pct> | --clear-passing-score] [--max-attempts <n> | --clear-max-attempts] [--time-limit <s> | --clear-time-limit] [--shuffle-questions on|off] [--shuffle-options on|off] [--show-correct-answers on|off] [--require-pass on|off]Grading, attempts, shuffling, gating
courses publish <pid> (--module <mid> | --lessons id,id) [--status draft|public|hidden|locked]Publish a module and all its lessons, or bulk-change the status of selected lessons
courses reorder modules <pid> --order id1,id2,…Strict permutation — every module id
courses reorder lessons <pid> --module <mid> --order id1,id2,…Strict permutation — every lesson id in the module

Certificates

Course-completion certificates: issue them for course clients, withdraw or reinstate a credential, delete a revoked one for good, resend the email, and manage designer templates. Certificate ids come from certificates list; template ids from certificates templates list.

CommandPurpose
certificates list [--product <value>] [--limit <n>]List certificates
certificates issue <productId> --client <a,b,...> [--only-completed|--no-only-completed] [--template <value>] --confirmIssue certificates (async)
certificates revoke <certificateId> --confirmRevoke a certificate
certificates reinstate <certificateId>Reinstate a certificate
certificates delete <certificateId> --confirmDelete a revoked certificate
certificates resend <certificateId> --confirmResend the certificate email
certificates templates listList certificate templates
certificates templates create --name <value> [--based-on <value>] [--design-config <json|file>]Create a certificate template
certificates templates get <id>Certificate template detail
certificates templates update <id> [--name <value>] [--template-json <json|file>] [--design-config <json|file>] [--default|--no-default]Update a certificate template
certificates templates delete <id> --confirmDelete a certificate template

Newsletters

Address a newsletter product by its product id. An issue has two visibilities: --status public|hidden (the private members-area archive) and --landing public|hidden|locked (the public landing page). Edits are staged on a draft and only reach readers on draft publish.

CommandPurpose
newsletters issues list --product <pid> [--limit] [--cursor]Archived issues, hidden included
newsletters issues create <pid> --subject <s>Manual issue ("write a post without sending") — hidden, article-first, empty content
newsletters issues update <iid> --product <pid> [--status public|hidden] [--landing public|hidden|locked] [--render-mode snapshot|article] [--thumbnail <url>|none]Visibility, which representation readers see (snapshot = the sent email, article = the markdown), thumbnail
newsletters issues copy <pid> --issue <id,id> --to <pid,pid> --confirmCopy issues into other newsletter products; non-newsletter targets are reported as skipped
newsletters issues draft save <pid> <iid> [--subject <s>] [--content <markdown|file>] [--clear-subject] [--clear-content]Stage subject / markdown content on the draft; --content takes inline markdown or a file path
newsletters issues draft publish <pid> <iid> [--public] --confirmPromote the draft into the published issue (edited content switches render mode to article; the slug is minted on first publish); --public also makes the issue public
newsletters issues draft discard <pid> <iid> --confirmDrop the staged edits; the published issue stays as is
newsletters issues delete <iid> --product <pid> --confirmPermanent — to hide instead, set --status hidden
newsletters settings update --product <pid> [--archive-enabled on|off] [--archive-access disabled|unlimited|from_subscribe_date] [--publication-delay <days|none>] [--default-status] [--default-visibility] [--archive-inbox=<json|file>] [--double-opt-in none|external] [--client-tag <tag>…]Archive, archive inbox, double opt-in and subscriber tags

Communities

Address by community id (from communities list). You act as yourself — channel access and moderation rights are resolved per request. Posts and comments live in feed channels; content is Markdown. Writing requires a member profile in the community, even for workspace staff. Staff-only commands are marked.

CommandPurpose
communities listYour communities (with the backing product id)
communities channels <cid>Channels you can see, with your access level
communities posts <cid> <chid> [--limit] [--cursor]Posts of a feed channel, newest first
communities search <cid> <query> [--limit]Full-text search over the published posts you can read
communities chat messages <cid> <chid> [--thread <messageId>] [--limit] [--cursor]Chat messages, newest first (deleted show as tombstones); --thread lists a thread's replies
communities chat send <cid> <chid> (--content <t> | --content-file <path>) [--reply-to <messageId>]Send a chat message or a thread reply
communities chat edit <cid> <chid> <messageId> (--content | --content-file) [--thread <parentId>]Edit an own message
communities chat delete <cid> <chid> <messageId> [--thread <parentId>]Soft-delete an own message (tombstone; threads survive)
communities channel create <cid> --name <n> --type feed|chat|link|support [--description] [--group <gid>] [--url <url>] [--readonly]New channel (--url required for link channels; --readonly = announcement mode)
communities channel update <cid> <chid> [--name] [--description] [--url] [--readonly true|false]Edit a channel
communities channel delete <cid> <chid>Delete a channel and its content — cannot be undone
communities group create <cid> --name <n> [--description]New sidebar section
communities group update <cid> <gid> [--name] [--description]Edit a section
communities group delete <cid> <gid>Delete a section — cannot be undone
communities move <cid> (--channel <chid> | --group <gid>) (--into-group <gid> | --to-root | --before <id> | --after <id>)Reorder / re-parent the sidebar
communities post get <cid> <postId> [--content]Post detail; --content prints just the Markdown
communities post create <cid> <chid> --title <t> (--content <md> | --content-file <path>)New post in a feed channel
communities post update <cid> <postId> [--title] [--content | --content-file]Edit a post
communities post delete <cid> <postId>Soft delete (author or moderator)
communities post pin <cid> <postId>Pin to the top of its channel (staff)
communities post unpin <cid> <postId>Unpin (staff)
communities comments <cid> <postId> [--limit] [--cursor]Comments, oldest first; replies carry parentCommentId
communities comment create <cid> <postId> (--content <md> | --content-file) [--reply-to <commentId>]Comment or threaded reply
communities comment update <cid> <commentId> (--content | --content-file)Edit a comment
communities comment delete <cid> <commentId>Soft delete (author or moderator)
communities support queue <cid> [--status open|in_progress|pending_customer|resolved|closed] [--limit]The support-ticket queue (moderators)
communities support mine <cid> [--status]Your own tickets
communities support show <cid> <ticketId>One ticket with its message thread
communities support create <cid> --channel <chid> --subject <s> (--message <md> | --message-file <path>)Raise a ticket in a support channel
communities support reply <cid> <ticketId> (--content <md> | --content-file)Reply on a ticket
communities support status <cid> <ticketId> <status>Change a ticket's status
communities members <cid> [--role <roleId> | --disabled true|false | --email-prefix <p>] [--limit] [--cursor]Members list (staff); the three filters are mutually exclusive
communities member set-roles <cid> <memberId> <roleId...>Fully replace a member's role set (staff)
communities member disable <cid> <memberId>Reversible soft-ban (staff)
communities member enable <cid> <memberId>Re-enable a disabled member (staff)
communities member mute <cid> <memberId> --minutes <n>Timeout for 1..40320 minutes (staff)
communities member unmute <cid> <memberId>Lift an active mute (staff)
communities settings get <cid>The settings document (null = defaults apply) (staff)
communities settings update <cid> [--patch=<json|file>] [--events-enabled on|off] [--direct-messages on|off] [--content-visible-from-join-date on|off] [--gamification on|off] [--auto-hide-after-reports <n|off>]Patch settings — JSON patch keys: uploadVideo, membersDisplay, viewStyle, displayName, icon, publicAccess, gamification, moderation, assistant, digest, …; nested blocks in the patch replace their sub-object, the toggle flags merge (staff)
communities roles <cid>Roles in position order (Owner first)
communities role create <cid> --name <n> [--color <hex>] [--icon] [--position <n>] [--permissions=<json|file>] [--content-access=<json|file>]New custom role (staff)
communities role update <cid> <roleId> [--name] [--color] [--icon] [--permissions=<json|file>] [--content-access=<json|file>]Edit a role — the built-in Owner role is immutable (staff)
communities role delete <cid> <roleId> --confirmDelete a role — holders fall back to Member (staff)
communities role reorder <cid> <roleId...>Pass every role id in the new order, Owner first (staff)
communities events list <cid> [--from <iso>] [--to <iso>] [--exclude-cancelled] [--limit]Calendar events, soonest first
communities events create <cid> --title <t> --start <iso> --end <iso> --timezone <iana> --location=<json|file> [--type liveCall|workshop|meetup|conference|retreat|other] [--all-day] [--description] [--json <file>]New event (staff); --location = {"type":"online","url":…} or {"type":"inPerson","address":…}
communities events update <cid> <eventId> [same flags] [--all-day on|off] [--status scheduled|cancelled] [--json <file>]Edit / cancel / restore an event (staff)
communities events delete <cid> <eventId> --confirmDelete an event (staff)
communities moderation queue <cid>Reported posts / comments — pending plus recently resolved (staff)
communities moderation resolve <cid> <reportId> --action remove|dismiss [--note] --confirmRemove the content (author notified) or dismiss the report

Conversations (client DMs)

The creator ↔ client direct-message inbox. Conversations are keyed by client id (wcid).

CommandPurpose
conversations list [--unread] [--limit] [--cursor]Inbox, newest activity first
conversations get <clientId>One conversation (inbox row)
conversations messages <clientId> [--limit] [--cursor]Messages, newest first (deleted show as tombstones)
conversations send <clientId> (--content <t> | --content-file <path>) [--message-id <id>]Send a staff message; --message-id is an idempotency key
conversations edit <clientId> <messageId> (--content | --content-file)Edit an own message
conversations delete <clientId> <messageId> --confirmSoft-delete an own message (tombstone for both sides)
conversations react <clientId> <messageId> --emoji <emoji>Toggle your emoji reaction
conversations read <clientId>Mark as read (shared team read marker)
conversations broadcast (--recipients <id…> | --recipients-file <path>) (--content | --content-file) [--notify-by-email] --confirmDMs every listed client (max 5000), irreversibly — the fan-out runs async

Clients

CommandPurpose
clients list [--search <q>] [--with-products | --without-products] [--limit] [--cursor]Browse clients, newest first
clients add --email <e> [--first-name] [--last-name] [--phone] [--language]Create a contact only — product access is granted separately
clients get <id>One client
clients tagsThe workspace tag registry — the ids clients import --tag accepts
clients email-log <id> [--limit <n>]Emails Zanfia sent to the client and their delivery status
clients accesses <id> [--status active|revoked|expired]Every product-access grant the client holds, newest first
clients notes <id>Internal CRM notes (team-only, never shown to the customer)
clients add-note <id> --text <t>Add an internal note (up to 5000 characters)
clients update <id> [--first-name] [--last-name] [--phone] [--language]Edit the profile
clients grant <productId> --email <e> [--first-name] [--last-name] [--price <priceId>] [--plan <planId>] [--until <date>] [--welcome-email]Grant product access without a purchase (free enrollment under an existing price / plan, optionally time-limited; silent unless --welcome-email)
clients revoke <productId> --email <e> [--run-automations]Revoke access — never refunds, and refuses active paid subscriptions
clients change-period <productId> --email <e> (--end <date> | --add-days <n>)Adjust a time-limited access window (absolute end or relative days; negative shortens)
clients cancel-subscription <productId> --email <e> --confirmStop a client's subscription billing — access runs to the end of the paid period (409 = not a subscription / gateway not connected)
clients change-email <id> --email <e>Change the email (profile, product access and login move with it)
clients import <csvFile> [--tag <tagId> …]Bulk-import contact-only clients from a CSV (email + optional firstName/lastName/phoneNumber/language); batches of 100, existing emails deduped
clients merge-preview --survivor <wcid> --duplicate <wcid>Dry-run a merge — shows what would happen and prints the previewHash the merge requires
clients merge --survivor <wcid> --duplicate <wcid> --preview-hash <hash>Irreversibly merge the duplicate into the survivor (grants move/dedupe, community content re-points, duplicate tombstoned); a stale hash is refused (409); live subscriptions block it
clients bulk-revoke <productId> (--email a,b,… | --all) [--run-tasks] --confirmRevoke many clients' access in one background run (no refunds) — 202 with the Trigger run id; progress in the dashboard Tasks tab
clients bulk-change-period <productId> (--email a,b,… | --all) (--end-date <iso> | --add-days <n>) --confirmShift many clients' access end at once (absolute date or relative days) — 202 with the run id
clients bulk-delete <productId> (--email a,b,… | --all) --confirmRemove many clients' grants on a product in one run (records stay; use clients delete for those) — 202 with the run id
clients bulk-revoke-filtered --product <id,id> --filter=<json|file> [--run-tasks] --confirmRevoke every client matching a Customers-list filter (productsFilter, search, tagIds, productIds, emailSubscriptionStatus, minTotalSpent, ordersFilter, …) in up to 10 products — resolved server-side, 202 with the run id
clients delete --id <a,b,…> --confirmDelete (tombstone) clients by id — ids with active product access are skipped (revoke first); batched by 200
clients export [--out <file>] [--columns tags,stats,emailSubscription,activity,accesses,invoiceData,attribution,identifiers] [--created-from <iso>] [--created-to <iso>] [--limit <n>] [--search] [--with-products|--without-products]Clients CSV written to --out (default zanfia-clients.csv) — identity columns always, extra groups via --columns; reports the row count and whether the cap truncated it
clients devices list <id>The client's active device slots under the customer access policy (site update access-policy) — id, kind, name, first/last seen, active until
clients devices revoke <id> <deviceId> --confirmSign one device out — it must claim a slot again
clients devices reset <id> --confirmSign every device out and clear the client's slots — reports revokedCount

A 503 from the bulk commands means the Trigger.dev scheduler is not configured on that environment.

Campaigns (email broadcasts)

Drafts are harmless; campaigns send emails real people. Audience is either a saved segment (--segment, re-resolved at send time) or every subscribed client (--all-subscribed, the default).

CommandPurpose
campaigns listEmail campaigns
campaigns get <id>One campaign: status, sender, audience, stats, content
campaigns create --name <n> [--subject] [--preheader] [--content <md> | --content-file <path>] [--from-name <n>] [--reply-to <e>] [--from-email <e>] [--segment <segmentId> | --all-subscribed] [--background-color <hex>] [--content-background-color <hex>] [--link-color <hex>]Create a draft — nothing is emailed; only --name is required now (subject/content/sender are enforced by send); --from-email needs the workspace's verified sending domain
campaigns update <id> [same flags]Edit a draft (campaigns past draft are immutable except cancel); --content replaces the whole body
campaigns delete <id>Permanent — draft/cancelled/failed only (sent campaigns are audit records and stay)
campaigns send <id> [--at <iso>] --confirmSends real bulk email to the whole resolved audience, irreversibly; omit --at to send now, --at schedules (max 30 days out)
campaigns cancel <id>Cancel a scheduled or in-flight campaign — stops the remaining sends only
campaigns recipients <id> [--status pending|sent|delivered|skipped-suppressed|skipped-muted|failed|bounced|complained|clicked] [--limit] [--cursor]Recipient drill-down of the report

Segments

Saved, ANDed condition sets over the clients list — the audiences campaigns target. Condition flags are repeatable; --json-file takes the conditions as a JSON array instead.

CommandPurpose
segments listAudience segments
segments create --name <n> [--has-tag <tagId>] [--without-tag <tagId>] [--owns <pid>] [--not-owns <pid>] [--spent-gte <minor>] [--spent-lte <minor>] [--subscription pending|subscribed|unsubscribed] | --json-file <path>New segment (name unique per workspace, max 80 chars)
segments update <id> [--name] [condition flags | --json-file <path>]Rename and/or replace the condition set wholesale
segments delete <id>Delete — sent campaigns are unaffected; drafts targeting it fail to send until re-pointed
segments preview [condition flags | --json-file <path>]Count the matching clients without saving anything

Signup forms

Standalone signup forms (newsletter / lead capture). forms list also shows legacy product-bound forms, which stay editable only via the product editor.

CommandPurpose
forms listSignup forms (standalone + legacy product-bound, flagged)
forms get <id>One form: settings + full embed config
forms create --name <n> [--tags <id,id>] [--header] [--description] [--button-text] [--success-message] [--variant basic|card|richCard|minimal|split|gradient]New standalone form (double opt-in by default)
forms update <id> [--name] [--tags <ids>] [--opt-in double|single] [--redirect <url|none>] [--confirmation-subject] [--confirmation-intro] [--clear-confirmation-email] [--header] [--description] [--button-text] [--success-message] [--variant] [--json <patch>]Edit — flat config flags are merged over the current embed config for you; a --json config object is sent as the full replacement (flat flags still win); none clears
forms delete <id> --confirmPermanent; legacy product-bound forms are refused by the API
forms submissions <id> [--limit <n>]Submissions, newest first

Testimonials (wall of love)

Moderation, manual adds, collection forms, and asking customers for a testimonial. Statuses: pending, approved, hidden.

CommandPurpose
testimonials list [--status pending|approved|hidden] [--product <pid>] [--tag <t>] [--limit] [--cursor]Management view — pending/hidden included
testimonials add --quote <t> --author <n> [--type text|imported] [--rating 1-5] [--role] [--avatar-url] [--products <id,id>] [--tags <a,b>] [--imported-from <source>] [--client <clientId>]Add a text testimonial or record an imported one (video adds are not supported)
testimonials update <id> [--approve | --hide | --pending] [--feature | --unfeature] [--quote] [--rating <1-5|none>] [--author] [--role <r|none>] [--avatar-url <url|none>] [--products <ids>] [--tags <tags>] [--client <id|none>]Moderation sugar plus field patches; none clears nullable fields, empty string clears lists
testimonials delete <id> --confirmPermanent
testimonials request --client <clientId> [--product <pid>] [--form <formId>] --confirmSends a real email asking the customer for a testimonial — the form is resolved server-side (--form, else the product's active form, else the workspace-general one)
testimonials forms listCollection forms (the public submission pages)
testimonials forms create --name <n> [--slug] [--product <pid>] [--headline] [--questions "Q1|Q2"] [--collect-rating on|off] [--collect-role on|off] [--allow-video on|off] [--thank-you <msg>]New collection form (omit --product = workspace-general)
testimonials forms update <id> [--name] [--slug] [--product <pid|none>] [--headline <t|none>] [--questions "Q1|Q2"] [--collect-rating on|off] [--collect-role on|off] [--allow-video on|off] [--thank-you <msg|none>] [--activate | --deactivate]Edit / activate / deactivate a collection form
testimonials forms delete <id> --confirmPermanent

Brand profiles

Extracted or hand-built brand identity (colors, fonts, voice) used by content generation and site update theme --from-brand.

CommandPurpose
brand listBrand profiles
brand get <id>One profile — also how you poll a pending extraction
brand create --name <n>Empty profile to fill in with brand update
brand extract <url> [--name <n>] [--into <brandProfileId>]Extract a profile from a website (async — starts pending; poll brand get until ready/failed); --into re-extracts into an existing profile, preserving manually edited fields
brand update <id> [--name <n>] [--json-file <path>]Rename, or patch { name?, visual?, editorial? } — the given top-level blocks are replaced wholesale
brand delete <id>Permanent — no undo, no archive; content generation that referenced it falls back to no brand context

Team

Workspace team members and invitations. Roles: admin, editor, content_creator, viewer.

CommandPurpose
team membersTeam members
team invitationsPending invitations (accepted/revoked/expired ones are history)
team invite <email> --role admin|editor|content_creator|viewer [--name <n>]Invite someone — they receive the accept link by email
team resend <invitationId>Re-email a pending invitation with a fresh link (60 s spacing)
team revoke <invitationId>Revoke a pending invitation — the link stops working immediately
team set-role <userId> <role>Change a member's workspace role
team remove <userId>Remove a member — access lost immediately; cannot be undone (re-invite to restore)

Orders

CommandPurpose
orders list [--product] [--status paid|pending|refunded|failed] [--from/--to] [--search] [--include-test] [--limit/--cursor]Order history, newest first — manual wire-transfer orders awaiting your decision are --status pending; simulated test purchases only with --include-test
orders get <orderId>One order: payment, refund, order bumps, invoice
orders approve <orderId> [--issue-invoice] --confirmApprove a manual (wire-transfer) order — grants access and sends the purchase email; --issue-invoice also issues an invoice via the connected invoicing account (409 = already decided)
orders reject <orderId> --confirmReject a manual order — cancels it and notifies the buyer (409 = already decided)
orders export --from <date> --to <date> [--product] [--status] [--search] [--include-test] [--max-rows <n>] [--out <file>]Orders CSV to a file — the --from/--to window is required; same filters as orders list (row cap default 10000, max 20000)

Subscriptions

Recurring-billing follow-ups: failed charges, customers on manual-renewal plans, and subscriptions that are ending.

CommandPurpose
subscriptions failedFailed subscription / installment charges — customer, product, amount, attempts and Stripe's next automatic retry, invoice id, hosted invoice URL; notes when Stripe is not connected (live dunning fields unavailable)
subscriptions retry <invoiceId> --confirmRetry a failed charge by its Stripe invoice id (in_…) — a real charge attempt; result is paid, requires_action (share the hosted invoice URL), card_declined, not_eligible or failed
subscriptions upcomingCustomers on manual-renewal plans whose access ends soon — days left, last reminder, and the renewal link they need to click
subscriptions endingLive subscriptions set to cancel at period end — still paying until the date shown, then they churn unless the customer reverses the cancellation

Stats

Range via --period 24h|7d|30d|90d or --from/--to (ISO date or instant); --currency forces the report currency.

CommandPurpose
stats overviewHigh-level revenue / order overview
stats sales [--period] [--from] [--to] [--currency]Sales report: revenue, refunds, AOV, per-product breakdown
stats recurring [--currency]MRR / ARR snapshot of the active recurring subscriptions
stats mrr-history [--period] [--from] [--to] [--currency]MRR over time + churn series from the daily snapshots
stats funnel [--period] [--from] [--to]Checkout funnel: sessions → conversions, per checkout
stats traffic [--period] [--from] [--to]Page traffic: views / entries / purchases per page and source
stats sessions [--period] [--from] [--to] [--limit]Recent checkout sessions across every checkout
stats product <id> [--currency]Per-product pulse (last 30 days): revenue, students, engagement, attention
stats conversion-events [--order] [--checkout] [--session] [--status a,b] [--client a,b] [--limit]Conversion-tracking event log — did the Purchase reach Meta / GA4 / TikTok, and with what result
stats conversion-health [--since-days <n>]Per-integration delivery health verdict (healthy / degraded / failing / inactive)

Discounts

CommandPurpose
discounts listDiscount codes
discounts create --code <CODE> (--percent-off <pct> | --amount-off <minor> --currency <c>) [--product <id> …] [--max-redemptions <n>] [--starts <iso>] [--ends <iso>] [--forever] [--inactive]New code — exactly one of --percent-off / --amount-off; --forever applies to every subscription renewal (default: first payment only)
discounts update <discountId> [--percent-off | --amount-off --currency] [--product <id> …] [--max-redemptions <n> | --clear-max-redemptions] [--ends <iso> | --clear-ends] [--forever] [--active | --inactive]Change value, products, limits, dates, active state
discounts archive <discountId>Soft delete — the code stops working at checkout
discounts delete <discountId>Permanent — prefer discounts archive

Referrals

Referral and affiliate programs: a program targets one or more products, enrolled clients get a personal ref code, and purchases attributed to a code count as referrals. Milestone rewards (referral kind) are approved automatically or by you; rewards whose delivery failed can be retried. Program ids come from referrals list, participant and ledger-entry ids from referrals get. The deep config (milestones, commission, attribution) travels as one JSON object — --json <file|inline>.

CommandPurpose
referrals listPrograms with kind, status, enrollment mode, product count and headline stats (participants, valid referrals, rewards pending approval)
referrals get <programId>Program dashboard: summary, participants (ref codes, stats), rewards pending approval and failed deliveries — --json dumps the full documents
referrals create --kind referral|affiliate --name <n> --product <id,id,…> [--enrollment open|inviteOnly|closed] [--status active|inactive] [--json <json|file>]Create a program — --json carries milestones (reward ladder), commission (affiliate terms) and attribution (window, coupon attribution, two-sided discount); flat flags win over --json keys
referrals update <programId> [--name <n>] [--status active|inactive] [--product <id,id,…>] [--enrollment <mode>] [--json <json|file>]Update a program — --product and --json milestones are full replaces (read the program first)
referrals archive <programId> --confirmArchive a program — new referrals stop counting and it leaves the active list
referrals enroll <programId> (--client <clientId> | --email <email>) [--first-name <n>] [--last-name <n>]Enroll one client as a participant and print their ref code — idempotent, a repeat reports alreadyEnrolled
referrals enroll-all <programId> --confirmEnroll every client with access to one of the program products — reports enrolled / alreadyEnrolled / skippedWithoutAccount (legacy access without a login account)
referrals resolve <programId> <entryId> --decision approve|reject --confirmApprove (delivers the reward) or reject a reward waiting for manual approval; the reply says whether delivery already happened
referrals retry-delivery <programId> <entryId>Re-run delivery for a reward listed under failed deliveries

Ads (Meta)

Meta Ads on connected ad accounts: spend per ad joined with Zanfia orders (attributed through the utm_content ad id the ads' URL tags carry), the ads directory behind those ids, and campaign / ad set / ad / creative management. Connection ids come from Settings → Integrations → Meta, ad account ids are Meta's act_… ids. Budgets are in minor units of the account currency. Every command that changes something on the live ad account requires --confirm; new ads are always created paused. The create / creative bodies travel as one JSON object — write the flag as --json <file|inline>.

CommandPurpose
ads performance [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--checkout <checkoutId>]Per-ad spend, impressions, clicks, Meta purchases, Zanfia orders, revenue and ROAS, plus totals and a per-day spend / revenue series; --checkout scopes orders and spend to one checkout (spendScope: checkout)
ads directoryAds known to the workspace from the insights ingest: ad / ad set / campaign ids and names, destination URL and checkout — the lookup for utm_content ad ids in orders and exports
ads campaigns <connectionId> <adAccountId> [--include-archived]Campaigns with status, effective status, objective and budgets
ads campaign update <connectionId> <adAccountId> <campaignId> [--status ACTIVE|PAUSED] [--name <n>] --confirmTurn a campaign on / off, change its daily budget or rename it
ads ad-sets <connectionId> <adAccountId> [--campaign <campaignId>] [--include-archived]Ad sets with budgets, schedule and effective status (e.g. CAMPAIGN_PAUSED)
ads ad-set get <connectionId> <adAccountId> <adSetId>One ad set — budget, schedule, status
ads ad-set update <connectionId> <adAccountId> <adSetId> [--daily-budget <minor>] [--lifetime-budget <minor>] [--end-time <iso>] [--status ACTIVE|PAUSED] --confirmChange an ad set budget (daily or lifetime, not both), schedule end or status — applies to every ad in the set
ads list <connectionId> <adAccountId> [--after <cursor>] [--include-archived]Ads of the account, 50 per page — the reply names the --after cursor for the next page
ads create <connectionId> <adAccountId> --json <json|file> --confirmDuplicate an existing ad into a new, paused one — sourceAdId and name are required; message, headline, description, callToActionType, link, imageUrl override the source creative
ads update <connectionId> <adAccountId> <adId> [--status ACTIVE|PAUSED] [--name <n>] --confirmTurn an ad on / off or rename it
ads creative get <connectionId> <adAccountId> <adId>The ad's creative: primary text, headline, description, CTA, link, image, URL tags — editable: no means video / carousel / dynamic (edit those in Ads Manager)
ads creative update <connectionId> <adAccountId> <adId> --json <json|file> --confirmBuild a new creative from the current one plus the given message, headline, description, callToActionType, link, imageUrl, urlTags and repoint the ad at it
ads sync [--lookback-days <n>]Pull ads insights now for every active Meta connection (the "Sync now" button) — reports connections, connection-day rollups written and failures

Workflows

Workflow automations are edited in the dashboard; the CLI inspects them, pauses/resumes them and (re)starts runs. Runs execute for real — emails, tags and webhooks fire — so the run commands require --confirm.

CommandPurpose
workflows listAutomations with status, triggers, product scope, node counts, last run
workflows get <id>One workflow in detail
workflows runs <id> [--limit]Recent runs, newest first
workflows pause <id>Stop reacting to trigger events
workflows resume <id>Restart a paused workflow (needs a published version)
workflows retry <id> <runId> --confirmRe-run a finished run with its original trigger context as a new run (202 → runId)
workflows test-run <id> --context=<json|file> [--trigger-node <nodeId>] --confirmStart a run from a hand-written trigger context (clientEmail, clientFirstName, productId, orderId, …) — not a dry run; a 503 means the Trigger.dev scheduler is not configured on that environment

Funnels

Marketing funnels are named step chains that reference existing checkouts, pages and workflows — a funnel never owns the artifacts its steps point to. Creation is recipe-first; from-template also generates the page steps with AI.

CommandPurpose
funnels listFunnels with status, recipe, step count
funnels get <id>One funnel's full step chain, workflows and products
funnels create --recipe <lead_magnet|tripwire|sell_product> --product <id> --lang en|pl [--name]Draft funnel over the product's existing checkouts plus the recipe email sequence as a draft workflow (409 when the product lacks the free/paid checkout the recipe needs)
funnels update <id> [--name] [--workflows a,b] [--json <patch>]Rename, swap workflows, or re-wire the step chain (steps/workflowIds are full replaces)
funnels status <id> --set draft|live|archivedStatus transitions (all reversible)
funnels delete <id> --confirmHard-delete the funnel document only; prefer archiving
funnels generate-page <id> --step <n|stepId> --lang en|pl [--brief] [--template]AI-generate the page for one page-type step and wire it — synchronous, 1-3 minutes; consumes AI credits
funnels templatesThe template catalog behind from-template (id, recipe, product requirement, step chain; available: no = coming soon)
funnels from-template <templateId> --product <id> --lang en|pl [--name] [--event-start <iso>] [--event-timezone] [--event-duration <min>] [--event-mode live|on_demand] --confirmCreate the funnel from a template and queue the AI generation of its page steps (202 → per-step runId table + skipped steps; consumes AI credits). Poll funnels get <id> until the steps have pages
funnels from-pages --page <id,id,…> [--name] [--json <file>]A funnel record over existing hosted pages in visitor order (first = entry); nothing is generated
funnels stats <funnelId>Per-step numbers (views / entries / signups, checkout sessions / completed, OTO viewed / accepted) plus totals; n/a = not applicable to that step kind

Pages

Custom hosted HTML pages, served on your storefront domain. Statuses: draft, published.

CommandPurpose
pages listHosted pages
pages get <id>One page, HTML body included
pages create --slug <s> --title <t> (--file <path> | --html <h>) [--publish]New page, draft unless --publish
pages update <id> [--slug] [--title] [--file | --html]Edit
pages publish <id>Go live
pages unpublish <id>Back to draft
pages delete <id>Delete the page and its HTML
pages set-main <id>Use a published page as the storefront homepage
pages unset-mainRevert the storefront to the product grid
pages generate --intent <lead-magnet|webinar|sales-page|thank-you|waitlist|generic> --title <t> --brief <text|file> [--template] [--product] [--checkout] [--form] [--source-url --source-mode rebuild|inspired] [--slug] [--delivery-url] [--delivery-name] [--event-start <iso>] [--event-timezone] [--event-duration <min>] [--event-mode live|on_demand] [--replay-media <mediaId>] [--opt-in-mode single|double] [--form-tags a,b] [--json <file>] --confirmAI-generate a draft page (consumes AI credits; 202 → runId, poll pages list). Signup intents also provision a form, a thank-you page and a draft delivery automation; a 503 means the Trigger.dev scheduler is not configured on that environment
pages edit <pageId> --instruction <text|file> --confirmAI-edit the page's content or design from a described change — synchronous, up to 1-2 minutes; prints the change summary and the revision id of the previous HTML
pages templatesDesign templates usable as pages generate --template seeds (id, name, category, intents)
pages revisions <pageId>HTML snapshots taken before each overwrite (newest first)
pages restore <pageId> <revisionId> --confirmRoll back to a revision (the current HTML is snapshotted first, so a restore is itself undoable)
pages activate-automations <pageId> --confirmPublish the page's draft delivery automation and schedule its webinar follow-up broadcasts — emails send for real; placeholder copy is skipped

Social media

Posts to the connected social accounts (Marketing → Social). Statuses: draft, scheduled, published, failed. Scopes: social:read / social:write.

CommandPurpose
social accountsConnected accounts and their target keys (linkedin, facebook:{pageId}, …)
social posts [--status <s>] [--limit <n>]Browse drafts, scheduled posts and publish history
social get <id>One post: content, media, schedule, publish results (permalinks)
social draft --text <t> --targets <a,b> [--media <ids>] [--first-comment <t>]Save a draft (appears in the dashboard Posts view)
social schedule [id] --at <iso> [--text … --targets … [--media] [--first-comment]]Schedule an existing draft or new content (>= 1 min ahead)
social publish [id] [--text … --targets linkedin [--media] [--first-comment]]Publish now - LinkedIn only (text + images)

Media library

CommandPurpose
media upload --file <path> [--name] [--mime] [--folder] [--description]Upload a file; prints the media id and public URL
media upload-video --file <path> [--title] [--mime] [--wait] [--poll-timeout <s>]Upload a video for lessons (Bunny); transcoding runs after upload
media list [--folder <name>]Browse
media get <id>One item; shows uploadStatus
media rename <mediaId> [--name] [--description | --clear-description]Rename and/or edit the description
media move <mediaId...> (--folder <name> | --unfile)File items into a folder, or unfile them
media transcript <mediaId> [--words] [--out <file>]A video transcript (plain text; --words adds word-level timings and implies JSON)
media delete <id> [--force]Delete (--force if still referenced elsewhere)
media folder listFolders
media folder create <name>New folder (idempotent by name)
media folder rename <name> --to <newName>Rename — renaming onto an existing folder merges into it
media folder delete <name>Delete a folder — its items are unfiled, never deleted

Videos need transcoding

media upload-video without --wait returns while the video is still processing. Poll media get <id> until uploadStatus is ready, then bind it with courses lesson set-video.

Was this article helpful?

Related articles

Spotted something off? Tell us at support@zanfia.com.