Custom Object APIs

Object API Basics

When you publish an object, Liferay generates REST APIs for it automatically. These APIs differ for Company and Site scoped objects, but they all use the c/[pluralobjectlabel] naming pattern (e.g., c/timeoffrequests). Use these APIs to create, access, update, and remove object entries.

Perform basic CRUD operations for a custom object using cURL commands. Before proceeding, set up a Liferay instance and prepare the sample code.

Tip

For a complete list of APIs generated for Site and Company objects, see Object’s Headless Framework Integration. View and test custom object APIs via the Liferay API Explorer at [server]:[port]/o/api (e.g., localhost:8080/o/api). They appear under REST Applications.

Setting Up a Liferay Instance

Start a new Liferay instance by running

docker run -it -m 8g -p 8080:8080 liferay/portal:7.4.3.132-ga132

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

Then, follow these steps to create a basic object for this tutorial:

  1. Click Global Menu (Global Menu) → Control PanelObjects.

  2. Click New and enter these values:

    FieldValue
    LabelAble
    Plural LabelAbles
    NameAble
  3. Select the new Object draft, go to the Fields tab, and add a single text field:

    LabelField NameTypeRequired
    NamenameText
  4. Go to the Details tab and click Publish.

    Important

    For this tutorial, you must use the above values.

Publishing an object creates and activates a new application for receiving and storing data. You can now access it via headless APIs.

Preparing the Sample Code

Run the following commands to download and unzip the provided sample code:

curl https://resources.learn.liferay.com/examples/liferay-v1s4.zip -O
unzip liferay-v1s4.zip

These scripts include the following APIs:

HTTP MethodHTTP EndpointDescription
GET/Returns a complete list of object entries in a Liferay instance; results can be paginated, filtered, searched, and sorted
POST/Creates a new object entry using the details provided in the API call
DELETE/{objectNameId}Deletes the specified object entry and returns a 204 if the operation succeeds
GET/{objectNameId}Returns details for the specified object entry
PUT/{objectNameId}Replaces the specified object entry’s details with those provided in the API call
GET/{objectNameId}/permissionsReturns details for the specified object entry permissions
PUT/{objectNameId}/permissionsReplaces the specified Object entry permission details with those provided in the API call

Calling the Custom Object’s APIs

  1. After downloading the sample code, navigate to the curl folder in the liferay-v1s4 project.

    cd liferay-v1s4/curl
    
  2. Execute Ables_POST_ToCompany. This creates three entries.

    ./Ables_POST_ToCompany.sh
    

    The terminal displays the complete schema for the newly created entries. Copy the first entry’s ID for use with the following methods.

    {
      "id" : 41969,
      ...
      "name" : "Able 1"
    }
    
    {
      "id" : 41971,
      ...
      "name" : "Able 2"
    }
    
    {
      "id" : 41973,
      ...
      "name" : "Able 3"
    }
    
  3. Execute Ables_GET_FromCompany. This returns a list of the Object’s entries.

    ./Ables_GET_FromCompany.sh
    
  4. Execute Ables_PUT_ById with the first entry’s ID as a parameter. This replaces the details of the specified entry with the details provided in the API call.

    ./Ables_PUT_ById.sh {entry-id}
    
    {
      "id" : 41969,
      ...
      "name" : "Able One"
    }
    
  5. Execute Ables_DELETE_ById with the same ID as its parameter. This deletes the specified entry.

    ./Ables_DELETE_ById.sh {entry-id}
    
  6. Execute Ables_GET_ById with the same ID as its parameter. This returns the details for the specified entry if it exists.

    ./Ables_GET_ById.sh {entry-id}
    

    Since you deleted the entry in the preceding step, it returns the following message.

    {
    	"status": "NOT_FOUND",
    	"title": "No ObjectEntry exists with the primary key 41969"
    }
    

Examining the Sample cURL Scripts

The following are examples of the tutorial’s cURL commands.

POST Ables to the Instance

