Content Management APIs

Fragments API Basics

Release Feature

Liferay’s headless-admin-fragment module provides REST services for Fragment Sets and Fragments in a site (Liferay’s unit for grouping pages and content). With these services, you can create, retrieve, update, and delete Fragment Sets and the Fragments they contain. Here, you call these services using cURL commands.

A Fragment Set groups related Fragments together. A Fragment is a single reusable page element that belongs to a Fragment Set. Because a Fragment always belongs to a Fragment Set, you must create the Fragment Set first.

Add a Fragment Set

Start a new Liferay DXP instance by running

docker run -it -m 8g -p 8080:8080 liferay/dxp:2026.q1.9-lts

Sign in to Liferay at http://localhost:8080 using the email address test@liferay.com and the password test. When prompted, change the password to learn.

Then, follow these steps:

  1. Enable the Fragment Management API release feature flag (LPD-39244). Open the Global Menu (Global Menu) and navigate to Control Panel → Instance Settings. Click Feature Flags under Platform, then click Release, and toggle on Fragment Management API. Until you enable this flag, the service calls below fail. Enabling a feature flag requires administrator access to the Liferay instance. See Release Feature Flags.

  2. Download and unzip Fragments API Basics.

    curl https://resources.learn.liferay.com/examples/liferay-s2v4.zip -O
    
    unzip liferay-s2v4.zip
    
  3. Find your site’s External Reference Code (ERC). The Fragment and Fragment Set endpoints address a site by its ERC. Use this value in each of the following service calls.

  4. Navigate to the curl folder, and execute FragmentSets_POST_ToSites.sh using your site’s ERC as a parameter. This creates a new Fragment Set in the specified site.

    cd liferay-s2v4/curl
    
    ./FragmentSets_POST_ToSites.sh [site-erc]
    

    The terminal displays the complete schema for the newly created Fragment Set:

    {
      "creator" : { ... },
      "dateCreated" : "2026-07-01T14:00:00Z",
      "dateModified" : "2026-07-01T14:00:00Z",
      "description" : "This fragment set was created by the headless API.",
      "externalReferenceCode" : "able-fragment-set",
      "key" : "able-fragment-set",
      "marketplace" : false,
      "name" : "Able Fragment Set"
    }
    
  5. Copy the Fragment Set’s ERC. You use it throughout the rest of this tutorial.

Important

The provided code uses basic authentication for demonstration purposes. For production, you should authorize users with OAuth2.

Examine the cURL Command

The FragmentSets_POST_ToSites.sh script calls the REST service with a cURL command.

curl \
	"http://localhost:8080/o/headless-admin-fragment/v1.0/sites/${1}/fragment-sets" \
	--data-raw '
		{
			"description": "This fragment set was created by the headless API.",
			"key": "able-fragment-set",
			"name": "Able Fragment Set"
		}' \
	--header "Content-Type: application/json" \
	--request "POST" \
	--user "test@liferay.com:learn"

The main arguments are

ArgumentDescription
--header "Content-Type: application/json"Declares JSON as the request body format
--request "POST"The HTTP method to invoke at the specified endpoint
"http://localhost:8080/o/headless-admin-fragment/v1.0/sites/${1}/fragment-sets"The REST service endpoint for creating a Fragment Set in the specified site
--data-raw '{...}'The JSON body containing the Fragment Set’s properties
--user "test@liferay.com:learn"Basic authentication credentials

The JSON body in --data-raw includes these parameters:

ParameterDescription
descriptionText description of the Fragment Set
keyUnique key for the Fragment Set, used internally
nameDisplay name of the Fragment Set (required)
Important

See FragmentSetResource for service details.

Get a Fragment Set

Get a specific Fragment Set with the following cURL command. Replace [site-erc] and [fragment-set-erc] with your site’s and Fragment Set’s ERCs.

FragmentSets_GET_ByExternalReferenceCode.sh

Command:

./FragmentSets_GET_ByExternalReferenceCode.sh [site-erc] [fragment-set-erc]

Code:

curl \
	"http://localhost:8080/o/headless-admin-fragment/v1.0/sites/${1}/fragment-sets/${2}" \
	--user "test@liferay.com:learn"

