First-party app
Storyblok

Storyblok

Storyblok: read published and draft content, and create, update, publish and organise stories.

stable Content ManagementMarketing

About

Storyblok ships in the w6w first-party pack. It declares 14 actions, 2 health checks, and the host runs its code in a sandbox that never sees the credential.

App id
io.w6w.storyblok
Version
0.1.1
Author
w6w
Licence
MIT
Categories
Content Management · Marketing

Overview

Storyblok is a headless CMS, and this app reads and writes the content stored in it: fetch a story in its draft or published form, list and search stories, create and update them, publish or unpublish, move a story between folders, and read the components, assets and datasource entries a space is built from.

Storyblok splits its API in two — a fast, read-only Content Delivery API for published (or draft) content, and a slower Management API for authoring — and this app routes each action to the one it needs. Updates merge into a story’s existing content rather than overwriting it wholesale, since Storyblok itself replaces the whole content object on a plain write, and validates a story’s structure before sending it so a malformed nested block doesn’t silently render as empty in the editor.

Beyond individual stories, this app lists a space’s components (the schemas content must satisfy), its uploaded assets, and the site’s link structure, giving a workflow enough visibility to navigate and maintain a content tree, not just edit one entry at a time.

Build with Storyblok

Three routes to the same 14 actions. The Workflow tab is generated from Storyblok's own manifest and carries its real ids, so it is copy-pasteable; the Code and CLI examples are the same call for any action on any app, so every app-specific value in them is a blank you fill in.

List assets

asset-list

Uploaded files. Note an asset URL is a CDN URL that outlives the record by up to a year, the Image Service resizes by URL SUFFIX rather than stored variants, and a PRIVATE asset needs its own token to load.

List components

component-list

The schemas stories are built from — what `story-create` has to satisfy. Storyblok stores UNKNOWN FIELDS silently rather than rejecting them, so content can import cleanly and render nothing. Separates content types from nestable blocks.

List datasource entries

datasource-entry-list

The key/value lists a space keeps outside its content — country codes, plan names, dropdown options. A story stores the VALUE, so this is what turns `de` into `Germany`. Dimensions are translations, and a missing one falls back silently.

List links

link-list

The site tree without any content — one small object per story. The right call for navigation, sitemaps and path checks, all of which are usually done by listing STORIES and dragging every story's content across to read one field.

Get the space and cache version

space-get

The space this token belongs to, and its `version` — which IS the `cv` cache version. Passing that to later delivery calls moves them from 50 requests a second to 1000, and Storyblok's own documentation makes this the first call of a run.

List spaces

space-list

Every space this token reaches — which is often more than intended, since a personal access token defaults to ALL SPACES its owner has. Reports each plan, because the plan decides whether the Management API allows 3 or 6 requests a second.

Create a story

story-create

Write a new content entry. Validates Storyblok's CONTENT SHAPE RULES first — every component needs a `component` property and every nested one a `_uid` — because a missing `_uid` either errors about a field or imports as an empty block nobody notices.

Delete a story

story-delete

Remove a content entry from the editor and the site. UNPUBLISHING is the reversible alternative and usually what was meant. Deleting a FOLDER takes everything inside it, so that needs an explicit acknowledgement.

Get a story

story-get

One content entry through the delivery API. DRAFT and PUBLISHED are separate documents, and a public token cannot see the draft at all — which is why an edit 'does not show'. Returns the `cv` cache version, which makes the next request twenty times cheaper.

List stories

story-list

Content entries through the delivery API. Note a BIGGER PAGE IS SLOWER: Storyblok's rate limit falls from 50 requests a second at 25 per page to 6 at 100, so 25 per page moves twice the content. Defaults to 25 and reports the limit it is under.

Move a story to another folder

story-move

Move a story between folders, which CHANGES ITS URL — the full slug is the folder path plus the story's slug, and Storyblok leaves no redirect behind. A published story changes address the moment this runs, with no publish step.

Publish or unpublish a story

story-publish

Make a story live, or take it down. Publishing copies the DRAFT over the live version — including anybody else's unsaved-to-live edits — so this reports whether the story had unpublished changes first. Note Storyblok's publish endpoint is a GET.

Search stories (management)

story-search

Stories as the EDITOR sees them — who changed what, what is published, and what has UNPUBLISHED CHANGES, which the delivery API cannot see at all. Slower: the Management API allows 3 to 6 requests a second.