curl \
	"http://localhost:8080/o/c/ables/" \
	--data-raw '
		{
			"name": "Able 1"
		}' \
	--header "Content-Type: application/json" \
	--request "POST" \
	--user "test@liferay.com:learn"

curl \
	"http://localhost:8080/o/c/ables/" \
	--data-raw '
		{
			"name": "Able 2"
		}' \
	--header "Content-Type: application/json" \
	--request "POST" \
	--user "test@liferay.com:learn"

curl \
	"http://localhost:8080/o/c/ables/" \
	--data-raw '
		{
			"name": "Able 3"
		}' \
	--header "Content-Type: application/json" \
	--request "POST" \
	--user "test@liferay.com:learn"

PUT Ables by Id

curl \
	"http://localhost:8080/o/c/ables/${1}" \
	--data-raw '
		{
			"name": "Able One"
		}' \
	--header "Content-Type: application/json" \
	--request "PUT" \
	--user "test@liferay.com:learn"

Managing Object Permissions

Use the object APIs to read and update permissions for object entries. See Objects Application Permissions for more information.

  1. Execute Ables_GET_Permissions to retrieve a list of object entry permissions.

    ./Ables_GET_Permissions.sh {entry-id}
    

    This example returns permissions for all roles. To retrieve permissions for a specific role, use the roleNames query parameter.

    curl \
       "http://localhost:8080/o/c/ables/${1}/permissions?roleNames=Site%20Member" \
       --user "test@liferay.com:learn"
    

    In this example, the only role is Owner, and its permissions are: Delete, Permissions, Update, and View.

    {
       "actions": {
          "get": {
             "method": "GET",
             "href": "http://localhost:8080/o/c/ables/41969/permissions"
          },
          "replace": {
             "method": "PUT",
             "href": "http://localhost:8080/o/c/ables/41969/permissions"
          }
       },
       "facets": [],
       "items": [
          {
             "actionIds": [
             "DELETE",
             "PERMISSIONS",
             "UPDATE",
             "VIEW"
             ],
             "roleName": "Owner"
          }
       ],
       "lastPage": 1,
       "page": 1,
       "pageSize": 1,
       "totalCount": 1
    }
    
  2. Execute Ables_PUT_Permissions with the entry ID as a parameter to replace the entry’s details with those provided in the API call.

    ./Ables_PUT_Permissions.sh {entry-id}
    

    The Update permission was removed from the Owner role.

    {
       "actions": {
          "get": {
             "method": "GET",
             "href": "http://localhost:8080/o/c/ables/41969/permissions"
          },
          "replace": {
             "method": "PUT",
             "href": "http://localhost:8080/o/c/ables/41969/permissions"
          }
       },
       "facets": [],
       "items": [
          {
             "actionIds": [
             "DELETE",
             "PERMISSIONS",
             "VIEW"
             ],
             "roleName": "Owner"
          }
       ],
       "lastPage": 1,
       "page": 1,
       "pageSize": 1,
       "totalCount": 1
    }
    

Managing Tags and Categories

Use Object APIs to read, set, and update tags and categories for object entries that have categorization enabled. See Tags and Categories for more information.

Tags are represented by the keywords property. Set or update tags by adding a keywords array to the body of any POST, PUT, or PATCH request.

"keywords" : [
   "tag1", "tag2", "tag3"
]

After making a GET request for an object entry, you can read its tags in the same keywords array.

Set and update categories for an object entry by adding the taxonomyCategoryIds array to any POST, PUT, or PATCH request.

"taxonomyCategoryIds" : [
   1234, 5678
]
Note

You must have an existing vocabulary of categories to assign categories to an object entry. See Defining Categories and Vocabularies for Content for more information.

After making a GET request for an object entry, read its categories in the taxonomyCategoryBriefs array, which contains the taxonomyCategoryId and taxonomyCategoryName for each assigned category.

Note

The entryReference nested field behavior requires Liferay DXP 2026.Q1+. Exporting object JSON in earlier versions omits these values, and importing it has no effect.

