Skip to content

@storyblok/management-api-client

@storyblok/management-api-client is Storyblok’s modern TypeScript client for the Management API (MAPI). It exposes a single createManagementApiClient() factory that returns resource clients for stories, components, component folders, assets, asset folders, datasources, datasource entries, presets, internal tags, spaces, users, and experiments. Request and response shapes are fully typed and can be narrowed end-to-end by passing a project schema type from @storyblok/schema to .withTypes<Schema>().

  • Node.js LTS (version 22.x recommended)
  • Modern web browser (the latest versions of Chrome, Edge, Firefox, and Safari) when used in the browser, for example, inside an app extension

Add the package to a project:

Terminal window
npm install @storyblok/management-api-client

The following example demonstrates how to initialize the client and list stories in a space:

import { createManagementApiClient } from "@storyblok/management-api-client";
const client = createManagementApiClient({
personalAccessToken: process.env.STORYBLOK_PERSONAL_ACCESS_TOKEN!,
spaceId: 12345,
region: "eu",
});
const { data, error } = await client.stories.list();
if (error) throw error;
console.log(data?.stories);

Every resource client method returns an ApiResponse object containing data, error, response, and request. Set throwOnError: true on the client (or per request) to receive data directly and have errors thrown as ClientError instances instead.

The following sections document the createManagementApiClient() factory, each resource client, and the exported error class and types.

Creates a Management API client. Authentication accepts either a personal access token or an OAuth bearer token, never both.

createManagementApiClient(config);

The client accepts the following options (the most common are shown here):

Option Description
personalAccessToken Required unless oauthToken is provided. Personal access token used for authentication.
oauthToken Required unless personalAccessToken is provided. OAuth bearer token used for authentication.
spaceId Required for space-scoped endpoints. Default space ID; can be overridden per request.
region Region code ('eu', 'us', 'ap', 'ca', 'cn'). Defaults to 'eu'.
baseUrl Overrides the regional base URL (useful for testing).
headers Extra request headers merged into every request.
throwOnError Throws ClientError on HTTP errors instead of returning them. Defaults to false.
retry Retry options. Defaults to up to 12 retries on HTTP 429 with exponential backoff.
timeout Request timeout in milliseconds. Defaults to 30_000.
rateLimit Preventive rate limiting. Defaults to six requests per second. false disables it; a number sets requestsPerSecond; or pass { requestsPerSecond }.

Every space-scoped method accepts an optional path: { space_id } override that takes precedence over the client-level spaceId. This allows a single client instance to operate against multiple spaces.

await client.stories.list({ path: { space_id: 67890 } });

Use the stories resource client to read, create, update, publish, duplicate, and version stories.

client.stories.list(options?);
client.stories.get(storyId, options?);
client.stories.create(options);
client.stories.update(storyId, options);
client.stories.delete(storyId, options?);
client.stories.duplicate(storyId, options);
client.stories.publish(storyId, options?);
client.stories.versions(storyId, options?);

storyId is a numeric ID. Pass story content as options.body.story. Use client.stories.versions(storyId) to list the version history of a story.

const { data } = await client.stories.create({
body: {
story: {
name: "Home",
slug: "home",
content: { component: "page", title: "Welcome" },
},
},
});

client.components and client.componentFolders

Section titled “client.components and client.componentFolders”

Use the components and componentFolders resource clients to manage block definitions and the folders they belong to.

client.components.list(options?);
client.components.get(componentId, options?);
client.components.create(options);
client.components.update(componentId, options);
client.components.delete(componentId, options?);
client.components.restore(componentId, options?);
client.components.versions(componentId, options?);
client.components.version(componentId, versionId, options?);
client.components.restoreVersion(componentId, versionId, options?);
client.componentFolders.list(options?);
client.componentFolders.get(componentGroupId, options?);
client.componentFolders.create(options);
client.componentFolders.update(componentGroupId, options);
client.componentFolders.delete(componentGroupId, options?);

Use the assets and assetFolders resource clients to upload, read, update, and organize assets and their folders.

client.assets.list(options?);
client.assets.get(assetId, options?);
client.assets.create(options);
client.assets.upload(options);
client.assets.update(assetId, options);
client.assets.delete(assetId, options?);
client.assets.deleteMany(options);
client.assets.bulkMove(options);
client.assets.bulkRestore(options);
client.assets.convertToShared(assetId, options);
client.assets.signResponseObject(options);
client.assets.finalize(signedResponseObjectId, options?);
client.assetFolders.list(options?);
client.assetFolders.get(assetFolderId, options?);
client.assetFolders.create(options);
client.assetFolders.update(assetFolderId, options);
client.assetFolders.delete(assetFolderId, options?);

client.assets.upload() performs the full upload flow in one call (sign, upload to S3, finalize) and returns the resulting Asset. client.assets.create() wraps upload() and additionally applies metadata such as alt, title, and copyright in a follow-up update. For the underlying multi-step flow, refer to Upload and replace assets in the Management API reference.

const asset = await client.assets.create({
body: {
short_filename: "hero.jpg",
asset_folder_id: 0,
alt: "Hero image",
title: "Homepage hero",
},
file: fileBlob,
});

client.assets.update() accepts an optional file argument: when provided together with a short_filename, the file is replaced first and metadata is updated afterwards in the same call.

Use the lower-level signResponseObject() and finalize() methods only when a custom upload flow is needed (for example, to stream the file from a different process or to integrate with a non-standard storage provider).

deleteMany(), bulkMove(), and bulkRestore() operate on multiple assets in a single request. convertToShared() transfers an asset from the space into the organization’s shared asset library.

The package exports the normalizeAssetUrl() helper for normalizing Storyblok asset URLs across regions.

client.datasources and client.datasourceEntries

