> This page is for API Reference.

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

## Overview

Data assemblies define how content and related data are shaped into structured outputs that experiences consume. They are reusable, publishable definitions for retrieving and shaping data at delivery time.
Data assemblies bind concrete data drawn from data types to an experience or fragment, producing the resolved record set that drives rendering at delivery time.

> **Info**
>
> Data assemblies store instructions, not fetched data.

Each data assembly has a stable shell contract consisting of its parameters, declared resolver intent, and return shape.

A definition declares:

* Typed input `parameters`
* Named `resolvers`
* A `return` mapping

> **Info**
>
> The management API infers `sys.dataType`, the output contract, when you save the definition and includes it in responses. Do not send `sys` in create or update requests.

A `resolver` result and the caller’s inputs form a data tree. Pointer expressions read it as follows:

* `$parameters`/`product` — caller input
* `$resolvers`/`product`/`name` — resolver output.

`return` maps those values into the data assembly’s declared output shape.

Declare assembly input `parameters` as an array, using each item's `id` to identify it. Resolver `parameters` map query variable names to assembly values.

```
{
  "parameters": [
    { "id": "productId", "type": "String", "required": true }
  ],
  "resolvers": {
    "product": {
      "source": "Contentful:GraphQL",
      "query": "query Product($id: ID!) { product(id: $id) { name } }",
      "parameters": { "id": "$parameters/productId" }
    }
  },
  "return": {
    "name": "$resolvers/product/name"
  }
}
```

For a bound product, this returns:
`{ "name": "Standing desk" }`

A `Contentful:DataAssembly` resolver invokes another assembly. It maps parent inputs or prior resolver output into the child’s parameters. The child output becomes the nested resolver result. This enables reusable units such as “resolve author profile” within larger assemblies.

> **Info**
>
> Data assemblies can have hard coded IDs (IDs that are not parameters). However, the entries or assets that are hard-coded in a data assembly will not be displayed in the publish impact warning.

## GraphQL

`Contentful:GraphQL` is the data-fetching resolver. It stores a GraphQL query and maps assembly values to query variables. At delivery, the data resolution service sends that query to the Graph API using the request’s space, environment, token, and locale.

Graph API is the delivery time interface that executes the GraphQL operations derived from assembly definitions. It resolves fields, references and unions according to the schema, allowing the assemblies runtime to rely on GraphQL for link traversal and type correctness rather than implementing those behaviors itself.

At save time of the data asembly, the stored query is validated against a limited version of the Graph API schema to ensure the query is valid.

### Available in the data assembly schema

The following capabilities are available in the data assembly schema:

* Each experience and the content it returns are localized.
* Content types, fields, assets and standard metadata.
* Cursor-based root collection queries, including filtering, ordering and pagination.
* Tag metadata and cross-space resource links to specific, allowed Contentful entries.

### Not available in the data assembly schema

The following capabilities are NOT available in the data assembly schema:

| Features                    | What this means                                                                                                                                                 |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Saved-query locale argument | Each experience and the content it returns are localized. The locale is set by the experience, not saved in the query, so the locale argument is not available. |
| Offset-based collections    | Offset-based entry, asset, nested and resource-link collections are not available. Use cursor-based collection fields instead.                                  |
| Preview                     | The preview argument is not exposed.                                                                                                                            |
| Content source maps         | Content source map support is not exposed.                                                                                                                      |
| Timeline preview            | Timeline preview support is not exposed.                                                                                                                        |
| Reverse links               | The `linkedFrom` field and its linking-collection types are not available.                                                                                      |
| Nested reference filters    | Nested link filter inputs are not available. You cannot use `where` on linked entry fields or nested collections.                                               |
| Taxonomy concepts           | Concept metadata and concept filters are not available. Tag metadata remains available.                                                                         |

## Linking

### Cross-space references