The entryReference nested field also contains the external reference codes for the associated object entries and their scope (site, asset library, or Liferay instance). When the same JSON is imported, it associates these categories with the imported object entries automatically (and creates matching empty categories if they don’t exist).

"taxonomyCategoryBriefs": [
   {
      "taxonomyCategoryId": 1234,
      "taxonomyCategoryName": "Category A",
      "entryReference":
      {
         "externalReferenceCode": "entryerc-1234-5678-1234-123456789012",
         "scope": {
            "externalReferenceCode": "siteerc1-1234-5678-1234-123456789012",
            "type:" "Site"
         }
      }
   },
   {
      "taxonomyCategoryId": 5678,
      "taxonomyCategoryName": "Category B",
      "entryReference":
      {
         "externalReferenceCode": "entryerc-5678-1234-5678-678901234567",
         "scope": {
            "externalReferenceCode": "siteerc2-5678-1234-5678-678901234567",
            "type": "Site"
         }
      }
   }
]

Filter object entries by keywords and taxonomyCategoryIds following the rules described in API Query Parameters. Example filter strings may look like these:

  • keywords/any(k:k in ('tag1','tag2')) retrieves all object entries tagged with tag1 or tag2.

  • taxonomyCategoryIds/any(k:k in (1234,5678)) retrieves all entries linked to the category with ID 1234 or 5678.

Sort object entries by fields from related objects using the syntax described in API Query Parameters. Example sorting strings may look like these:

  • sort=relatedRelationship/fieldName:asc sorts the entries based on a field from a directly related object in ascending order.

  • sort=relatedRelationship1/relatedRelationship1/fieldName:desc sorts the entries based on a field from a nested related object in descending order.

Managing Comments

Liferay DXP 2026.Q2

If comments are enabled in the object definition, you can manage object entry comments via headless APIs. Follow these steps to enable comments:

Important

These endpoints are available for site-scoped object definitions only. If you created the Able object earlier in this article, it is company-scoped — create a new site-scoped object definition, add at least one entry to it, and note the entry’s external reference code before continuing.

  1. Click Global Menu (Global Menu) → Control PanelObjects.

  2. Select an object definition.

  3. In the Details tab, toggle Enable Comments.

    Switch the Enable Comments toggle to active.

  4. Click Save or Publish.

  5. Open the Liferay API Explorer at [server]:[port]/o/api (e.g., localhost:8080/o/api).

  6. Click REST Applications and select your object definition.

  7. Expand the Comment section to see the available endpoints.

Once comments are enabled, use the object’s headless endpoints to perform GET, POST, PUT, and DELETE operations on entry comments using external reference codes (ERCs). The Comment resource also exposes child-comment endpoints for managing threaded replies to a comment.

View the GET, POST, PUT, and DELETE operations for object comments in the API Explorer.

Using the nestedFields Parameter to Retrieve Additional Details

Some information is embedded in the object’s API response as a nested field.

Parameter ValueDescription
nestedFields=[fieldName].fileBase64Return the Base64 encoding of an attachment field.
nestedFields=[fieldName].folderReturn the folder where the Base64 encoded attachment is stored.
nestedFields=auditEventsWhen entry history is enabled, return the audit events for the entry.
nestedFields=[firstObjectRelationship],[secondObjectRelationship]Return related object entries.
nestedFields=profileURLReturn the profile URL of the entry creator user.
nestedFields=permissionsReturn the roles and permissions for an entry (available in Liferay DXP 2024.Q4+/Portal GA129+).
nestedFields=commentsReturn the entry comments (available for site-scoped object definitions only).

For example, calling the endpoint http://localhost:8080/o/c/ables/41969?nestedFields=permissions returns the permissions element in the API response:

"permissions" : [ {
    "actionIds" : [ "DELETE", "PERMISSIONS", "UPDATE", "VIEW" ],
    "roleName" : "Owner"
  } ],