Navigation Menu API Basics
Liferay’s REST APIs provide services for Liferay’s navigation menus. You can create and edit navigation menus with the API. Start by seeing an example of adding a new navigation menu.
Liferay DXP 2025.Q2+ The Navigation Menus API now uses External Reference Codes (ERCs) to reference these elements, enabling consistent identification across instances and supporting batch export/import for improved content management and portability.
Adding a Navigation Menu
Start a new Liferay DXP instance by running
docker run -it -m 8g -p 8080:8080 liferay/dxp:2025.q1.6-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:
-
Download and unzip Categories and Vocabulary API Basics.
curl https://resources.learn.liferay.com/examples/liferay-p7s4.zip -Ounzip liferay-p7s4.zip -
Find your site ID. Use it in different service calls below.
-
Use the cURL script to add a new navigation menu to your Site. On the command line, navigate to the
curlfolder. Execute theNavigationMenus_POST_ToSites.shscript with yoursite IDas a parameter../NavigationMenus_POST_ToSites.sh 1234The JSON response shows a new navigation menu has been added:
{ "creator" : { "additionalName" : "", "contentType" : "UserAccount", "familyName" : "Test", "givenName" : "Test", "id" : 20129, "name" : "Test Test" }, "dateCreated" : "2021-09-09T21:41:31Z", "dateModified" : "2021-09-09T21:41:31Z", "id" : 40131, "name" : "Foo", "navigationMenuItems" : [ ], "siteId" : 20125 } -
Go to the Navigation Menus application by navigating to Administration Menu → Site Builder → Navigation Menus. See that a new navigation menu has been added.

