> This page is for Experience Orchestration.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://contentful.com/developers/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://contentful.com/developers/docs/_mcp/server.

# Fetch experiences

> Fetch and resolve experiences from the Experience Delivery API with the Experiences SDK.

> **Info**
>
> Experiences SDK is currently available in alpha. APIs are unstable and subject to change.

`fetchExperience` fetches an experience from the Experience Delivery API and resolves it in one call. Every framework adapter exports the same function with the same options. The resolved experience is plain, serializable data, so you can fetch on the server and pass it to the client.

## Fetch by ID

`fetchExperience` takes three arguments: what to fetch, how to fetch it, and how to resolve it.

```ts
import { fetchExperience } from '@contentful/experiences-react';

const experience = await fetchExperience(
  { spaceId: '<space-id>', environmentId: 'master', experienceId, locale: 'en-US' },
  { accessToken: process.env.CDA_TOKEN! },
  { config: experienceConfig, metadata: { slug: experienceId }, debug: false }
);
```

| Argument           | Fields                                                                              | Description                                                                               |
| ------------------ | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Experience options | `spaceId`, `environmentId`, `experienceId`, `locale?`, `parameters?`, `extensions?` | Which experience to fetch.                                                                |
| Client options     | `accessToken`, `previewToken?`, `preview?`, `host?`, or `{ client }`                | How to fetch: credentials and endpoint, or your own client.                               |
| Resolve options    | `config`, `metadata?`, `debug?`                                                     | How to resolve: your configuration, application data for your components, and debug mode. |

`metadata` and `debug` are stored on the resolved experience, so the renderer reads them without being passed them again. `metadata` must be plain serializable data, because it travels with the experience.

Call `fetchExperience` on the server, so your access token stays out of the browser:

* **Next.js**: In a server component or route handler.
* **SvelteKit**: In a `+page.server.ts` `load` function, returning the experience as page data.
* **Angular**: In your Express server, passing the experience to `AngularNodeAppEngine.handle()` as request context. Route resolvers also run in the browser, so they would ship your token to the client.

## Handle errors

`fetchExperience` throws `NotFoundError` when the experience doesn't exist. Route it to your framework's not-found handling:

```ts
import { fetchExperience, NotFoundError } from '@contentful/experiences-react';
import { notFound } from 'next/navigation';

try {
  const experience = await fetchExperience(experienceOptions, clientOptions, resolveOptions);
  // render the experience
} catch (error) {
  if (error instanceof NotFoundError) notFound();
  throw error;
}
```

Other delivery failures throw `ExperienceFetchError`, with the original error, including status and body, as `cause`. An experience with no published nodes isn't a not-found error: it resolves to an experience with an empty `nodes` array.

## Fetch draft content

To fetch unpublished changes from the Content Preview API, configure both tokens and set `preview: true` on the request:

```ts
const experience = await fetchExperience(
  { spaceId, environmentId, experienceId },
  { accessToken: process.env.CDA_TOKEN!, previewToken: process.env.CPA_TOKEN, preview: true },
  { config: experienceConfig }
);
```

`preview: true` without a `previewToken` throws an error. To show unsaved canvas edits as they happen, see [Set up live preview](/developers/docs/experience-orchestration/experiences-sdk/live-preview).

## Pass runtime parameters

An experience can declare parameters, which its bindings read as `$parameters/<id>`. Pass values with `parameters` to resolve the experience with them:

```ts
import { fetchExperience, type ExperienceParameters } from '@contentful/experiences-react';

const parameters = {
  featuredPosts: { limit: 6, order: '-fields.title' },
} satisfies ExperienceParameters;

const experience = await fetchExperience(
  { spaceId, environmentId, experienceId, locale: 'en-US', parameters },
  { accessToken },
  { config: experienceConfig }
);
```

* **Values**: Strings, numbers, or one-level records of strings and numbers.
* **Validation**: The Experience Delivery API validates names, required values, fallbacks, and values. The SDK doesn't need the experience definition before fetching.
* **Ordering**: A collection `order` is one field path, `fields.<apiName>` or `sys.<id>`, optionally prefixed with `-` for descending order, such as `-fields.title` or `sys.createdAt`.

To change the values, fetch the experience again and pass the new experience to your renderer. Each fetch runs `resolveData` again. In a browser fetch, use a token that's safe to expose, or a trusted proxy.

Preview fetches accept the same parameters. The Content Preview API can report missing or invalid values in the experience's `deliveryDiagnostics` instead of failing the request. See [Errors and debugging](/developers/docs/experience-orchestration/experiences-sdk/errors-and-debugging#delivery-diagnostics).

