Skip to content
Content
Skill

/webiny-sdk

Using @webiny/sdk to read and write CMS data from external applications. Use this skill when the developer is building a Next.js, Vue, Node.js, or any external app that needs to fetch or write content to Webiny, set up the SDK, use the Result pattern,

BOOST
From plugin
webiny-js
8k76 skills3 MCP
Install
$ npx -y skills add webiny/webiny-js --skill webiny-sdk --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/webiny-sdk

Context preview

The summary Claude sees to decide when to auto-load this skill.

Using @webiny/sdk to read and write CMS data from external applications. Use this skill when the developer is building a Next.js, Vue, Node.js, or any external app that needs to fetch or write content to Webiny, set up the SDK, use the Result pattern,

SKILL.md

webiny-sdk.SKILL.md
name: webiny-sdk
description: >
  Using @webiny/sdk to read and write CMS data from external applications.
  Use this skill when the developer is building a Next.js, Vue, Node.js, or any external app
  that needs to fetch or write content to Webiny, set up the SDK, use the Result pattern,
  list/get/create/update/publish entries, filter and sort queries, use TypeScript generics
  for type safety, work with the File Manager, list languages, trigger or monitor background tasks, or create API keys programmatically.
  Covers read vs preview mode, the `values` wrapper requirement, correct method names,
  and the `fields` required parameter, and background task management via `sdk.tasks`. Also covers common SDK errors and troubleshooting
  — especially "Content model '<modelName>' not found" (which usually means the API key
  lacks permission, not that the model is missing) and the Website Builder starter
  pitfall of reusing NEXT_PUBLIC_WEBSITE_BUILDER_API_KEY for Headless CMS reads (that key
  has no CMS permissions by default and needs either extension or replacement with a
  CMS-scoped key).

Webiny SDK

TL;DR

The `@webiny/sdk` package provides a TypeScript interface for external apps (Next.js, Vue, Node.js) to interact with Webiny's Headless CMS and File Manager. Every method returns a `Result` object (checked with `isOk()`). Supports listing, getting, creating, updating, publishing, and unpublishing entries with filtering, sorting, pagination, and TypeScript generics for type safety.

Installation & Setup

npm install @webiny/sdk

Initialize once and reuse:

// lib/webiny.ts
import { Webiny } from "@webiny/sdk";

export const webiny = new Webiny({
  token: process.env.WEBINY_API_TOKEN!,
  endpoint: process.env.WEBINY_API_ENDPOINT!,
  tenant: process.env.WEBINY_API_TENANT || "root"
});
  • `token` -- API key token generated in Webiny Admin (Settings > API Keys)
  • `endpoint` -- The base API URL, **without a trailing slash**. Run `yarn webiny info` in your Webiny project to find the API URL. For Website Builder projects use `NEXT_PUBLIC_WEBSITE_BUILDER_API_HOST`.
  • `tenant` -- Tenant ID, defaults to `"root"`

> **IMPORTANT:** Never add a trailing slash to `endpoint`. The SDK appends `/graphql` to the endpoint internally, so `https://xxx.cloudfront.net/` will break all requests.

The `fields` Parameter (Required)

Every SDK method requires a `fields` array that specifies which fields to return. Omitting it will cause a runtime error.

  • Use `"values.<fieldId>"` for content fields: `"values.name"`, `"values.price"`
  • Use top-level field names for metadata: `"id"`, `"entryId"`, `"createdOn"`, `"status"`
  • Use dot notation for nested fields: `"values.author.name"`
// Minimal fields -- just IDs
fields: ["id", "entryId"];

// Content fields
fields: ["id", "entryId", "values.name", "values.price", "values.description"];

// Nested reference fields
fields: ["id", "values.title", "values.author.name", "values.author.email"];

CMS: Read vs Preview Mode

`webiny.cms.listEntries` and `webiny.cms.getEntry` accept a `preview` parameter to control which revisions are returned:

| `preview` | Returns | Use For | | ----------------- | ------------------------------------ | -------------------------------- | | `false` (default) | Published entries only | Public-facing apps, SSG | | `true` | Latest revision (draft or published) | Content preview, editorial tools |

Write operations (`createEntry`, `updateEntryRevision`, etc.) are not affected by `preview`.

The Result Pattern

Every SDK method returns a `Result` object -- it never throws:

const result = await webiny.cms.listEntries({
  modelId: "product",
  fields: ["id", "values.name"]
});

if (result.isOk()) {
  console.log(result.value.data); // success -- typed data
} else {
  console.error(result.error.message); // failure -- error info
}

TypeScript Generics

Pass a type parameter for full type safety on `values`:

import type { CmsEntryData } from "@webiny/sdk";

interface Product {
  name: string;
  price: number;
  sku: string;
  description: string;
  category?: CmsEntryData<ProductCategory>;
}

interface ProductCategory {
  name: string;
  slug: string;
}

const result = await webiny.cms.listEntries<Product>({
  modelId: "product",
  fields: ["id", "entryId", "values.name", "values.price", "values.sku"]
});

if (result.isOk()) {
  // result.value.data is CmsEntryData<Product>[]
  const products = result.value.data;
  // products[0].values.name -- fully typed
}

Reference fields like `category` are typed as `CmsEntryData<T>`, which wraps referenced entries with `id`, `entryId`, and `values`.

Reading Data

List Entries

const result = await webiny.cms.listEntries<Product>({
  modelId: "product",
  fields: ["id", "entryId", "values.name", "values.price"],
  sort: { "values.name": "asc" },
  limit: 10
});

List with Filters

const result = await webiny.cms.listEntries<Product>({
  modelId: "product",
  fields: ["id", "entryId", "values.name", "values.price"],
  where: {
    "values.price_gte": 100,
    "values.name_contains": "Pro"
  },
  sort: { "values.price": "desc" }
});

Filter Operators

| Operator | Description | Example | | -------------- | ------------------ | -------------------------------------------- | | `_eq` | Equals (default) | `"values.status": "active"` | | `_not` | Not equals | `"values.status_not": "archived"` | | `_contains` | Contains substring | `"values.name_contains": "Pro"` | | `_startsWith` | Starts with | `"values.name_startsWith": "Web"` | | `_gt` / `_gte` | Greater than / >= | `"values.price_gte": 100` | | `_

Read more
Ships withwebiny-js

Open-source content platform. Self-hosted on AWS serverless. Built as a TypeScript framework you extend with code, not a closed product you configure through a UI. Runs on Lambda, DynamoDB, S3, and CloudFront inside your own AWS account. Scales automatically.

Get the whole plugin
Stats
8,048
Stars
682
Forks
Active
Maintenance
TypeScript
Language
2h ago
Last commit
8y ago
Created
9h ago
Added

Repo: webiny/webiny-js

Other skills on webiny-js.