Generic CRUD API

OpenBoxes provides a generic REST endpoint that offers uniform CRUD operations across all domain classes. This is useful for accessing entities that do not have a dedicated REST endpoint, or when you need a consistent interface across different object types.

Endpoint Pattern

GET    /api/generic/{domainClass}            # List all
GET    /api/generic/{domainClass}/{id}        # Get by ID
POST   /api/generic/{domainClass}             # Create
PUT    /api/generic/{domainClass}/{id}        # Update
DELETE /api/generic/{domainClass}/{id}        # Delete

Replace {domainClass} with the camelCase name of the domain object.

Available Domain Classes

The following domain classes are accessible through the generic API:

Domain Class Description Dedicated Endpoint?
product Products in the catalog Yes (/api/products)
inventoryItem Lot/batch records No
shipment Shipment records No
shipmentItem Items within a shipment No
requisition Internal requests/orders No
requisitionItem Items within a requisition No
transaction Inventory transactions No
transactionEntry Line items within a transaction No
category Product categories No
locationType Location type definitions No
locationGroup Location groupings No
person People (users, contacts) No
organization Organizations No

List Objects

Retrieve a paginated list of objects for any domain class:

# List inventory items
curl -b cookies.txt \
  "https://acme.openboxes.cloud/openboxes/api/generic/inventoryItem?max=25&offset=0"

# List product categories
curl -b cookies.txt \
  "https://acme.openboxes.cloud/openboxes/api/generic/category?max=50"

# List shipments
curl -b cookies.txt \
  "https://acme.openboxes.cloud/openboxes/api/generic/shipment?max=10&offset=0"

Query Parameters

Parameter Type Default Description
max integer all Maximum records to return
offset integer 0 Number of records to skip

Get by ID

Retrieve a single object by its ID:

curl -b cookies.txt \
  "https://acme.openboxes.cloud/openboxes/api/generic/inventoryItem/inv-001"

Response

{
  "id": "inv-001",
  "lotNumber": "LOT-2025-001",
  "expirationDate": "2026-06-30T00:00:00Z",
  "product": {
    "id": "prod-001",
    "name": "Ibuprofen 200mg"
  },
  "dateCreated": "2025-01-10T08:00:00Z",
  "lastUpdated": "2025-03-15T12:30:00Z"
}

Create Object

Create a new object of any domain class:

# Create a product category
curl -X POST \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  "https://acme.openboxes.cloud/openboxes/api/generic/category" \
  -d '{
    "name": "Surgical Supplies",
    "description": "Instruments and consumables for surgery"
  }'

# Create an inventory item (lot record)
curl -X POST \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  "https://acme.openboxes.cloud/openboxes/api/generic/inventoryItem" \
  -d '{
    "product": { "id": "prod-001" },
    "lotNumber": "LOT-2025-099",
    "expirationDate": "2027-12-31T00:00:00Z"
  }'

Update Object

Update an existing object by ID:

curl -X PUT \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  "https://acme.openboxes.cloud/openboxes/api/generic/category/cat-005" \
  -d '{
    "name": "Surgical Supplies & Instruments",
    "description": "Updated description"
  }'

Only include the fields you want to change. Omitted fields are not modified.

Delete Object

Delete an object by ID:

curl -X DELETE \
  -b cookies.txt \
  "https://acme.openboxes.cloud/openboxes/api/generic/category/cat-005"

Warning: Deletion fails if the object has dependent records (e.g., deleting a category used by products). Deactivate records instead of deleting when possible.

When to Use Generic vs. Dedicated Endpoints

Use Generic API When... Use Dedicated Endpoints When...
No dedicated endpoint exists (e.g., inventoryItem, category) A specific endpoint exists (e.g., /api/products, /api/stockMovements)
You need uniform CRUD across multiple domain classes You need advanced features like search, filtering, or status transitions
Building a general-purpose integration layer Working with stock movements or purchase orders (complex workflows)

Dedicated endpoints often provide richer query parameters, nested data, and workflow-specific operations (like shipping or receiving) that the generic API does not support.

Response Differences

The generic API returns objects with a flat structure. Associations are returned as references with id and basic fields:

{
  "id": "inv-001",
  "product": {
    "id": "prod-001",
    "name": "Ibuprofen 200mg",
    "class": "org.pih.warehouse.product.Product"
  }
}

The class field indicates the Grails domain class of associated objects.

Pagination Notes

Pagination via max and offset works on most domain classes through the generic API. However, behavior may vary:

  • Some domain classes return all records regardless of max
  • Very large result sets may time out before completing
  • Always test pagination for your specific domain class

Error Handling

{
  "errorCode": 400,
  "errorMessage": "Property [name] of class [class Category] cannot be null"
}

Common errors:

Error Cause
cannot be null Required field was omitted
must be unique A unique constraint was violated
not found Referenced ID does not exist
optimistic locking Object was modified by another request