Section titled “client.datasources and client.datasourceEntries”

Use the datasources and datasourceEntries resource clients to manage datasources and their entries.

client.datasources.list(options?);
client.datasources.get(datasourceId, options?);
client.datasources.create(options);
client.datasources.update(datasourceId, options);
client.datasources.replace(datasourceId, options);
client.datasources.delete(datasourceId, options?);
client.datasourceEntries.list(options?);
client.datasourceEntries.get(datasourceEntryId, options?);
client.datasourceEntries.create(options);
client.datasourceEntries.update(datasourceEntryId, options);
client.datasourceEntries.replace(datasourceEntryId, options);
client.datasourceEntries.delete(datasourceEntryId, options?);

update() issues a PATCH: only the fields present in the body change. replace() issues a PUT: all updatable fields are replaced.

Use the presets resource client to manage the pre-filled component configurations that editors can choose from in the Visual Editor.

client.presets.list(options?);
client.presets.get(presetId, options?);
client.presets.create(options);
client.presets.update(presetId, options);
client.presets.delete(presetId, options?);

Use the internalTags resource client to manage the internal tags that categorize blocks and assets in the Storyblok UI.

client.internalTags.list(options?);
client.internalTags.create(options);
client.internalTags.update(internalTagId, options);
client.internalTags.delete(internalTagId, options?);

Use the spaces resource client to list and manage spaces.

client.spaces.list(options?);
client.spaces.get(options?);
client.spaces.create(options);
client.spaces.update(options);
client.spaces.delete(options?);

client.spaces.list() returns every space the authenticated user can access. The other methods operate on the space identified by spaceId.

Use the users resource client to read and update the authenticated user.

client.users.me(options?);
client.users.updateMe(options);

me() returns the profile of the authenticated user; updateMe() updates it.

Use the experiments resource client to manage experiments and their lifecycle.

client.experiments.list(options?);
client.experiments.get(experimentId, options?);
client.experiments.create(options);
client.experiments.update(experimentId, options);
client.experiments.delete(experimentId, options?);
client.experiments.activate(experimentId, options?);
client.experiments.pause(experimentId, options?);
client.experiments.complete(experimentId, options?);
client.experiments.completeWithWinner(experimentId, variantId, options?);
client.experiments.selectWinner(experimentId, variantId, options?);
client.experiments.stories.create(experimentId, options);
client.experiments.stories.delete(experimentId, storyId, options?);
client.experiments.storyMappings.create(experimentId, variantId, options);
client.experiments.storyMappings.delete(experimentId, variantId, originalStoryId, options?);
client.experiments.results.get(experimentId, options?);
client.experiments.results.push(experimentId, options);

The top-level methods cover CRUD plus the state transitions (activate, pause, complete, completeWithWinner, selectWinner). The nested stories and storyMappings resource clients attach stories and variant mappings to an experiment, and results reads or pushes experiment results.

client.sharedAssets, client.sharedAssetFolders, and client.sharedInternalTags

Section titled “client.sharedAssets, client.sharedAssetFolders, and client.sharedInternalTags”

Organizations can maintain shared asset libraries: top-level asset folders that several spaces reuse, each with per-space read or write access. Use the sharedAssets, sharedAssetFolders, and sharedInternalTags resource clients to manage them.

client.sharedAssets.list(options?);
client.sharedAssets.get(assetId, options?);
client.sharedAssets.upload(options);
client.sharedAssets.create(options);
client.sharedAssets.delete(assetId, options?);
client.sharedAssetFolders.list(options?);
client.sharedAssetFolders.get(folderId, options?);
client.sharedAssetFolders.create(options);
client.sharedAssetFolders.update(folderId, options);
client.sharedAssetFolders.delete(folderId, options?);
client.sharedInternalTags.list(options);
client.sharedInternalTags.create(options);
client.sharedInternalTags.update(tagId, options);
client.sharedInternalTags.delete(tagId, options);

As with client.assets, sharedAssets.create() runs the full signed-upload flow, while sharedAssets.upload() is the lower-level variant. Each shared asset folder carries per-space access, exposed through the AccessLevel and AssetFolderAccess types.

Escape-hatch HTTP methods and interceptors

Section titled “Escape-hatch HTTP methods and interceptors”

For endpoints the resource clients do not yet cover, the client exposes raw HTTP methods that return the same ApiResponse wrapper:

client.get(path, options?);
client.post(path, options?);
client.put(path, options?);
client.patch(path, options?);
client.delete(path, options?);

The client also exposes client.interceptors, the underlying request and response middleware hooks, for advanced request customization.

Every non-2xx response is wrapped in a ClientError. The error exposes a response property containing status, statusText, and the parsed data from the response body (error.response.status, error.response.statusText, error.response.data). When throwOnError is true, ClientError instances are thrown instead of returned.

throw new ClientError(message, { status, statusText, data });

@storyblok/management-api-client ships complete type definitions and is fully compatible with @storyblok/schema.

Use the client in conjunction with [@storyblok/schema](/docs/libraries/js/schema) to narrow story.content to the project’s component union. The client object exposes a withTypes<T>() method that returns the same instance cast to a version that narrows story content to the components declared in the passed schema. withTypes<T>() accepts either { components: Block } or { blocks: Block }, the latter matches the Schema type produced by @storyblok/schema. It carries no runtime cost and adds no schema values to the bundle.

import { createManagementApiClient } from "@storyblok/management-api-client";
import type { Schema } from "./schema";
const client = createManagementApiClient({
personalAccessToken: "your-token",
spaceId: 12345,
}).withTypes<Schema>();
const { data } = await client.stories.get(42);
// data.story.content is now a discriminated union of the schema's blocks.

Was this page helpful?

What went wrong?

This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window).Terms of Service (opens in a new window) apply.