The response matches the schema shown in the previous step, confirming the Fragment Set exists with the details you created.

Add a Fragment

A Fragment Set must exist before you can create a Fragment inside it. Because the Fragment Set is already identified by the URL, the request body omits any reference to it.

Important

Every Fragment create or update requires a type of BasicFragment or FormFragment in the request body, and a Fragment’s type is fixed once it’s created. See Basic and Form Fragments.

  1. Execute Fragments_POST_ToFragmentSets.sh using the site’s ERC and the Fragment Set’s ERC as parameters. This creates a new Fragment inside the specified Fragment Set. The script uses the key charlie-fragment and name “Charlie Fragment.”

    ./Fragments_POST_ToFragmentSets.sh [site-erc] [fragment-set-erc]
    

    The terminal displays the complete schema for the newly created Fragment:

    {
      "cacheable" : ...,
      "creator" : { ... },
      "dateCreated" : "2026-07-01T14:05:00Z",
      "dateModified" : "2026-07-01T14:05:00Z",
      "externalReferenceCode" : "charlie-fragment",
      "fragmentVersions" : [
        {
          "configuration" : { "fieldSets" : [] },
          "css" : "",
          "html" : "<div>Charlie Fragment</div>",
          "js" : "",
          "status" : "Approved"
        }
      ],
      "icon" : "",
      "key" : "charlie-fragment",
      "marketplace" : false,
      "name" : "Charlie Fragment",
      "readOnly" : false,
      "type" : "BasicFragment"
    }
    
    Note

    The response includes thumbnailURLReference only when you request it with the nestedFields query parameter, for example http://localhost:8080/o/headless-admin-fragment/v1.0/sites/[site-erc]/fragments/[fragment-erc]?nestedFields=thumbnailURLReference.

  2. Copy the Fragment’s ERC. You use it with the Fragment services described in Fragment Services.

Examine the cURL Command

The Fragments_POST_ToFragmentSets.sh script calls the REST service with a cURL command.

curl \
	"http://localhost:8080/o/headless-admin-fragment/v1.0/sites/${1}/fragment-sets/${2}/fragments" \
	--data-raw '
		{
			"fragmentVersions": [
				{
					"configuration": {"fieldSets": []},
					"css": "",
					"html": "<div>Charlie Fragment</div>",
					"js": "",
					"status": "Approved"
				}
			],
			"key": "charlie-fragment",
			"name": "Charlie Fragment",
			"type": "BasicFragment"
		}' \
	--header "Content-Type: application/json" \
	--request "POST" \
	--user "test@liferay.com:learn"

This script follows the same argument pattern as FragmentSets_POST_ToSites.sh, but posts to "http://localhost:8080/o/headless-admin-fragment/v1.0/sites/${1}/fragment-sets/${2}/fragments". The site’s ERC and the Fragment Set’s ERC identify where the new Fragment is added. The JSON body includes these parameters:

ParameterDescription
fragmentVersionsAt least one FragmentVersion with a status of Approved or Draft (required). An Approved version’s configuration is a JSON object with a required fieldSets array; use "configuration": {"fieldSets": []} when the Fragment has no configuration fields. A Draft version’s configuration is a string that the API stores without validating. See Fragment Configuration.
keyUnique key for the Fragment within the group. Liferay generates one from name when you omit it
nameDisplay name of the Fragment (required)
typeBasicFragment or FormFragment (required); see Basic and Form Fragments

A Fragment’s html, css, js, and configuration values live in its fragmentVersions entries. A Fragment can carry a Draft version only, an Approved version only, or both at the same time. An update must carry at least one version, and it can’t drop the Approved version of a published Fragment, so resend the existing version in every update.

Fragment Services

The remaining Fragment sample files work like the Fragment Set services, identifying a Fragment by its site’s ERC and its own ERC. Run each one from the liferay-s2v4/curl folder, replacing [site-erc] with your site’s ERC and [fragment-erc] with the Fragment ERC you copied in the previous section.

./Fragments_GET_ByExternalReferenceCode.sh [site-erc] [fragment-erc]
./Fragments_PUT_ByExternalReferenceCode.sh [site-erc] [fragment-erc]
./Fragments_DELETE_ByExternalReferenceCode.sh [site-erc] [fragment-erc]

