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/v1/generic/{domainClass}            # List all
GET    /api/v1/generic/{domainClass}/{id}        # Get by ID
POST   /api/v1/generic/{domainClass}             # Create
PUT    /api/v1/generic/{domainClass}/{id}        # Update
DELETE /api/v1/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/v1/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 records (contacts); user accounts are not accessible through this API No
organization Organizations No

Security-related objects — user accounts, roles, location roles, documents and document types — are not accessible through the Generic API on the hosted platform, and person is read-only there because user accounts are person records; manage them in the OpenBoxes interface instead.

List Objects#

Retrieve a paginated list of objects for any of the domain classes listed above:

# List inventory items
curl -H "X-API-Key: $OB_API_KEY" \
  "https://acme.openboxes.cloud/api/v1/generic/inventoryItem?max=25&offset=0"

# List product categories
curl -H "X-API-Key: $OB_API_KEY" \
  "https://acme.openboxes.cloud/api/v1/generic/category?max=50"

# List shipments
curl -H "X-API-Key: $OB_API_KEY" \
  "https://acme.openboxes.cloud/api/v1/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 -H "X-API-Key: $OB_API_KEY" \
  "https://acme.openboxes.cloud/api/v1/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 of the domain classes listed above:

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

# Create an inventory item (lot record)
curl -X POST \
  -H "X-API-Key: $OB_API_KEY" \
  -H "Content-Type: application/json" \
  "https://acme.openboxes.cloud/api/v1/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 \
  -H "X-API-Key: $OB_API_KEY" \
  -H "Content-Type: application/json" \
  "https://acme.openboxes.cloud/api/v1/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 \
  -H "X-API-Key: $OB_API_KEY" \
  "https://acme.openboxes.cloud/api/v1/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/v1/products, /api/v1/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