Structured Content

This guide helps you configure an existing Edge Delivery Services project with Document Authoring to use the Structured Content feature, enabling schema-based content editing and JSON data delivery.

Structured Content brings form-based editing to Document Authoring: you define a schema, authors fill in a generated form, and the result is delivered as JSON that your Edge Delivery Services blocks can consume. If you're familiar with JSON Schema or form builders the concepts will feel familiar—but even if you're new to them, the guided editor makes it approachable.

Key Concepts

Before you begin, here are a few important concepts:

  1. Schemas define structure. Every structured content document is based on a schema that defines what fields are available and how they're organized.
  2. The editor has two panels. The Navigation panel shows your content hierarchy as a tree, while the Editor area provides full-width forms for each section.
  3. Changes auto-save. Your content is saved automatically as you type—no save button needed.
  4. Data is delivered as JSON. Published content is automatically available via a JSON endpoint for your blocks to consume.

Overview

Structured Content allows you to:

Prerequisites

Required Access and Permissions

To set up and use Structured Content, you need:

If you don't have the required permissions, contact your Document Authoring organization administrator to request access.

Setup Steps

1. Create or Use an Existing Edge Delivery Services Project

If you don't have an Edge Delivery Services project yet, follow the official tutorial at https://www.aem.live/developer/tutorial

2. Configure Structured Content Storage

Structured content documents are stored in your project's content tree in Document Authoring.

Create a dedicated folder:

  1. Navigate to your project in Document Authoring at https://da.live/#/<ORG>/<SITE>
  2. Create a new folder (e.g., /forms, /products, or any custom name)
  3. This folder will store all your structured content documents

Enable the Structured Content Editor:

You must configure Document Authoring to use the Structured Content editor for documents in your chosen folder. This is done by adding a configuration entry that maps a folder path to the editor URL.

  1. Open the Document Authoring config page: https://da.live/config#/<ORG>/ (requires Organization Administrator role)
  2. Add a new config entry with the following:
key value
editor.path
/<ORG>/<SITE>/<FOLDER_NAME>=https://da.live/form#

Example:

key value
editor.path
/aemsites/myproject/products=https://da.live/form#

This configuration tells Document Authoring: "For any document created in the /aemsites/myproject/products folder, open it with the Structured Content editor instead of the default Document Authoring editor."

Multiple Folder Configuration:

To use the Structured Content editor for multiple folders, add multiple editor.path entries:

key value
editor.path
/aemsites/myproject/products=https://da.live/form#
editor.path
/aemsites/myproject/data=https://da.live/form#

3. Create a Schema

Schemas define the structure of your content and are created using the Schema Editor app.

  1. Open the Schema Editor app: https://da.live/apps/schema
  2. Use the dropdown menu to select "New Schema"
  3. Define your schema following the documented subset (see Working with Schemas below, and the canonical schema-spec.md)
  4. Save the schema

Note: Schemas are stored as documents under /<ORG>/<SITE>/.da/forms/schemas/, but they are only visible and manageable through the Schema Editor app.

4. Create Your First Structured Content Document

  1. Navigate to your structured content folder: https://da.live/#/<ORG>/<SITE>/<FOLDER_NAME>
  2. Click "New" to create a new document
  3. Give your document a name (e.g., morning-muse-light-roast)

5. Select a Schema

When the Structured Content editor loads:

  1. You'll be prompted to select one of your schemas
  2. Choose the appropriate schema for your content type (e.g., "Coffee Product")
  3. The form-based editor loads with fields corresponding to your schema

6. Editing Your Content

The editor makes managing complex data structures straightforward. It has two main areas:

Navigation Panel:

Editor Area:

Changes are saved automatically as you type.

7. Preview and Publish

When you preview or publish your document:

  1. Use the standard Preview or Publish buttons in the editor
  2. You'll be redirected to the delivery URL
  3. The page displays an overview of your structured data
  4. The same URL can be used by blocks to fetch JSON data

8. Consume Data in Blocks

Blocks on your Edge Delivery Services site can fetch structured content as JSON using the delivery endpoint (see Delivery Endpoint).

Walkthrough: a Frescopa coffee catalog

This end-to-end example ties the pieces together using Frescopa, the Edge Delivery Services coffee demo. We'll model a Coffee Product, author one product (Frescopa's "Morning Muse Light Roast"), and fetch it as JSON. The schema exercises most of the supported features—strings with length and pattern constraints, numbers and integers with bounds, enum dropdowns, a boolean, an array of primitives, a nested object, and a reusable $def referenced via $ref.