These calls take the Fragment’s ERC in the second position, not the Fragment Set’s.

FileDescription
Fragments_GET_ByExternalReferenceCode.shReturns details for the specified Fragment.
Fragments_PUT_ByExternalReferenceCode.shUpdates the specified Fragment’s details. Renames it to “Dog Fragment,” resending its existing fragmentVersions entry unchanged, so the Fragment keeps its original “Charlie Fragment” content and only the name changes; the request must include at least one version, or the update fails.
Fragments_DELETE_ByExternalReferenceCode.shDeletes the specified Fragment. A successful deletion returns an empty 204 No Content response.

A few related capabilities exist in the API but aren’t included in the runnable sample project:

  • Listing all Fragment Sets or Fragments in a site (GET /sites/{siteExternalReferenceCode}/fragment-sets and GET /sites/{siteExternalReferenceCode}/fragments).
  • Creating a Fragment directly under a site, with the Fragment Set specified in the request body, instead of nested under a Fragment Set’s URL (POST /sites/{siteExternalReferenceCode}/fragments).
  • Narrowing a site-level Fragment listing with a filter query parameter using OData filter syntax.

The API Explorer lists all FragmentSet and Fragment services and schemas and has an interface to try out each service.

Update a Fragment Set

Update an existing Fragment Set with the following cURL command. Replace [site-erc] and [fragment-set-erc] with your site’s and Fragment Set’s ERCs.

FragmentSets_PUT_ByExternalReferenceCode.sh

Command:

./FragmentSets_PUT_ByExternalReferenceCode.sh [site-erc] [fragment-set-erc]

Code:

curl \
	"http://localhost:8080/o/headless-admin-fragment/v1.0/sites/${1}/fragment-sets/${2}" \
	--data-raw '
		{
			"description": "This fragment set was updated by the headless API.",
			"name": "Baker Fragment Set"
		}' \
	--header "Content-Type: application/json" \
	--request "PUT" \
	--user "test@liferay.com:learn"

The script renames the Fragment Set to “Baker Fragment Set”:

{
  ...
  "name" : "Baker Fragment Set",
  ...
}
Important

When the Fragment Set exists, only dateModified, description, and name are honored; any values sent for dateCreated, externalReferenceCode, key, and marketplace are ignored. When no Fragment Set matches the ERC you pass, this request creates one instead, applying key, marketplace, and dateCreated. Check the ERC carefully, since a typo produces a new Fragment Set rather than updating the one you meant. The Fragment Set’s external reference code always comes from the URL.

Delete a Fragment Set

Delete an existing Fragment Set with the following cURL command. Replace [site-erc] and [fragment-set-erc] with your site’s and Fragment Set’s ERCs.

FragmentSets_DELETE_ByExternalReferenceCode.sh

Command:

./FragmentSets_DELETE_ByExternalReferenceCode.sh [site-erc] [fragment-set-erc]

Code:

curl \
	"http://localhost:8080/o/headless-admin-fragment/v1.0/sites/${1}/fragment-sets/${2}" \
	--request "DELETE" \
	--user "test@liferay.com:learn"

A successful deletion returns an empty 204 No Content response.

Basic and Form Fragments

A Fragment requires a type. The FragmentResource service distinguishes two Fragment subtypes:

  • BasicFragment: a JSON-configured Fragment. This tutorial’s sample cURL scripts create this type.
  • FormFragment: adds a fieldTypes array that lists the input field types the form fragment collects. The available field types are Boolean, CAPTCHA, Date, DateTime, Email, File, FormButton, FriendlyURL, HTML, LocalizationSelect, LongText, Multiselect, Number, PhoneNumber, Relationship, Select, Stepper, and Text.
Important

Every Fragment create or update request sets type to BasicFragment or FormFragment. A Fragment’s type is fixed at creation, so to move a Fragment between the two subtypes, delete it and create it again.

This cURL example illustrates a FormFragment creation payload:

