CMS CLI publisher
One command shape for nine stable CMS targets, plus Framer beta
Always begin with --dry-run. It reads approved due posts and builds a no-write payload and field-mapping preview, but it does
not call the CMS provider, validate credentials or schema remotely, publish a page, or prove that a public canonical route exists.
If no post is due, provider-specific preview work might not run. Remove --dry-run only after review; then verify the first
rendered route before enabling the scheduled runner.
SEO_BLOG_PUBLISH_TOKEN=<tenant-token> \
SEO_BLOG_SITE_SLUG=flashes \
WEBFLOW_TOKEN=<webflow-token> \
WEBFLOW_COLLECTION_ID=<collection-id> \
npx --yes @wotaso/seo-blog-admin-cli@0.2.2 publish-cms \
--cms webflow \
--site-slug flashes \
--site-origin https://flashes.app \
--field-map 'post-body=bodyHtml,meta-title=metaTitle,meta-description=metaDescription' \
--dry-run
WordPress: WORDPRESS_URL, WORDPRESS_USERNAME, WORDPRESS_APP_PASSWORD
Webflow: WEBFLOW_TOKEN, WEBFLOW_COLLECTION_ID, --field-map
Framer (beta): FRAMER_API_KEY, FRAMER_PROJECT_URL, FRAMER_COLLECTION
Wix: WIX_API_KEY, WIX_SITE_ID, WIX_MEMBER_ID
Contentful: CONTENTFUL_MANAGEMENT_TOKEN, CONTENTFUL_SPACE_ID, CONTENTFUL_CONTENT_TYPE
Sanity: SANITY_TOKEN, SANITY_PROJECT_ID, SANITY_DATASET
Strapi: STRAPI_URL, STRAPI_TOKEN, STRAPI_COLLECTION
Ghost: GHOST_ADMIN_URL, GHOST_ADMIN_API_KEY
Shopify: SHOPIFY_SHOP, SHOPIFY_ADMIN_TOKEN, SHOPIFY_BLOG_ID
HubSpot: HUBSPOT_PRIVATE_APP_TOKEN, HUBSPOT_CONTENT_GROUP_ID, HUBSPOT_AUTHOR_ID
Webhook: WEBHOOK_URL, optional WEBHOOK_SECRET
Squarespace uses a reviewed manual handoff
There is no native Squarespace publisher command in this flow. Hand the approved Markdown or HTML to a human editor, create or update
the draft in the tenant's normal Squarespace session, and verify the rendered route, metadata, assets, and canonical URL before recording
completion. Do not request a Squarespace password or show an optional Squarespace webhook command. If the tenant already owns an
authenticated custom publisher, select the generic webhook connector separately and document it as that connector.
Scheduled-runner health heartbeat
Run this from the real CMS cron or server runner after seo-blog.config.json contains the matching site identity and
cms.provider. Keep the publish token and provider credential in that runner's secret store.
{
"apiUrl": "https://api.seodrafts.com",
"siteSlug": "flashes",
"siteOrigin": "https://flashes.app",
"publishTokenEnv": "SEO_BLOG_PUBLISH_TOKEN",
"cms": { "provider": "webflow" }
}
SEO_BLOG_PUBLISH_TOKEN=<tenant-token> \
SEO_BLOG_SCHEDULED=true \
WEBFLOW_TOKEN=<webflow-token> \
npx --yes @wotaso/seo-blog-admin-cli@0.2.2 health \
--config seo-blog.config.json \
--mode cms \
--site-slug flashes \
--site-origin https://flashes.app
The heartbeat checks the runner's config, site identity, declared schedule, publish token, and presence of the configured provider secret;
it does not validate the provider credential remotely or prove that a page is live. Ready status is temporary: use the server-provided
publisherHealthNextExpectedAt deadline, or treat a heartbeat older than 36 hours as stale when no deadline is available.