The schema (paste this into the Schema Editor as a new schema named coffee):

  • schema (json)
{
  "$defs": {
    "Faq": {
      "type": "object",
      "title": "FAQ",
      "required": [
        "question"
      ],
      "properties": {
        "question": {
          "type": "string",
          "title": "Question"
        },
        "answer": {
          "type": "string",
          "title": "Answer"
        }
      }
    }
  },
  "type": "object",
  "title": "Coffee Product",
  "required": [
    "name",
    "slug",
    "price",
    "status"
  ],
  "properties": {
    "name": {
      "type": "string",
      "title": "Product Name",
      "minLength": 2,
      "maxLength": 80
    },
    "slug": {
      "type": "string",
      "title": "Slug",
      "minLength": 3,
      "maxLength": 60,
      "pattern": "^[a-z0-9-]+$"
    },
    "description": {
      "type": "string",
      "title": "Description",
      "maxLength": 400
    },
    "category": {
      "type": "string",
      "title": "Category",
      "enum": [
        "Bagged Coffee",
        "Coffee Pods",
        "Coffee Machines",
        "Bundles",
        "Accessories"
      ],
      "default": "Bagged Coffee"
    },
    "roastLevel": {
      "type": "string",
      "title": "Roast Level",
      "enum": [
        "Light",
        "Medium",
        "Medium-Dark",
        "Dark"
      ],
      "default": "Medium"
    },
    "origin": {
      "type": "string",
      "title": "Origin"
    },
    "price": {
      "type": "number",
      "title": "Price",
      "minimum": 0
    },
    "currency": {
      "type": "string",
      "title": "Currency",
      "enum": [
        "USD",
        "EUR",
        "GBP"
      ],
      "default": "USD"
    },
    "weightGrams": {
      "type": "integer",
      "title": "Bag Weight (g)",
      "minimum": 0
    },
    "status": {
      "type": "string",
      "title": "Status",
      "enum": [
        "Draft",
        "Active",
        "Discontinued"
      ],
      "default": "Draft"
    },
    "inStock": {
      "type": "boolean",
      "title": "In Stock",
      "default": true
    },
    "rating": {
      "type": "number",
      "title": "Average Rating",
      "minimum": 0,
      "maximum": 5
    },
    "tastingNotes": {
      "type": "array",
      "title": "Tasting Notes",
      "minItems": 1,
      "maxItems": 10,
      "items": {
        "type": "string",
        "title": "Note"
      }
    },
    "brewing": {
      "type": "object",
      "title": "Brewing Guide",
      "properties": {
        "method": {
          "type": "string",
          "title": "Recommended Method"
        },
        "ratio": {
          "type": "string",
          "title": "Coffee-to-Water Ratio"
        },
        "temperature": {
          "type": "integer",
          "title": "Water Temperature (°C)",
          "minimum": 0,
          "maximum": 100
        }
      }
    },
    "faqs": {
      "type": "array",
      "title": "FAQs",
      "items": {
        "$ref": "#/$defs/Faq"
      }
    }
  }
}

The data. After selecting the coffee schema for a new document and filling in the form, the saved document delivers the following JSON. (You can also paste this JSON to seed a local instance for testing.)

  • data (json)
{
  "name": "Morning Muse Light Roast",
  "slug": "morning-muse-light-roast",
  "description": "A bright, citrusy light roast with a smooth finish—a perfect start to any day.",
  "category": "Bagged Coffee",
  "roastLevel": "Light",
  "origin": "Yirgacheffe, Ethiopia",
  "price": 16.5,
  "currency": "USD",
  "weightGrams": 340,
  "status": "Active",
  "inStock": true,
  "rating": 4.7,
  "tastingNotes": ["citrus", "floral", "smooth finish"],
  "brewing": { "method": "Pour-over (V60)", "ratio": "1:16", "temperature": 94 },
  "faqs": [
    { "question": "Is this coffee whole bean or ground?", "answer": "It ships as whole bean by default; choose your grind at checkout." },
    { "question": "How fresh is it?", "answer": "Every bag is roasted to order and shipped within 48 hours." }
  ]
}

Fetch it from a block once the document is published:

  • sample (javascript)
const response = await fetch(
  'https://da-sc.adobeaem.workers.dev/live/aemsites/da-frescopa/products/morning-muse-light-roast'
);
const product = await response.json();

Working with Schemas

Schemas are the foundation of structured content—they define what fields exist, what types of data they accept, and how they're organized. Structured Content consumes a documented subset of JSON Schema 2020-12. Anything outside the subset is ignored.

Read the full specification before authoring a schema. The complete, authoritative, and always-current reference lives in the da-sc-sdk repository: schema-spec.md. It defines the exact authoring rules, the supported types, the presentation annotations and validation constraints, how $defs / $ref reuse works, and the empty-value semantics that decide what ends up in the delivered JSON. This guide intentionally does not duplicate it—treat the spec as the single source of truth.

A few orientation points to know before you open the spec:

The walkthrough above shows a schema that exercises these in practice.

