HubSpot CMS blog publishing with editorial approval

The HubSpot publisher targets the CMS Blog Posts API. It binds every article to a configured blog and author, searches by slug before writing, carries the reviewed body and metadata into native fields, and changes the post state only during an approved publish run.

Best for: B2B marketing teams whose website, forms, campaigns, and canonical blog already run through HubSpot CMS Hub.

Support status: stable
Publisher: hubspot

How the connection works

Authentication

A HubSpot private app token with CMS blog access, plus the destination Content Group ID and Blog Author ID held in the publisher environment.

Server-side variables

  • SEO_BLOG_PUBLISH_TOKEN
  • SEO_BLOG_SITE_SLUG
  • HUBSPOT_PRIVATE_APP_TOKEN
  • HUBSPOT_CONTENT_GROUP_ID
  • HUBSPOT_AUTHOR_ID
  • SEO_BLOG_SITE_ORIGIN

Publishing sequence

01

Search HubSpot blog posts for the reviewed slug.

02

Map the title, body, summary, metadata, author, and blog group.

03

Patch the existing post or create a native CMS blog post.

04

Move the saved post to PUBLISHED only for a live run and record its returned URL.

No-write payload and mapping preview

SEO_BLOG_PUBLISH_TOKEN='<tenant-token>' \
SEO_BLOG_SITE_SLUG='<slug>' \
HUBSPOT_PRIVATE_APP_TOKEN='<hubspot-private-app-token>' \
HUBSPOT_CONTENT_GROUP_ID='<hubspot-content-group-id>' \
HUBSPOT_AUTHOR_ID='<hubspot-author-id>' \
SEO_BLOG_SITE_ORIGIN='<origin>' \
npx --yes @wotaso/seo-blog-admin-cli publish-cms --cms hubspot --site-slug '<slug>' --site-origin '<origin>' --dry-run

For a currently due approved post, --dry-run builds the destination payload without writing to the provider. It does not contact the provider or prove credentials, remote schema compatibility, deployment, or a live page; when no post is due, provider inputs are not exercised.

Scheduled runner health heartbeat

SEO_BLOG_PUBLISH_TOKEN='<tenant-token>' \
SEO_BLOG_SITE_SLUG='<slug>' \
HUBSPOT_PRIVATE_APP_TOKEN='<hubspot-private-app-token>' \
HUBSPOT_CONTENT_GROUP_ID='<hubspot-content-group-id>' \
HUBSPOT_AUTHOR_ID='<hubspot-author-id>' \
SEO_BLOG_SITE_ORIGIN='<origin>' \
SEO_BLOG_SCHEDULED='true' \
npx --yes @wotaso/seo-blog-admin-cli health --config seo-blog.config.json --mode cms --site-slug '<slug>' --site-origin '<origin>'

Run this from the real scheduled runner after seo-blog.config.json contains the selected connector configuration and the listed secrets are loaded. It reports configuration readiness, not a live article. The dashboard expires readiness at the returned deadline, with a 36-hour fallback when no deadline is available.

Fields and data model

  • Content group and author are explicit required mappings
  • Meta description, HTML title, body, and summary use native blog fields
  • Featured image and alt text are sent when a durable URL exists

Canonical and route

HubSpot CMS owns the domain connection, URL structure, canonical markup, sitemap, templates, analytics, and public rendering. SEODrafts accepts the post URL returned by HubSpot as authoritative.

Known limits

  • The private app needs the correct CMS scopes for reading and writing blog posts.
  • Content Group and Author IDs must belong to the destination account.
  • HubSpot theme modules beyond standard blog fields require custom template or field work.

Recovery

  • Create a draft in the target blog and verify its author, template, and preview URL.
  • Inspect private-app scopes and account IDs when HubSpot returns authorization errors.
  • Rerun with the same slug after fixing the mapping so the adapter updates instead of duplicates.

Integration questions

Where are credentials stored?

The HubSpot credentials stay in CI or another server-side secret environment.

Does SEODrafts own the canonical URL?

No. The destination website owns its route, canonical tag, sitemap, rendering, and analytics.

Can the integration be tested before publishing?

Use the no-write payload preview to inspect local mapping, then separately test credentials and the first destination draft before a live run.

Keep the website your team already owns.

Review the opportunity and draft in SEODrafts, then publish approved work through HubSpot.

Add my website