---
title: @storyblok/management-api-client
description: @storyblok/management-api-client is Storyblok's modern TypeScript client for the Management API, with built-in retry logic, rate limiting, and typed payloads.
url: https://www.storyblok.com/docs/libraries/js/management-api-client
---

# @storyblok/management-api-client

[@storyblok/management-api-client](https://github.com/storyblok/monoblok/tree/main/packages/mapi-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`](/docs/libraries/js/schema) to `.withTypes<Schema>()`.

## Requirements

-   **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

## Installation

Add the package to a project:

```bash
npm install @storyblok/management-api-client
```

## Usage

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

```ts
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.

## API

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

### `createManagementApiClient()`

Creates a Management API client. Authentication accepts either a [personal access token](/docs/api/management/access-tokens) or an OAuth bearer token, never both.

```ts
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 }`. |

### Per-request space override

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.

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

### `client.stories`

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

```ts
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.

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

### `client.components` and `client.componentFolders`

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

```ts
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?);
```

> [!TIP]
> Block schemas authored with [`@storyblok/schema`](/docs/libraries/js/schema)’s `defineBlock()` and `defineFolder()` produce payloads accepted by `create()` and `update()`.

### `client.assets` and `client.assetFolders`

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

```ts
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](/docs/api/management/assets/upload-and-replace-assets) in the Management API reference.

```ts
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`

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

```ts
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.

### `client.presets`

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

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

### `client.internalTags`

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

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

### `client.spaces`

Use the `spaces` resource client to list and manage spaces.

```ts
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`.

### `client.users`

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

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

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

### `client.experiments`

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

```ts
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`

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.

```ts
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

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

```ts
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.

### `ClientError`

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.

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

## TypeScript

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

### Typed story content

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.

```ts
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.
```

## Pagination

-   [Previous: Content Delivery API Client](/docs/libraries/js/content-delivery-api-client)
-   [Next: JavaScript SDK](/docs/libraries/js/js-sdk)
