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 -O
unzip 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
curl
folder. Execute theNavigationMenus_POST_ToSites.sh
script with yoursite ID
as a parameter../NavigationMenus_POST_ToSites.sh 1234
The 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
curl
folder and into thejava
folder. Compile the source files with the following command:javac -classpath .:* *.java
-
Run the
NavigationMenus_POST_ToSites
class with the following command. Replace thesiteId
value 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
POST
request 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
permissions
array 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=permissions
to 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
PUT
request. 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
getSiteNavigationMenusPage
endpoint. 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-menus
Endpoint to fetch navigation menus for site 20126
using the headless delivery API.filter=dateCreated%20le%202025-07-18T14%3A25%3A00Z
URL-encoded. Refines results to include only menus created on or before July 18, 2025, at 14:25 UTC. sort=dateCreated%3Adesc
URL-encoded. Orders results by dateCreated
in 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:desc
This retrieves only navigation menus with
Main
in the name. -
Export filtered navigation menus using the
postSiteNavigationMenusPageExportBatch
endpoint. 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-batch
Endpoint to start an export batch for navigation menus on site 20126
.filter=dateCreated%20ge%202025-07-17T00%3A00%3A00Z
URL-encoded. Limits the export to menus created on or after July 17, 2025, at midnight UTC. search=Main
Filters exported menus to those whose name contains “Main” (case-insensitive). sort=dateModified%3Adesc
URL-encoded. Orders exported menus by dateModified
in 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"