From webflow-pack
Generates TypeScript examples for Webflow Data API v2: list sites, read CMS collections/items, create items. For new integrations, testing setups, or learning basics.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin webflow-packThis skill is limited to using the following tools:
Minimal working examples demonstrating the three core Webflow Data API v2 operations:
Manages Webflow CMS collections and items via Data API v2: list, CRUD (single/bulk), publish. For dynamic content like blog posts or team members.
Automates Webflow CMS collections, site publishing, page management, asset uploads, and ecommerce orders via Composio tools and Rube MCP. Use for managing live sites programmatically.
Automates Webflow CMS collections, site publishing, page management, asset uploads, and ecommerce orders via Rube MCP (Composio). Useful for programmatic site and content management.
Share bugs, ideas, or general feedback.
Minimal working examples demonstrating the three core Webflow Data API v2 operations: listing sites, reading CMS collections/items, and creating a CMS item.
webflow-install-auth setupwebflow-api package installedsites:read and cms:read scopesEvery Webflow API call starts with a site_id. List your sites to find it:
// hello-webflow.ts
import { WebflowClient } from "webflow-api";
const webflow = new WebflowClient({
accessToken: process.env.WEBFLOW_API_TOKEN!,
});
async function listSites() {
const { sites } = await webflow.sites.list();
for (const site of sites!) {
console.log(`${site.displayName}`);
console.log(` ID: ${site.id}`);
console.log(` Short name: ${site.shortName}`);
console.log(` Custom domains: ${site.customDomains?.map(d => d.url).join(", ")}`);
console.log(` Last published: ${site.lastPublished}`);
console.log(` Locales: ${site.locales?.map(l => l.displayName).join(", ")}`);
}
}
listSites().catch(console.error);
Collections define your content types (blog posts, team members, products, etc.):
async function listCollections(siteId: string) {
const { collections } = await webflow.collections.list(siteId);
for (const col of collections!) {
console.log(`Collection: ${col.displayName}`);
console.log(` ID: ${col.id}`);
console.log(` Slug: ${col.slug}`);
console.log(` Item count: ${col.itemCount}`);
console.log(` Fields:`);
for (const field of col.fields || []) {
console.log(` - ${field.displayName} (${field.type}, required: ${field.isRequired})`);
}
}
}
// Usage: pass your site_id
listCollections("your-site-id").catch(console.error);
Fetch items from a collection — staged (draft) or live (published):
async function readItems(collectionId: string) {
// Get staged (draft + published) items
const { items } = await webflow.collections.items.listItems(collectionId, {
limit: 10,
offset: 0,
});
for (const item of items!) {
console.log(`Item: ${item.fieldData?.name || item.id}`);
console.log(` ID: ${item.id}`);
console.log(` Slug: ${item.fieldData?.slug}`);
console.log(` Draft: ${item.isDraft}`);
console.log(` Archived: ${item.isArchived}`);
console.log(` Created: ${item.createdOn}`);
}
// Get live (published) items only
const live = await webflow.collections.items.listItemsLive(collectionId, {
limit: 10,
});
console.log(`\nLive items: ${live.items?.length}`);
}
async function createBlogPost(collectionId: string) {
// Items are created as drafts by default (isDraft: true)
const item = await webflow.collections.items.createItem(collectionId, {
fieldData: {
name: "Hello from the API",
slug: "hello-from-api",
// Field names must match your collection schema
// Use the slug version of field names (lowercase, hyphens)
"post-body": "<p>This post was created via the Webflow Data API v2.</p>",
"author": "API Bot",
"published-date": new Date().toISOString(),
},
isDraft: false, // Set false to stage for publishing
});
console.log(`Created item: ${item.id}`);
console.log(` Draft: ${item.isDraft}`);
console.log(` Slug: ${item.fieldData?.slug}`);
return item;
}
import { WebflowClient } from "webflow-api";
const webflow = new WebflowClient({
accessToken: process.env.WEBFLOW_API_TOKEN!,
});
async function main() {
// 1. Get first site
const { sites } = await webflow.sites.list();
const site = sites![0];
console.log(`Using site: ${site.displayName} (${site.id})\n`);
// 2. List collections
const { collections } = await webflow.collections.list(site.id!);
console.log(`Found ${collections!.length} collections:`);
for (const col of collections!) {
console.log(` - ${col.displayName} (${col.itemCount} items)`);
}
// 3. Read items from first collection
if (collections!.length > 0) {
const firstCol = collections![0];
const { items } = await webflow.collections.items.listItems(firstCol.id!, {
limit: 5,
});
console.log(`\nFirst ${items!.length} items in "${firstCol.displayName}":`);
for (const item of items!) {
console.log(` - ${item.fieldData?.name} (${item.id})`);
}
}
console.log("\nWebflow connection verified successfully.");
}
main().catch(console.error);
Run it:
npx tsx hello-webflow.ts
Webflow connection verified successfully.| Error | Cause | Solution |
|---|---|---|
401 Unauthorized | Bad token | Re-check token at developers.webflow.com |
403 Forbidden | Missing cms:read scope | Add scope to token or app |
404 Not Found | Wrong site_id or collection_id | List sites first to get valid IDs |
429 Too Many Requests | Rate limited | Wait 60s (Retry-After header) |
Empty sites array | Token has no site access | Check workspace token permissions |
sites.list().collections.list(siteId).post-body, not Post Body).isDraft: true. Set false to stage for publishing.listItems() returns all items; listItemsLive() returns only published.Proceed to webflow-local-dev-loop for development workflow setup.