[Cross-space references](https://www.contentful.com/developers/docs/references/content-management-api/cross-space-references/) are supported in data assemblies.

> **Info**
>
> Data assemblies cannot directly link to other spaces as a parameter. Instead, cross-space access is controlled via reference by entries within the same space.

A Contentful entry may link to entries in another space/environment. In Graph API, that value is exposed through a `ResourceLink` wrapper and its node union. Collections use `items`/`node`.

```
relatedContent {
  node {
    ... on BlogPost_blog456_Master {
      title
    }
  }
}
```

Cross-space credentials are request-scoped, not part of a data assembly definition. Data resolution forwards `x-contentful-resource-resolution` unchanged to Graph API, which uses it to authenticate subrequests to linked spaces.

> **Info**
>
> Without valid credentials, the remote link cannot resolve.

## Native external references (NER)

[Native external references (NER)](https://www.contentful.com/developers/docs/references/content-management-api/native-external-references/) are resource fields pointing to provider-owned resource types, for example `MockShop:Product`. Graph API stitches the provider schema into the query schema and exposes the value through the same `ResourceLink`/`node` pattern.

> **Info**
>
> [Custom external references (CER)](/developers/docs/concepts/external-references/) are not currently supported in data assemblies.

```
productReference {
  node {
    ... on Node_MockShop_Product {
      title
      price
    }
  }
}
```

The Native external references feature does not add a new data assembly resolver or parameter type. It is selected through the existing `Contentful:GraphQL` resolver.

> **Info**
>
> External type references used by the SDK are discovery-only and are not persisted as assembly parameters.

## Collection filters

Collection filters allow a data assembly to return only the content that matches a specific condition, for example, showing only "Technology" articles on a Technology homepage, without the application having to fetch every entry and filter client-side. The filter is expressed as a `where` clause on the GraphQL collection query behind the data assembly.

You author the `where` clause by editing the data assembly's GraphQL query through the Content Management API (CMA), then manage the resulting Experience binding in the Contentful web app.

### Example scenario

A news team publishes articles across Technology, Culture, and Climate categories. The Technology homepage should display only Technology articles. A data assembly can do this in its GraphQL resolver, as long as `categorySlug` is available in the live schema's `where` input for that collection:

```
articleCollection(limit: 3, where: { categorySlug: "technology" }) {
  items { title slug }
}
```

Once this is in place, new Technology articles are automatically displayed in the section as editors publish them. No change to the app's filtering logic is needed, and Culture articles are excluded automatically.

### Prerequisites

* **A Content Management API (CMA) token**. It's used to read the live GraphQL schema and to create/update the filtered data assembly and its binding.
* **A Delivery or Preview API token**. It's used to verify that the filtered collection returns the expected content.
* The space ID and environment ID you're working in.

> **Info**
>
> If you're driving this process with an AI agent in a shell environment, provide these as environment variables and reference the variable names (not the raw token values) in your instructions to the agent.

### Recommended workflow

Don't start by writing the filtered query from scratch. Build a working, unfiltered binding first. This gives you (or an AI agent) a known-good query and a confirmed mapping from your content model to the Experience's cards, which makes it much easier to isolate just the filter change.

1. **Create an unfiltered baseline**. In the Contentful web app, on the experience, create a data assembly binding for the collection with no `where` clause. Map the returned fields to the cards and confirm the generic (unfiltered) collection renders correctly.
2. **Create a filtered copy via the API**. Duplicate that data assembly, preserving its output shape and card mappings. Confirm the field you want to filter on, for example, `categorySlug`, is actually supported in the live GraphQL `where` input for that collection, then add the `where` clause to the new copy's resolver query via the CMA.
3. **Swap in the filtered binding**. Use the new, filtered binding set wherever you need the filtered view, for example, the Technology homepage. Preview it with both matching and non-matching content to confirm the filter behaves correctly. Keep the original unfiltered binding around as a reusable starting point for other collections.

Once you have that in place, follow these steps:

#### Step 1: Inspect the live schema

Before writing any `where` clause, confirm the field you want to filter on is actually queryable. Fetch the live GraphQL schema with a CMA credential:

```
GET https://api.contentful.com/spaces/{spaceId}/environments/{environmentId}/graphql_schema
```

Inspect the target collection's `where` input type to see which fields and types it supports. If the field you need isn't there, you won't be able to express the filter this way — look for a supported alternative (e.g. filtering on a different field, or restructuring the content model) rather than forcing an unsupported query.

#### Step 2: Add the `where` clause to the resolver query

A data assembly's configuration includes a `resolvers` block containing the raw GraphQL query string for each resolver. To add a filter, you edit that query string directly.

For example, this unfiltered query against a news article collection:

```graphql
query ($limit: Int!, $order: [NewsArticleOrder]) {
  newsArticleCursorCollection(limit: $limit, order: $order) {
    items {
      __typename
      category { name }
      imageUrl
      publishedAt
      readMinutes
      slug
      summary
      title
    }
  }
}
```

Becomes this, once a `where` argument is added to the collection call:

```graphql
query ($limit: Int!, $order: [NewsArticleOrder]) {
  newsArticleCursorCollection(limit: $limit, order: $order, where: { categorySlug: "technology" }) {
    items {
      __typename
      category { name }
      imageUrl
      publishedAt
      readMinutes
      slug
      summary
      title
    }
  }
}
```

Everything else, the `limit` and `order` parameters, the selected fields, and the return mapping, stays the same. Only the collection call itself changes.

For a filter that should vary at runtime (rather than being hardcoded, like `"technology"` above), define a parameter for the filter value and bind it in the query instead of hardcoding the literal.

#### Step 3: Push the update through the CMA

Data assemblies are updated with a `PUT` request to the `data_assemblies` endpoint, sending the **complete** configuration object (not a partial patch). Key things to note about the request:

* Send the writable fiels at the top-level.
* You must pass the current `X-Contentful-Version` header, matching the data assembly's current `sys.version`. This is the standard optimistic-locking mechanism: get the current version by reading the data assembly first.
* The Data Assembly keeps its existing `id` and any bindings pointing to it; you're only changing its internal query and, optionally, its `name`/`description`.

```bash
curl -X PUT "$API_BASE/data_assemblies/$DA_ID" \
  -H "Authorization: Bearer $CMA_TOKEN" \
  -H "X-Contentful-Version: $CURRENT_VERSION" \
  --json '{
    "metadata": {"tags": []},
    "name": "Technology articles",
    "description": "Filtered news collection",
    "parameters": {},
    "resolvers": {
      "articles": {
        "source": "Contentful:GraphQL",
        "query": "query { newsArticleCursorCollection(limit: 3, where: { categorySlug: \"technology\" }) { items { title slug } } }"
      }
    },
    "return": {
      "items": "$resolvers/articles/newsArticleCursorCollection/items"
    }
  }'
```

> **Info**
>
> Because this is a full-document `PUT`, the safest approach is to `GET` the existing data assembly first, edit only the `query` string (and `name`/`description` if relevant) in the returned JSON, and `PUT` the whole thing back with the version you just read. This avoids accidentally dropping fields you didn't mean to touch.

If you're working against a *copy* of the data assembly rather than updating one in place (the recommended approach), create the new data assembly first, then create a new binding set that references it and carries over the same Experience field mappings.

#### Step 4: Verify

Use a Delivery or Preview API token to execute (or preview) the filtered collection query, and confirm:

* Matching content (e.g. Technology articles) is returned.
* Non-matching content (e.g. Culture articles) is excluded.
* No errors are returned from the query.

In the example scenario, the filtered data assembly correctly returns Technology articles while excluding a Culture article when previewed through the Experience.

### Working with an AI agent

If you're delegating this work to an AI coding agent, you can use the following prompt template that is structured around four steps:

1. Inspect the existing baseline data assembly and binding.
2. Check the live schema and confirm filter intent against supported fields.
3. Create a separate filtered copy through the API (never edit the original in place).
4. Verify the result before reporting back. The agent should have shell access with the CMA and Delivery/Preview tokens available as environment variables (not pasted into the prompt), plus the target space and environment IDs.

#### Reusable AI prompt: create a collection filter

Use these inputs:

* Space `[space ID]`,
* Environment `[environment ID]`,
* Source data assembly or Experience `[link or ID]`,
* Desired filter in plain language `[filter goal]`,
* Examples that should match `[matching entries]`,
* Examples that should not match `[excluded entries]`.

Discover missing internal IDs through the API. Use my configured credentials without displaying their values.

1. **Inspect the baseline**. Read the existing Data Assembly and Experience binding. Identify the collection resolver, query, parameters, output fields, return map, and Experience field mappings. Keep the original generic binding unchanged.

2. **Check the live schema and intent**. Fetch `GET https://api.contentful.com/spaces/{spaceId}/environments/{environmentId}/graphql_schema` with a CMA credential. Inspect the collection field’s where input, including supported fields and types. Propose the exact predicate and explain which example entries it should include and exclude. Check that this matches my stated goal, not just that the query is syntactically valid. If the intent is ambiguous, ask me; if the schema cannot express it, explain the limitation and a supported alternative before creating anything.

3. **Create a separate filtered set through the API**. Copy the working Data Assembly into a new filtered variant. Preserve its output shape, return map, and existing mappings; change the collection query to use the validated where clause. For a runtime filter, define and bind compatible parameters instead of hardcoding the value. Create a new binding set referencing the filtered variant, with the same Experience field mappings. Attach it to the target Experience if one was supplied; otherwise leave it ready to select in the UI.

4. **Verify and report**. Read back the new Data Assembly and binding set. Execute or preview the collection with representative matching and excluded content, and check the returned items and any errors. Give me the new IDs, the final GraphQL query and binding details, the evidence that the filter matches my goal, and any unresolved limitation.

Key guardrails worth enforcing, whether you're doing this by hand or via an agent:

* **Never overwrite the original, unfiltered data assembly**. Always create a filtered variant and a new binding set, so the generic version remains available as a reusable baseline.
* **Validate before creating**. Confirm the field exists in the live schema's `where` input before writing a query against it. Don't assume based on the content model alone.

## Endpoints

The following endpoints are available:

### Draft data assemblies

[List draft data assemblies](/developers/docs/references/experience-management-api/data-assemblies/list-data-assemblies)

[Create a new data assembly](/developers/docs/references/experience-management-api/data-assemblies/create-data-assembly)

### Data assembly

[Get a data assembly by ID](/developers/docs/references/experience-management-api/data-assemblies/get-data-assembly)

[Update a data assembly by ID](/developers/docs/references/experience-management-api/data-assemblies/upsert-data-assembly)

[Delete a data assembly by ID](/developers/docs/references/experience-management-api/data-assemblies/delete-data-assembly)

### Publishing

[Publish a data assembly by ID](/developers/docs/references/experience-management-api/data-assemblies/publish-data-assembly)

[Unpublish a previously published data assembly by ID](/developers/docs/references/experience-management-api/data-assemblies/unpublish-data-assembly)