Delivery Endpoint

Once your content is published, it becomes available as JSON through a delivery endpoint. This is how your Edge Delivery Services blocks fetch the structured data.

Endpoint Format

https://da-sc.adobeaem.workers.dev/<ENVIRONMENT>/<ORG>/<SITE>/<PATH_TO_DOCUMENT>

Parameters:

Example

curl 'https://da-sc.adobeaem.workers.dev/live/aemsites/da-frescopa/forms/offer'

Note: The delivery endpoint URL may change in the future. We will communicate any changes through official channels.

Accessing protected content

The delivery API supports authenticated access, so it can serve content from protected sites and paths. If your content is protected, the client must first complete the site authentication setup to obtain a token—follow Authentication setup for a site.

Once you have a token, pass it in the authorization header (replace hlx__TOKEN with your token):

GET https://da-sc.adobeaem.workers.dev/<ENVIRONMENT>/<ORG>/<SITE>/<PATH_TO_DOCUMENT>
authorization: token hlx__TOKEN

Listing and querying structured content

The delivery endpoint returns one structured-content document at a time. When a block needs to render a list across many documents (or to filter, sort, or project specific fields without downloading each document one by one), configure a query-index.

The index runs as part of the Edge Delivery Services pipeline and produces a single JSON file at a path you choose, listing one row per included document with whichever properties you extract. The resulting JSON is publicly cacheable, so the same index can be consumed by EDS blocks and by external clients.

Define indices in helix-query.yaml at the root of your project's GitHub repository (see the Indexing reference for the full schema).

Recommended selector pattern (key-anchored): the form editor renders each property key as an element with an id matching that key. Select properties by key name with :has():

  • schema (json)
version: 1
indices:
  products:
    target: /products-index.json
    include:
      - /products/**
    exclude:
      - "**/fragments/**"
      - "**/drafts/**"
      - "**/*.json"
    properties:
      name:
        select: div > div > div:has(#name) > div:last-child
        value: textContent(el)
      price:
        select: div > div > div:has(#price) > div:last-child
        value: textContent(el)
      status:
        select: div > div > div:has(#status) > div:last-child
        value: textContent(el)
      inStock:
        select: div > div > div:has(#inStock) > div:last-child
        value: textContent(el)

This produces an index at https://main--<REPO>--<OWNER>.aem.live/products-index.json with name, price, status, and inStock extracted for every product document under /products/.

Page metadata & indexing (robots, sitemap)

When a structured content page is published it behaves like any other Edge Delivery Services page: it can be crawled, it appears in query-index.json, and it can be listed in sitemap.xml. Today the form editor view does not expose a way to set page-level metadata (for example robots: noindex, nofollow, a canonical URL, or a custom title/description). If you need to keep certain structured pages out of search results or out of the sitemap, use one of the workarounds below.

Why there's no field for it: page metadata is a property of the page, not of your schema's data. In fact the keys metadata and section-metadata are reserved in schemas (see the schema specification) precisely so your data never collides with the page's metadata block.

Workaround 1 — Metadata block (per page, via Edit mode)

Open the document in the standard Document Authoring editor (Edit mode) rather than the form editor, and add a Metadata block—a two-column table whose first row is the word metadata. Each subsequent row is a key/value pair:

metadata
robots noindex, nofollow
title Morning Muse Light Roast
description A bright, citrusy light roast with a smooth finish.

On publish this renders as <meta name="robots" content="noindex, nofollow"> (and the corresponding title/description tags) in the page <head>.

Workaround 2 — Bulk Metadata sheet (many pages by path)

To apply metadata to many structured pages at once, create or edit a Bulk Metadata spreadsheet (commonly /metadata) with a URL column that matches a path pattern. For example, to keep an entire folder out of search engines:

URL robots
/products/drafts/** noindex, nofollow

See the Bulk Metadata reference for the sheet format and matching rules.

Keeping pages out of the sitemap and query-index

Limitations

Maximum Depth Level

Using $ref / $defs can produce recursive structures. To prevent infinite loops the current implementation limits expansion to 10 levels. This value is experimental and may change. Prefer flattening deeply nested structures.

For the full set of supported and unsupported constructs, see schema-spec.md. If you need something it doesn't cover, please reach out to discuss your use case.

Under the hood: da-sc-sdk

The schema validation, the JSON↔HTML conversion, and the editing engine are provided by da-sc-sdk—a headless, pure-ESM SDK with no DOM or I/O dependencies. The form editor and the delivery API build on it.

Useful exports for developers building their own tooling:

Reminder: da-sc-sdk is an early version. Treat schema-spec.md as the source of truth for what a schema may contain.

Troubleshooting

Structured Content editor not loading

Schema not appearing in Schema Editor app

Data not fetching from delivery endpoint

A field's value is missing from the delivered JSON