-
The REST service can also be called using the Java client. Navigate out of the
curlfolder and into thejavafolder. Compile the source files with the following command:javac -classpath .:* *.java -
Run the
NavigationMenus_POST_ToSitesclass with the following command. Replace thesiteIdvalue with your site ID:java -classpath .:* -DsiteId=1234 NavigationMenus_POST_ToSites
Examine the cURL Command
The NavigationMenus_POST_ToSites.sh script calls the REST service with a cURL command.
curl \
"http://localhost:8080/o/headless-delivery/v1.0/sites/${1}/navigation-menus" \
--data-binary '
{
"name": "Foo"
}' \
--header "Content-Type: application/json" \
--request "POST" \
--user "test@liferay.com:learn"
Here are the command’s arguments:
| Arguments | Description |
|---|---|
-H "Content-Type: application/json" | Indicates that the request body format is JSON. |
-X POST | The HTTP method to invoke at the specified endpoint |
"http://localhost:8080/o/headless-delivery/v1.0/sites/${1}/navigation-menus" | The REST service endpoint |
-d "{\"name\": \"Foo\"}" | The data you are requesting to post |
-u "test@liferay.com:learn" | Basic authentication credentials |
Basic authentication is used here for demonstration purposes. For production, you should authorize users via OAuth2. See Using OAuth2 to Authorize Users for a sample React application that uses OAuth2.
The other cURL commands use similar JSON arguments.
Examine the Java Class
The NavigationMenus_POST_ToSites.java class adds a navigation menu by calling the navigation menu related service.
public static void main(String[] args) throws Exception {
NavigationMenuResource.Builder builder =
NavigationMenuResource.builder();
NavigationMenuResource navigationMenuResource = builder.authentication(
"test@liferay.com", "learn"
).build();
NavigationMenu navigationMenu =
navigationMenuResource.postSiteNavigationMenu(
Long.valueOf(System.getProperty("siteId")),
new NavigationMenu() {
{
name = "Foo";
}
});
System.out.println(navigationMenu);
}
This class invokes the REST service using only three lines of code:
| Line (abbreviated) | Description |
|---|---|
NavigationMenuResource.Builder builder = ... | Gets a Builder for generating a NavigationMenuResource service instance. |
NavigationMenuResource navigationMenuResource = builder.authentication(...).build(); | Specifies basic authentication and generates a NavigationMenuResource service instance. |
NavigationMenu navigationMenu = navigationMenuResource.postSiteNavigationMenu(...); | Calls the navigationMenuResource.postSiteNavigationMenu method and passes the data to post. |
Note that the project includes the com.liferay.headless.delivery.client.jar file as a dependency. You can find client JAR dependency information for all REST applications in the API explorer in your installation at /o/api.
The main method’s comment demonstrates running the class.
The other example Java classes are similar to this one, but call different NavigationMenuResource methods.
See NavigationMenuResource for service details.
Below are examples of calling other NavigationMenu REST services using cURL and Java.
Get Navigation Menus from Site
You can list a site’s navigation menus by executing the following cURL or Java command. As above, replace 1234 with your site ID.
NavigationMenus_GET_FromSites.sh
Command:
./NavigationMenus_GET_FromSites.sh 1234
Code:
curl \
"http://localhost:8080/o/headless-delivery/v1.0/sites/${1}/navigation-menus" \
--user "test@liferay.com:learn"
NavigationMenus_GET_FromSites.java
Command:
java -classpath .:* -DsiteId=1234 NavigationMenus_GET_FromSites
Code:
public static void main(String[] args) throws Exception {
NavigationMenuResource.Builder builder =
NavigationMenuResource.builder();
NavigationMenuResource navigationMenuResource = builder.authentication(
"test@liferay.com", "learn"
).build();
Page<NavigationMenu> page =
navigationMenuResource.getSiteNavigationMenusPage(
Long.valueOf(System.getProperty("siteId")),
Pagination.of(1, 2));
System.out.println(page);
}
The site’s NavigationMenu objects appear in JSON.
Get a Navigation Menu
Get a specific navigation menu with the following cURL or Java command. Replace 1234 with the navigation menu’s ID.
Use NavigationMenus_GET_FromSites.[java|sh] to get NavigationMenu IDs.
NavigationMenus_GET_ById.sh
Command:
./NavigationMenus_GET_ById.sh 1234
Code:
curl \
"http://localhost:8080/o/headless-delivery/v1.0/navigation-menus/${1}" \
--user "test@liferay.com:learn"
NavigationMenus_GET_ById.java
Command:
java -classpath .:* -DnavigationMenuId=1234 NavigationMenus_GET_ById
Code:
public static void main(String[] args) throws Exception {
NavigationMenuResource.Builder builder =
NavigationMenuResource.builder();
NavigationMenuResource navigationMenuResource = builder.authentication(
"test@liferay.com", "learn"
).build();
System.out.println(
navigationMenuResource.getNavigationMenu(
Long.valueOf(System.getProperty("navigationMenuId"))));
}
The NavigationMenu fields appear in JSON.
Put a Navigation Menu
Do a complete overwrite of an existing navigation menu with the following cURL and Java commands. Note, replace 1234 with your navigation menu’s ID.
NavigationMenus_PUT_ById.sh
Command:
./NavigationMenus_PUT_ById.sh 1234
Code:
curl \
"http://localhost:8080/o/headless-delivery/v1.0/navigation-menus/${1}" \
--data-binary '
{
"name": "Bar"
}' \
--header "Content-Type: application/json" \
--request "PUT" \
--user "test@liferay.com:learn"
NavigationMenus_PUT_ById.java
Command:
java -classpath .:* -DnavigationMenuId=1234 NavigationMenus_PUT_ById
Code:
public static void main(String[] args) throws Exception {
NavigationMenuResource.Builder builder =
NavigationMenuResource.builder();
NavigationMenuResource navigationMenuResource = builder.authentication(
"test@liferay.com", "learn"
).build();
NavigationMenu navigationMenu =
navigationMenuResource.putNavigationMenu(
Long.valueOf(System.getProperty("navigationMenuId")),
new NavigationMenu() {
{
name = "Bar";
}
});
System.out.println(navigationMenu);
}
Delete a Navigation Menu
Delete an existing navigation menu with the following cURL and Java commands. Note, replace 1234 with your navigation menu’s ID.
NavigationMenus_DELETE_ById.sh
Command:
./NavigationMenus_DELETE_ById.sh 1234
Code:
curl \
"http://localhost:8080/o/headless-delivery/v1.0/navigation-menus/${1}" \
--request "DELETE" \
--user "test@liferay.com:learn"
NavigationMenus_DELETE_ById.java
Command
java -classpath .:* -DnavigationMenuId=1234 NavigationMenus_DELETE_ById
Code:
public static void main(String[] args) throws Exception {
NavigationMenuResource.Builder builder =
NavigationMenuResource.builder();
NavigationMenuResource navigationMenuResource = builder.authentication(
"test@liferay.com", "learn"
).build();
navigationMenuResource.deleteNavigationMenu(
Long.valueOf(System.getProperty("navigationMenuId")));
}
The API Explorer lists all NavigationMenu services and schemas and has an interface to try out each service.
Navigation Menu Management via Batch Engine
Liferay DXP 2025.Q3
The Navigation Menus API supports batch export and import for migrating site navigation menus and related entities across Liferay instances. The process includes exporting and importing associated permissions, applying user-defined filters based on creation/modification dates or other attributes, and maintaining creator data during imports.
For details on using the batch engine, see Exporting and Importing Data using the Batch Engine API.
Navigation Menu Permissions
Manage navigation menu permissions using these API endpoints. Replace ${1} with the site ID or navigation menu ID in the examples.
-
Create Navigation Menu with Permissions
Assign custom permissions during creation by sending a
POSTrequest to the navigation menu endpoint. The specified roles must already exist in the system. If a specified role does not exist, the API returns a 404 error.Example:
curl \ "http://localhost:8080/o/headless-delivery/v1.0/sites/${1}/navigation-menus" \ --data-binary ' { "name": "Main Menu", "permissions": [ { "roleName": "Owner", "actionIds": ["VIEW", "UPDATE"] } ] }' \ --header "Content-Type: application/json" \ --request "POST" \ --user "test@liferay.com:learn"The
permissionsarray specifies the roles and their associated permissions for the navigation menu. If you declare no permissions, the menu is created with default permissions assigned to the roles. -
Get Navigation Menu Permissions
By default, permissions are not included in the response. To retrieve them, append
nestedFields=permissionsto the query.Example:
curl \ "http://localhost:8080/o/headless-delivery/v1.0/sites/20126/navigation-menus?nestedFields=permissions" \ --user "test@liferay.com:learn" -
Update Navigation Menu Permissions
Update the permissions of an existing navigation menu using a
PUTrequest. Replace${1}with the navigation menu ID.TipYou can find the navigation menu ID while getting navigation menus from a site.
Example:
curl \ "http://localhost:8080/o/headless-delivery/v1.0/navigation-menus/${1}/permissions" \ --data-binary ' [ { "actionIds": ["VIEW", "UPDATE", "DELETE", "PERMISSIONS"], "roleName": "Owner" } ]' \ --header "Content-Type: application/json" \ --request "PUT" \ --user "test@liferay.com:learn"
Filter Data
You can use filters to refine which navigation menus to fetch or export. Filters work in two contexts:
-
Fetch filtered navigation menus using the
getSiteNavigationMenusPageendpoint. Retrieve only the menus you need using the filter parameter:curl \ "http://localhost:8080/o/headless-delivery/v1.0/sites/20126/navigation-menus?filter=dateCreated%20le%202025-07-18T14%3A25%3A00Z&sort=dateCreated%3Adesc" \ --header "Content-Type: application/json" \ --request "GET" \ --user "test@liferay.com:learn"Here’s how the query parameters work:
Parameter Description http://localhost:8080/o/headless-delivery/v1.0/sites/20126/navigation-menusEndpoint to fetch navigation menus for site 20126using the headless delivery API.filter=dateCreated%20le%202025-07-18T14%3A25%3A00ZURL-encoded. Refines results to include only menus created on or before July 18, 2025, at 14:25 UTC. sort=dateCreated%3AdescURL-encoded. Orders results by dateCreatedin descending order (most recent first).TipAdd a
search=parameter to retrieve specific navigation menus by name. For example,http://localhost:8080/o/headless-delivery/v1.0/sites/20126/navigation-menus?filter=dateCreated%20le%202025-07-18T14%3A25%3A00Z&search=Main&sort=dateModified:descThis retrieves only navigation menus with
Mainin the name. -
Export filtered navigation menus using the
postSiteNavigationMenusPageExportBatchendpoint. Use the same filter syntax to limit which menus are included in the export file:curl \ "http://localhost:8080/o/headless-delivery/v1.0/sites/20126/navigation-menus/export-batch?filter=dateCreated%20ge%202025-07-17T00%3A00%3A00Z&search=Main&sort=dateModified%3Adesc" \ --data-binary '' \ --header "Content-Type: application/json" \ --request "POST" \ --user "test@liferay.com:learn"Here’s how the query parameters work:
Parameter Description http://localhost:8080/o/headless-delivery/v1.0/sites/20126/navigation-menus/export-batchEndpoint to start an export batch for navigation menus on site 20126.filter=dateCreated%20ge%202025-07-17T00%3A00%3A00ZURL-encoded. Limits the export to menus created on or after July 17, 2025, at midnight UTC. search=MainFilters exported menus to those whose name contains “Main” (case-insensitive). sort=dateModified%3AdescURL-encoded. Orders exported menus by dateModifiedin descending order (most recent first).--data-binary ''Empty body because filtering and sorting are controlled entirely via query parameters.
Maintain Creator Data
When importing navigation menus, you can use the importCreatorStrategy and creatorStrategy query parameters to control how creator information is handled.
| Parameter | Description |
|---|---|
creatorStrategy=INSERT | Adds a new user as the creator if the specified creator is missing in the target system. Required when using importCreatorStrategy=KEEP_CREATOR. |
importCreatorStrategy=KEEP_CREATOR | Keeps the original creator from the exported data. The import fails if the creator does not exist unless creatorStrategy=INSERT is also set. |
importCreatorStrategy=OVERWRITE_CREATOR | Replaces the original creator with the importing user. All imported navigation menus are assigned to the user performing the import. Defaults to this if omitted. |
Here’s an example:
curl \
"http://localhost:8080/o/headless-batch-engine/v1.0/import-task/com.liferay.headless.delivery.dto.v1_0.NavigationMenu?siteId=20126&creatorStrategy=INSERT&importCreatorStrategy=KEEP_CREATOR" \
--data-raw '
[
{
"name": "Secondary Menu",
"externalReferenceCode": "secondary-menu-001",
"navigationType": "Primary"
}
]' \
--header "Content-Type: application/json" \
--request "POST" \
--user "test@liferay.com:learn"