curl \
   "http://localhost:8080/o/headless-admin-fragment/v1.0/sites/[site-erc]/fragment-sets/[fragment-set-erc]/fragments" \
   --data-raw '
      {
         "key": "contact-form-fragment",
         "name": "Contact Form Fragment",
         "type": "FormFragment",
         "fieldTypes": ["Text", "Email", "LongText"],
         "fragmentVersions": [
            {
               "configuration": {"fieldSets": []},
               "css": "",
               "html": "<form>...</form>",
               "js": "",
               "status": "Approved"
            }
         ]
      }' \
   --header "Content-Type: application/json" \
   --request "POST" \
   --user "test@liferay.com:learn"

Fragment Configuration

An Approved version’s configuration defines the fields a page creator uses to customize the Fragment in the page editor. It’s a JSON object whose required fieldSets array holds the Fragment’s field sets.

A Draft version’s configuration is a string rather than an object. The string holds the configuration JSON in the format that Fragment Configuration Types Reference describes. The API stores the string without validating it.

This Approved fragmentVersions entry holds a configuration with one field set. The field set holds a text field and an itemSelector field whose default value references an object entry by its ERC. To use it, add it to the fragmentVersions array of a Fragment create or update request, such as the one Fragments_POST_ToFragmentSets.sh sends:

{
  "configuration" : {
    "fieldSets" : [
      {
        "fields" : [
          {
            "defaultValue" : {
              "value" : "Welcome"
            },
            "label" : "Heading",
            "name" : "heading",
            "type" : "text"
          },
          {
            "defaultValue" : {
              "value" : {
                "itemExternalReference" : {
                  "className" : "com.liferay.object.model.ObjectDefinition#U5B4",
                  "externalReferenceCode" : "spring-sale"
                }
              }
            },
            "label" : "Featured Promotion",
            "name" : "featuredPromotion",
            "type" : "itemSelector"
          }
        ],
        "label" : "Content"
      }
    ]
  },
  "css" : "",
  "html" : "<div>Promotion Banner</div>",
  "js" : "",
  "status" : "Approved"
}

An object entry’s className names its object definition, and the suffix after # differs from one object definition to another. Replace com.liferay.object.model.ObjectDefinition#U5B4 with the className that GET /o/object-admin/v1.0/object-definitions returns for your object definition, and replace spring-sale with your entry’s ERC.

A field set has these properties:

PropertyDescription
configurationRoleThe field set’s role in the page editor’s configuration panel, either advanced or style
customComponentModuleThe import declaration of a React component that the page editor shows in place of the field set’s inputs (see Custom Component Configuration)
fieldsThe field set’s fields (required)
labelThe field set’s label

Each field has these properties:

PropertyDescription
dataTypeThe type Liferay converts the field’s value to when the Fragment renders, one of array, double, int, object, or string
defaultValueThe field’s default value, wrapped in an object as {"value": ...}
descriptionThe field’s description
labelThe field’s label
localizableWhether the field’s value can be localized
nameThe field’s name, containing only letters and digits (required)
typeThe field’s type (required)
typeOptionsOptions specific to the field’s type

The field’s type determines what the value inside defaultValue holds:

typeDefault value
categoryTreeNodeSelectorA category or vocabulary, referenced by className (com.liferay.asset.kernel.model.AssetCategory or com.liferay.asset.kernel.model.AssetVocabulary) and externalReferenceCode
checkboxA boolean
collectionSelectorAn object whose collectionType is Collection, with the collection referenced by its externalReferenceCode; CollectionProvider, with the provider referenced by its className; or RepeatableFieldsCollectionProvider, with the provider referenced by its className and fieldName
colorPaletteAn object with color, or with cssClass and rgbValue
colorPickerA string
itemSelectorAn item, referenced in itemExternalReference by className and externalReferenceCode
lengthA string
navigationMenuSelectorAn object whose navigationMenuType is ContextualMenu, with a required contextualMenuType of Children, ParentAndItsSiblings, or SelfAndSiblings; SiteMenu, with the menu referenced in navigationMenuItemExternalReference by its ERC; or SitePages, with an optional pageSetType and parentSitePageExternalReferenceCode
selectA string equal to the value of one of the field’s validValues. A select field requires this default value
targetCollectionDisplayAn array of the ERCs of the collection display page elements that the field targets
textA string
urlAn object whose urlType is Href, with the URL in href, or SitePage, with the page referenced in sitePageItemExternalReference by its externalReferenceCode
videoSelectorAn object with html and title