Update a story

story-update

Change a story. Storyblok REPLACES content rather than merging, so this reads first and merges by default — a two-field payload would otherwise leave a two-field story. Touches the DRAFT unless `publish` is set.

A workflow step names the app and the action, and the editor fills in the connection when you pick one. This is the Step shape from the workflow spec, carrying Storyblok's real ids.

{
  "manifestVersion": "2",
  "name": "storyblok-example",
  "steps": [
    {
      "id": "datasource-entry-list",
      "uses": {
        "app": "io.w6w.storyblok",
        "action": "datasource-entry-list",
        "connection": "conn_YOUR_CONNECTION_ID"
      },
      "with": {
        "datasource": "<datasource>"
      }
    }
  ]
}

Here are some of the things you can do

  • List datasource entries

    read
    datasource-entry-list
  • List assets

    search
    asset-list
  • List components

    read
    component-list
  • List links

    read
    link-list
  • Get the space and cache version

    read
    space-get

+9 more actions available

Every app-specific value here is a blank you have to fill in. An app action is reached through the connection that authenticates it, so the address is a connection id, not the app id — and connections belong to your account, so a public page cannot know yours. Create one for Storyblok, then fill in the three blanks: conn_YOUR_CONNECTION_ID, the action key, and the parameters that action declares. The call itself is real — the shape is transcribed from the studio's own snippet builder, which prints the same kind of blanks — but nothing in it is specific to Storyblok. The Workflow tab is where this app's real ids are.

Install
npm install @w6w/sdk
yarn add @w6w/sdk
pnpm add @w6w/sdk
deno add npm:@w6w/sdk
Code
import { W6wClient, isActionRun } from "@w6w/sdk";

// Reads W6W_BASE_URL and W6W_TOKEN from the environment when omitted.
const client = new W6wClient();

const envelope = await client.run({
  urn: "conn_YOUR_CONNECTION_ID",
  action: "datasource-entry-list",
  payload: {
    datasource: "<value>",
    // dimension: "<value>",
    // perPage: "<value>",
    // page: "<value>",
    // cacheVersion: "<value>",
  },
});

if (isActionRun(envelope)) console.log(envelope.value);
Install the CLI
npm install -g @w6w/cli
CLI
w6w run conn_YOUR_CONNECTION_ID --action datasource-entry-list --payload '{"datasource":"<value>"}'

Give an AI agent Storyblok — without giving it Storyblok's credentials. One MCP endpoint exposes every app, function and workflow the caller is entitled to, as tools it can discover and run. Access is granted per team while we onboard.

One tool call
{
  "name": "w6w_invoke",
  "arguments": {
    "ref": "app:io.w6w.storyblok#datasource-entry-list",
    "input": {
      "datasource": "<datasource>"
    }
  }
}

Every tool names its target with a single ref. The app: form above doesn't name a connection at all — the host resolves which of the caller's Storyblok connections to sign with, and refuses rather than guesses when the answer is ambiguous.

What the agent gets

Credentials it can't read

The token is attached host-side, at the moment of the call. It is never a tool argument, never in the model's context, and never in a transcript — so a prompt injection has nothing to exfiltrate.

A tool surface scoped to the caller

Tools are derived per end user from what that person has actually connected and is entitled to — not one shared bot identity carrying the union of everyone's access.

A durable workflow in one call

Multi-step work runs on the workflow engine and returns a run handle the agent can poll — retries, branching and state survive the conversation that started them.

Health-aware discovery

Storyblok's declared health checks are on the surface too, so an agent can tell "the vendor is down" from "your credential expired" before it burns a retry on either.

The MCP surface is part of the hosted platform. Storyblok itself is MIT, and the runtime that executes it is source-available (FSL).

Request MCP access

Health checks

Storyblok declares its own checks, so its health is a property of the app rather than something the host guesses at.

service

Storyblok status

Declared unavailable — measured, Storyblok's status page publishes no machine-readable feed. It would also answer the wrong question: the delivery CDN and the Management API fail independently, and `api` probes whichever this connection uses.

dependency

Storyblok API reachable

Probes whichever API this connection uses — delivery or management — in the region its space lives in. Signed, since Storyblok has no unauthenticated endpoint, so it names the WRONG-REGION case, which returns the same bare `Unauthorized` as a wrong token.