AgentStack
SKILL verified MIT Self-run

Publish Webflow

skill-busyeugene-content-marketing-skills-publish-webflow · by busyeugene

Use when the user wants to publish a blog post draft to Webflow CMS via the Webflow API v2. Reads a markdown draft, maps frontmatter to CMS fields, creates or updates the CMS item, and optionally publishes it live.

No reviews yet
0 installs
8 views
0.0% view→install

Install

$ agentstack add skill-busyeugene-content-marketing-skills-publish-webflow

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Publish Webflow? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Publish → Webflow

Publish a local markdown draft to Webflow CMS using the Webflow Data API v2.

Setup

Required:

  • WEBFLOW_API_TOKEN — a Data API token (Site Settings → Apps & Integrations → API Access). Scopes needed: cms:read, cms:write, sites:read, and (if publishing live) sites:write.
  • WEBFLOW_SITE_ID — the site ID. Get it via GET https://api.webflow.com/v2/sites.
  • WEBFLOW_COLLECTION_ID — the collection ID for your blog. Get it via GET /v2/sites/{site_id}/collections.

Optional:

  • SLACK_WEBHOOK_URL — Slack notification when a post goes live.

All requests use:

Authorization: Bearer ${WEBFLOW_API_TOKEN}
accept-version: 2.0.0
Content-Type: application/json

Inputs

  1. Draft path — markdown file under posts/.
  2. Modedraft (create as unpublished) or live (create and publish). Default draft.
  3. Update existing? — if an item with the same slug exists, skip, update, or create-new. Default update.
  4. Field mapping override — optional: path to a JSON file mapping markdown frontmatter keys to Webflow field slugs. Default mapping is documented below.

Use AskUserQuestion for mode and existing-item handling.

Process

1. Validate collection schema

Call GET /v2/collections/{WEBFLOW_COLLECTION_ID} and cache the field list. For each required frontmatter field, confirm there's a matching Webflow field. If a field is missing, fail fast with a clear error naming which Webflow field to add.

Default frontmatter → Webflow field mapping:

| Frontmatter key | Webflow field slug | Type | |---|---|---| | title | name | PlainText | | slug | slug | PlainText | | meta_title | meta-title | PlainText | | meta_description | meta-description | PlainText | | author | author | Reference (fallback PlainText) | | date | published-on | Date | | hero_image | hero-image | Image | | hero_alt | hero-alt | PlainText | | keyword | primary-keyword | PlainText | | status | — | (ignored; controlled by publish mode) | | body | post-body | RichText |

If the user provides a custom mapping file, it overrides the defaults.

2. Load and transform the draft

  • Read the draft file.
  • Parse frontmatter.
  • Convert the markdown body to Webflow-compatible HTML (rich text field accepts HTML). Preserve: headings, paragraphs, lists, code blocks, blockquotes, images, internal and external links. Strip any HTML comments.
  • For the hero image:
  • If hero_image is a local path, upload via the Webflow 2-step asset flow:
  1. POST /v2/sites/{site_id}/assets with { "fileName": "...", "fileHash": "" }. Response contains uploadUrl and uploadDetails (S3 presigned fields).
  2. POST the file as multipart/form-data to uploadUrl, including every field from uploadDetails before the file field. Use the returned asset hostedUrl in fieldData.
  • If it's already a URL, use it directly.
  • For author as a reference field: resolve the name to an author collection item ID via GET /v2/collections/{authors_collection}/items?name={author}. If not found, create the author item (or fall back to PlainText if the field allows).

3. Check for existing item

GET /v2/collections/{WEBFLOW_COLLECTION_ID}/items?slug={slug}

  • If found and mode is skip → stop and report.
  • If found and mode is updatePATCH /v2/collections/{collection_id}/items/{item_id} with the new field values.
  • If found and mode is create-new → append a timestamp to the slug, then create.
  • If not found → create.

4. Create or update

Create (staged / draft):

POST /v2/collections/{WEBFLOW_COLLECTION_ID}/items
{
  "isArchived": false,
  "isDraft": ,
  "fieldData": { ...mapped fields }
}

For bulk creates (multiple posts at once), use POST /v2/collections/{collection_id}/items/bulk with { "items": [...] }. As of Dec 2024 newly-created items default to isDraft: true unless the live endpoint is used.

Create live (mode = live, single call): use POST /v2/collections/{collection_id}/items/live with the same body and isDraft: false. This creates and publishes in one shot.

Update:

PATCH /v2/collections/{WEBFLOW_COLLECTION_ID}/items/{item_id}
{
  "fieldData": { ...mapped fields }
}

Handle the response: capture id (item ID), lastUpdated, and the fieldData.slug.

5. Publish live (if mode = live)

There is no per-item "promote to live" endpoint in the Data API v2. Options:

  • New item: use the POST /v2/collections/{collection_id}/items/live create-live endpoint above in step 4 instead of the staged create.
  • Existing staged item: PATCH the item with isDraft: false via PATCH /v2/collections/{collection_id}/items/{item_id}/live (the /live bulk-publish path accepts an item list) — or publish the entire site, which flushes all staged items.

Then publish the site so any staged changes are visible:

POST /v2/sites/{WEBFLOW_SITE_ID}/publish
{ "publishToWebflowSubdomain": false, "customDomains": [...] }

Or if the user only wants the staging subdomain:

{ "publishToWebflowSubdomain": true }

Ask via AskUserQuestion the first time a publish runs in a session.

6. Optional Slack notification

If SLACK_WEBHOOK_URL is set and publish was successful:

POST ${SLACK_WEBHOOK_URL}
Content-Type: application/json

{ "text": "📤 New post published: " }

7. Write a publish log

Append a line to publish-log.md at the project root:

| {YYYY-MM-DD HH:mm} | webflow | {mode} | {title} | {item_id} | {live URL or "—"} | {status} |

8. Print summary

One block: mode, item ID, live URL (if live), Slack notified (Y/N), publish-log updated.

Fallbacks

  • Missing required env var: fail fast with the exact variable name.
  • Collection schema mismatch: print the missing field names and the expected types; do not guess.
  • Image upload fails: ask the user to upload manually via Webflow and retry; don't create the item without the hero if the field is required.
  • Site publish rate limited: create/update the item anyway, skip the site publish, and note it in the log so the user can publish manually from the Webflow dashboard.

Safety

  • Default mode is draft. This skill never publishes live without explicit user confirmation in the current session.
  • Never delete existing items. If a collision happens in skip mode, stop and report.

Verification

  1. Item exists in the target collection (GET /items/{id} returns 200).
  2. fieldData contains all mapped fields with expected values.
  3. If mode = live, the public URL returns 200.
  4. publish-log.md has a new row.
  5. Slack notification delivered (if configured).

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.