The types `ExperienceParameters`, `ExperienceParameterValue`, `ExperienceParameterRecord`, and `ExperienceParameterOrder` are exported by every adapter and by the Node and Web SDKs.

## Request the content source map

Add `extensions: { sourceMap: {} }` to the experience options to fetch the content source map with the experience:

```ts
const experience = await fetchExperience(
  { spaceId, environmentId, experienceId, extensions: { sourceMap: {} } },
  { accessToken },
  { config: experienceConfig }
);

experience.sourceMap; // ExperienceSourceMap | undefined
```

With a source map, the SDK adds attribution to the experience:

* **`experience.attribution.scopes`**: The experience and the persisted experience fragments in it, each with `entityId`, `entityKind`, and the selected `variantId`.
* **`node.attribution`**: The scopes a node belongs to (`scopes`, outer to inner), the scopes it's a top-level node of (`roots`), and the entries its content is bound to (`entryIds`).

Only nodes with an ID get attribution. Read it in a component through the node payload's `attribution` field.

The source map is large, so it's opt-in. Requesting it also switches the request from `GET` to `POST`, which isn't cacheable by a CDN the way `GET` is.

## Fetch by destination

If your space uses [destinations](/developers/docs/experience-orchestration/channels-and-destinations/overview), fetch the experience routed to a path or a destination node instead of by ID:

```ts
const result = await fetchExperience(
  { spaceId, destinationId, path: '/products/shoes' },
  { accessToken },
  { config: experienceConfig }
);

if ('redirect' in result) {
  // Navigate to result.redirect.path, or fetch again with that path.
} else {
  // result is the resolved experience.
}
```

Use `nodeId` instead of `path` to fetch by destination node. Destination fetches don't take `environmentId` or `locale`, and they're delivery only: `preview: true` throws.

To list every path in a destination, for example to build a sitemap or pre-render routes, use `fetchDestinationSitemap`:

```ts
import { fetchDestinationSitemap } from '@contentful/experiences-react';

const { items, pages } = await fetchDestinationSitemap(
  { spaceId, destinationId },
  { accessToken },
  { limit: 100 }
);
// items: [{ path, experienceId, type }]. Pass pages.next as pageNext to get the next page.
```

## Use your own client

To manage the delivery client yourself, for example to add custom caching or middleware, create one with `createClient` and pass it as `{ client }`:

```ts
import { createClient, fetchExperience } from '@contentful/experiences-react';

const client = createClient({ accessToken: process.env.CDA_TOKEN!, host: 'https://proxy.example.com' });

const experience = await fetchExperience({ spaceId, environmentId, experienceId }, { client }, { config: experienceConfig });
```

A client is bound to one token, so it can't switch between delivery and preview per request. Use the inline `{ accessToken, previewToken, preview }` options for that.

If you call the client directly, resolve its response with `resolveExperience`. `toExperiencePayload` narrows the response type, and `readSourceMap` extracts the source map, if present:

```ts
import { createClient, readSourceMap, resolveExperience, toExperiencePayload } from '@contentful/experiences-react';

const response = await client.experience.get(spaceId, environmentId, experienceId, { locale: 'en-US' });

const experience = await resolveExperience(toExperiencePayload(response), experienceConfig, {
  sourceMap: readSourceMap(response),
});
```

`resolveExperience(payload, config, options?)` is the resolve half of `fetchExperience`. It takes the same `metadata` and `debug` options, plus `sourceMap`.

## Fetch without a framework adapter

To fetch outside a framework adapter, use the Node or Web SDK. Both return the same resolved experience:

* **[`@contentful/experiences-node`](https://github.com/contentful/experiences/tree/main/packages/node)**: For servers. Create one instance per process, then call `forRequest()` for each request, so locale and metadata never leak between concurrent requests.
* **[`@contentful/experiences-web`](https://github.com/contentful/experiences/tree/main/packages/web)**: For the browser. Only one instance can be live at a time.

```ts
import { ContentfulExperiences } from '@contentful/experiences-node';

const experiences = new ContentfulExperiences({
  spaceId,
  environmentId: 'master',
  delivery: { accessToken: process.env.CDA_TOKEN! },
  locale: 'en-US',
});

// For each request:
const experience = await experiences
  .forRequest({ locale })
  .fetchExperience({ experienceId }, { config: experienceConfig });
```

The Node and Web SDKs also send events and personalize experiences. See their READMEs.