Fragments API Basics
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:
-
Enable the Fragment Management API release feature flag (LPD-39244). Open the 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. -
Download and unzip Fragments API Basics.
curl https://resources.learn.liferay.com/examples/liferay-s2v4.zip -Ounzip liferay-s2v4.zip -
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.
-
Navigate to the
curlfolder, and executeFragmentSets_POST_ToSites.shusing 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" } -
Copy the Fragment Set’s ERC. You use it throughout the rest of this tutorial.
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
| Argument | Description |
|---|---|
--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:
| Parameter | Description |
|---|---|
description | Text description of the Fragment Set |
key | Unique key for the Fragment Set, used internally |
name | Display name of the Fragment Set (required) |
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.
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.
-
Execute
Fragments_POST_ToFragmentSets.shusing 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 keycharlie-fragmentand 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" }NoteThe response includes
thumbnailURLReferenceonly when you request it with thenestedFieldsquery parameter, for examplehttp://localhost:8080/o/headless-admin-fragment/v1.0/sites/[site-erc]/fragments/[fragment-erc]?nestedFields=thumbnailURLReference. -
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:
| Parameter | Description |
|---|---|
fragmentVersions | At 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. |
key | Unique key for the Fragment within the group. Liferay generates one from name when you omit it |
name | Display name of the Fragment (required) |
type | BasicFragment 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.
| File | Description |
|---|---|
Fragments_GET_ByExternalReferenceCode.sh | Returns details for the specified Fragment. |
Fragments_PUT_ByExternalReferenceCode.sh | Updates 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.sh | Deletes 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-setsandGET /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
filterquery 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",
...
}
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 afieldTypesarray that lists the input field types the form fragment collects. The available field types areBoolean,CAPTCHA,Date,DateTime,Email,File,FormButton,FriendlyURL,HTML,LocalizationSelect,LongText,Multiselect,Number,PhoneNumber,Relationship,Select,Stepper, andText.
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:
| Property | Description |
|---|---|
configurationRole | The field set’s role in the page editor’s configuration panel, either advanced or style |
customComponentModule | The import declaration of a React component that the page editor shows in place of the field set’s inputs (see Custom Component Configuration) |
fields | The field set’s fields (required) |
label | The field set’s label |
Each field has these properties:
| Property | Description |
|---|---|
dataType | The type Liferay converts the field’s value to when the Fragment renders, one of array, double, int, object, or string |
defaultValue | The field’s default value, wrapped in an object as {"value": ...} |
description | The field’s description |
label | The field’s label |
localizable | Whether the field’s value can be localized |
name | The field’s name, containing only letters and digits (required) |
type | The field’s type (required) |
typeOptions | Options specific to the field’s type |
The field’s type determines what the value inside defaultValue holds:
type | Default value |
|---|---|
categoryTreeNodeSelector | A category or vocabulary, referenced by className (com.liferay.asset.kernel.model.AssetCategory or com.liferay.asset.kernel.model.AssetVocabulary) and externalReferenceCode |
checkbox | A boolean |
collectionSelector | An 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 |
colorPalette | An object with color, or with cssClass and rgbValue |
colorPicker | A string |
itemSelector | An item, referenced in itemExternalReference by className and externalReferenceCode |
length | A string |
navigationMenuSelector | An 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 |
select | A string equal to the value of one of the field’s validValues. A select field requires this default value |
targetCollectionDisplay | An array of the ERCs of the collection display page elements that the field targets |
text | A string |
url | An object whose urlType is Href, with the URL in href, or SitePage, with the page referenced in sitePageItemExternalReference by its externalReferenceCode |
videoSelector | An 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:
| Option | Applies To | Description |
|---|---|---|
dependency | Every type | A 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. |
displayType | checkbox | Shows the field as a checkbox or a toggle. |
numberOfItems | collectionSelector | The number of collection items made available to the Fragment, at least 1. |
validValues | select | An array of options, each with a required value and an optional label. A select field requires at least one. |
validation | text | A 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. Theirhtml,css,js, andconfigurationare 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:
| Situation | Error Message |
|---|---|
| Calling any of these endpoints before enabling the Fragment Management API release feature flag | The request returns an error. |
Sending an Approved version’s configuration as a string instead of a JSON object | Unable to map JSON path: fragmentVersions.null.configuration |
Creating a Fragment with POST /sites/{siteExternalReferenceCode}/fragments without specifying a Fragment Set in the request body | A 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 exist | No fragment set was found with external reference code {0}. |
Updating a Fragment without at least one FragmentVersion in the request body | At least one fragment entry version is required. |
Setting a published Fragment’s FragmentVersion status back to Draft | Unpublishing a fragment entry is not supported. |
Updating a Fragment with a type that differs from the one it was created with | The fragment type cannot be changed. |
| Creating or updating a Fragment Set with a name that isn’t valid | Fragment set name is invalid. |
| Creating or updating a Fragment with a name that isn’t valid | Fragment name is invalid. |
| Creating or updating a Fragment with HTML that isn’t valid | Fragment 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 dependency | Fragment 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 digits | The 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 contextualMenuType | A contextual menu type is required. |
| Deleting a Fragment that a page or page template uses | The 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 type | The 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. |