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.
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
| Command | Purpose |
|---|---|
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 logout | Clear stored credentials |
zanfia auth status | Show 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 whoami | The workspace (or community member) identity behind the current credential |
Products
| Command | Purpose |
|---|---|
products list | List 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.
| Command | Purpose |
|---|---|
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> --confirm | Eagerly create community member docs for every active customer whose plan includes the community |
Downloads content (products files)
| Command | Purpose |
|---|---|
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.
| Command | Purpose |
|---|---|
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> --confirm | Permanent — 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] --confirm | Permanent; a non-empty category needs --delete-articles |
Offers (checkouts)
An offer is a checkout of a product; <cid> comes from offers list.
| Command | Purpose |
|---|---|
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> --confirm | Sends 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.
| Command | Purpose |
|---|---|
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 list | The 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.
| Command | Purpose |
|---|---|
site get | Summary 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 --confirm | Publish or unpublish the site (the platform "live" flag) |
site slug set <slug> --confirm | Change 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 --confirm | Replace 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 get | Storefront 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>] --confirm | Write 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.
| Command | Purpose |
|---|---|
emails list | The 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 get | The 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":"…"}]}}'.
| Command | Purpose |
|---|---|
oto list | The 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> --confirm | Permanent; checkout oto.steps still referencing the id are left dangling |
Order bumps
| Command | Purpose |
|---|---|
bumps list | Order 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.
| Command | Purpose |
|---|---|
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> | --clear | Lesson thumbnail; video lessons also get it as the player thumbnail |
courses lesson set-attachments <pid> <lid> --media <id,id,…> | --clear | Replace 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>] --confirm | Replace 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.
| Command | Purpose |
|---|---|
certificates list [--product <value>] [--limit <n>] | List certificates |
certificates issue <productId> --client <a,b,...> [--only-completed|--no-only-completed] [--template <value>] --confirm | Issue certificates (async) |
certificates revoke <certificateId> --confirm | Revoke a certificate |
certificates reinstate <certificateId> | Reinstate a certificate |
certificates delete <certificateId> --confirm | Delete a revoked certificate |
certificates resend <certificateId> --confirm | Resend the certificate email |
certificates templates list | List 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> --confirm | Delete 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.
| Command | Purpose |
|---|---|
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> --confirm | Copy 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] --confirm | Promote 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> --confirm | Drop the staged edits; the published issue stays as is |
newsletters issues delete <iid> --product <pid> --confirm | Permanent — 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.
| Command | Purpose |
|---|---|
communities list | Your 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> --confirm | Delete 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> --confirm | Delete an event (staff) |
communities moderation queue <cid> | Reported posts / comments — pending plus recently resolved (staff) |
communities moderation resolve <cid> <reportId> --action remove|dismiss [--note] --confirm | Remove the content (author notified) or dismiss the report |
Conversations (client DMs)
The creator ↔ client direct-message inbox. Conversations are keyed by client id (wcid).
| Command | Purpose |
|---|---|
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> --confirm | Soft-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] --confirm | DMs every listed client (max 5000), irreversibly — the fan-out runs async |
Clients
| Command | Purpose |
|---|---|
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 tags | The 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> --confirm | Stop 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] --confirm | Revoke 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>) --confirm | Shift many clients' access end at once (absolute date or relative days) — 202 with the run id |
clients bulk-delete <productId> (--email a,b,… | --all) --confirm | Remove 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] --confirm | Revoke 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,…> --confirm | Delete (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> --confirm | Sign one device out — it must claim a slot again |
clients devices reset <id> --confirm | Sign 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).
| Command | Purpose |
|---|---|
campaigns list | Email 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>] --confirm | Sends 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.
| Command | Purpose |
|---|---|
segments list | Audience 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.
| Command | Purpose |
|---|---|
forms list | Signup 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> --confirm | Permanent; 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.
| Command | Purpose |
|---|---|
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> --confirm | Permanent |
testimonials request --client <clientId> [--product <pid>] [--form <formId>] --confirm | Sends 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 list | Collection 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> --confirm | Permanent |
Brand profiles
Extracted or hand-built brand identity (colors, fonts, voice) used by content generation and site update theme --from-brand.
| Command | Purpose |
|---|---|
brand list | Brand 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.
| Command | Purpose |
|---|---|
team members | Team members |
team invitations | Pending 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
| Command | Purpose |
|---|---|
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] --confirm | Approve 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> --confirm | Reject 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.
| Command | Purpose |
|---|---|
subscriptions failed | Failed 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> --confirm | Retry 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 upcoming | Customers on manual-renewal plans whose access ends soon — days left, last reminder, and the renewal link they need to click |
subscriptions ending | Live 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.
| Command | Purpose |
|---|---|
stats overview | High-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
| Command | Purpose |
|---|---|
discounts list | Discount 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>.
| Command | Purpose |
|---|---|
referrals list | Programs 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> --confirm | Archive 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> --confirm | Enroll 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 --confirm | Approve (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>.
| Command | Purpose |
|---|---|
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 directory | Ads 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>] --confirm | Turn 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] --confirm | Change 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> --confirm | Duplicate 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>] --confirm | Turn 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> --confirm | Build 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.
| Command | Purpose |
|---|---|
workflows list | Automations 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> --confirm | Re-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>] --confirm | Start 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.
| Command | Purpose |
|---|---|
funnels list | Funnels 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|archived | Status transitions (all reversible) |
funnels delete <id> --confirm | Hard-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 templates | The 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] --confirm | Create 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.
| Command | Purpose |
|---|---|
pages list | Hosted 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-main | Revert 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>] --confirm | AI-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> --confirm | AI-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 templates | Design 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> --confirm | Roll back to a revision (the current HTML is snapshotted first, so a restore is itself undoable) |
pages activate-automations <pageId> --confirm | Publish 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.
| Command | Purpose |
|---|---|
social accounts | Connected 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
| Command | Purpose |
|---|---|
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 list | Folders |
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?