A default value that points at a collection provider references it by className. Reference other entities by ERC, never by their numeric ID. An item reference carries className, externalReferenceCode, and optionally a scope whose externalReferenceCode identifies the item’s scope, such as its site. Omit scope when the item is in the Fragment’s own site.

typeOptions carries options specific to the field’s type:

OptionApplies ToDescription
dependencyEvery typeA map keyed by the name of another checkbox, select, or text field in the same field set. Each key’s value is a condition with a type of equal or not-equal and a value. The field appears only when the named field’s value meets that condition.
displayTypecheckboxShows the field as a checkbox or a toggle.
numberOfItemscollectionSelectorThe number of collection items made available to the Fragment, at least 1.
validValuesselectAn array of options, each with a required value and an optional label. A select field requires at least one.
validationtextA rule whose type is email, number, pattern, text, or url, with optional required and errorMessage properties. An email, text, or url validation accepts minLength and maxLength. A number validation accepts min and max. A pattern validation requires regexp.

This shape differs from the configuration JSON that Fragment Configuration Types Reference describes: The API wraps each default value in value, and it references entities by ERC where that format uses IDs. See that reference for what the field types do in the page editor.

Additional Fragment Set Capabilities

A few related capabilities exist alongside the CRUD flow this tutorial demonstrates:

  • Fragments installed from Liferay Marketplace appear in listings with marketplace: true. Their html, css, js, and configuration are withheld on read, so export and import cover the fragments you create.
  • Default (system) fragments are excluded entirely from listing and export/import.
  • Fragment Compositions (fragments grouped and saved together as a single reusable unit) and fragments built with the deprecated Fragments Toolkit are out of scope for this API.
  • Resource files and folders that belong to a Fragment Set, modeled on Documents and Media with support for nested folders, are a related capability not covered by this tutorial’s runnable example.
  • You can export and import Fragment Sets across environments in bulk using Liferay’s Batch Engine. See Batch Engine API Basics: Importing Data and Batch Engine API Basics: Exporting Data.

Handling Errors

Calling the Fragment and Fragment Set services incorrectly returns an error message describing the problem:

SituationError Message
Calling any of these endpoints before enabling the Fragment Management API release feature flagThe request returns an error.
Sending an Approved version’s configuration as a string instead of a JSON objectUnable to map JSON path: fragmentVersions.null.configuration
Creating a Fragment with POST /sites/{siteExternalReferenceCode}/fragments without specifying a Fragment Set in the request bodyA fragment set external reference code is required to create a new fragment.
Creating or updating a Fragment at /sites/{siteExternalReferenceCode}/fragments with a Fragment Set ERC that does not existNo fragment set was found with external reference code {0}.
Updating a Fragment without at least one FragmentVersion in the request bodyAt least one fragment entry version is required.
Setting a published Fragment’s FragmentVersion status back to DraftUnpublishing a fragment entry is not supported.
Updating a Fragment with a type that differs from the one it was created withThe fragment type cannot be changed.
Creating or updating a Fragment Set with a name that isn’t validFragment set name is invalid.
Creating or updating a Fragment with a name that isn’t validFragment name is invalid.
Creating or updating a Fragment with HTML that isn’t validFragment HTML is invalid. The error detail holds the validator’s message.
Creating or updating a Fragment with an Approved configuration that fails a validation check, such as duplicate field names or an invalid dependencyFragment configuration is invalid.
Creating or updating a Fragment with an Approved configuration that doesn’t match the configuration format, such as a field name with characters other than letters and digitsThe request returns an error without a specific message. Check the configuration against the properties in Fragment Configuration.
Setting a navigationMenuSelector field’s default value to a contextual menu without a contextualMenuTypeA contextual menu type is required.
Deleting a Fragment that a page or page template usesThe request returns 409 Conflict with The fragment cannot be deleted because it is required by one or more pages or page templates.
Getting a Fragment whose stored Approved configuration isn’t valid JSON, has no fieldSets, or holds a field of an unknown typeThe request returns 500 Internal Server Error. To repair the Fragment, send a PUT with a valid Approved configuration, which replaces the stored one. Because the GET fails, take the Fragment’s name, type, html, css, and js from your own copy of the Fragment and send them in the same request.