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 |