Inward Item Request API - hmislk/hmis GitHub Wiki
Inward Item Request API
Overview
The Inward Item Request API (/api/itemrequests) lets an external system — such as a patient mobile app or a ward request terminal — submit service requests (e.g. ward services, investigations) and stock item requests (e.g. medicines, consumables) against an admitted patient's BHT.
Requests are saved as Pending — the patient is not charged and no stock moves. Each request is routed to a target department's in-app queue, where a privileged department user processes each line by billing it through the normal Add Services/Investigations page (services) or Direct Issue to BHTs page (stock items), or rejects still-pending lines with a reason. Status is tracked per line, so a request can be fulfilled gradually across more than one visit, and can end up partially fulfilled and partially rejected.
Important: Processing and rejection happen only inside HMIS — there is no API for them. The external system polls
GET /api/itemrequests/{id}to learn the outcome.
For the in-app side of the workflow, see Inward Service and Item Requests (Approval Queue).
Authentication
All endpoints require an API key in the Finance HTTP header:
Finance: <your-api-key>
API keys are issued in the HMIS Admin panel under API Key Management. The key must be active (not retired), activated, and not expired. Missing or invalid keys return 401.
Response Envelope
{"status": "success", "code": 200, "data": { ... }}
{"status": "error", "code": 400, "message": "..."}
Endpoints
1. Submit a Request
POST /api/itemrequests
Request body:
{
"bhtNo": "RH/2026/001234",
"targetDepartmentId": 42,
"comments": "Patient requested via mobile app",
"lines": [
{"itemId": 1001, "qty": 1},
{"itemId": 2002, "qty": 2}
]
}
A single request may mix service lines and stock item lines. Validation rules:
bhtNomust be an active admission (not discharged, final bill not settled)targetDepartmentIdmust be an active department- At least one line; every
itemIdmust be an active item; everyqtymust be greater than 0
Response 200 — the request in PENDING state:
{
"status": "success",
"code": 200,
"data": {
"id": 98765,
"requestNo": "Inward//26/000123",
"bhtNo": "RH/2026/001234",
"targetDepartmentId": 42,
"targetDepartmentName": "Ward",
"status": "PENDING",
"comments": "Patient requested via mobile app",
"createdAt": "2026-07-02 09:15:00",
"createdBy": "api-user",
"fulfillingBillIds": [],
"lines": [
{"billItemId": 111, "itemId": 1001, "itemName": "Oxygen Charges", "itemType": "SERVICE", "qty": 1.0, "netValue": 0.0, "status": "PENDING"},
{"billItemId": 112, "itemId": 2002, "itemName": "Panadol 500mg tab", "itemType": "INVENTORY", "qty": 2.0, "netValue": 0.0, "status": "PENDING"}
]
}
}
2. Poll a Request's Status
GET /api/itemrequests/{id}
Returns the same structure as above. status on the request reflects the mix of its lines — PENDING, PARTIALLY_FULFILLED, FULFILLED, REJECTED, PARTIALLY_FULFILLED_AND_REJECTED, or CANCELLED. fulfillingBillIds lists every distinct real bill id that has fulfilled at least one line so far. Each line in lines carries its own status (PENDING / FULFILLED / REJECTED), and once fulfilled or rejected also carries fulfillingBillId + fulfillingBillType, or rejectionReason respectively. Unknown ids return 404.
Example of a partially processed request (one line billed, one still pending):
{
"status": "success",
"code": 200,
"data": {
"id": 98765,
"requestNo": "Inward//26/000123",
"status": "PARTIALLY_FULFILLED",
"fulfillingBillIds": [55321],
"lines": [
{"billItemId": 111, "itemId": 1001, "itemName": "Oxygen Charges", "itemType": "SERVICE",
"qty": 1.0, "netValue": 500.0, "status": "FULFILLED",
"fulfillingBillId": 55321, "fulfillingBillType": "InwardBill"},
{"billItemId": 112, "itemId": 2002, "itemName": "Panadol 500mg tab", "itemType": "INVENTORY",
"qty": 2.0, "netValue": 0.0, "status": "PENDING"}
]
}
}
3. List / Search Requests
GET /api/itemrequests?targetDepartmentId=&status=&fromDate=&toDate=&limit=
All query parameters are optional:
| Parameter | Format | Meaning |
|---|---|---|
targetDepartmentId |
number | Only requests for this department |
status |
PENDING | PARTIALLY_FULFILLED | FULFILLED | REJECTED | PARTIALLY_FULFILLED_AND_REJECTED | CANCELLED |
Filter by the request's overall derived status |
fromDate / toDate |
yyyy-MM-dd |
Filter by submission date |
limit |
number | Maximum rows returned |
Returns a list of lightweight rows: id, requestNo, bhtNo, targetDepartmentName, status, createdAt.
4. Cancel a Pending Request
PUT /api/itemrequests/{id}/cancel
Request body:
{"reason": "Duplicate submission"}
Withdraws the request while it is still PENDING (no line has been fulfilled or rejected yet). Returns the updated request with status: "CANCELLED". If any line has already been fulfilled or rejected, the call fails — cancel a request only before anyone has acted on it.
Status Lifecycle
Status is derived from the mix of line statuses, not stored as a single flag:
PENDING --(this API, cancel)--------------------------------> CANCELLED
PENDING --(department processes some lines)-----------------> PARTIALLY_FULFILLED
PENDING --(department processes all lines)-------------------> FULFILLED
PENDING --(department rejects all remaining lines)-----------> REJECTED
PARTIALLY_FULFILLED --(department rejects the rest)----------> PARTIALLY_FULFILLED_AND_REJECTED
- PENDING — no line has been fulfilled or rejected yet.
- PARTIALLY_FULFILLED — at least one line fulfilled, at least one still pending.
- FULFILLED — every line fulfilled (no pending or rejected lines remain).
- REJECTED — every line rejected, none fulfilled.
- PARTIALLY_FULFILLED_AND_REJECTED — every line is either fulfilled or rejected, with at least one of each, and none still pending.
- CANCELLED — withdrawn by the requesting system before any line was acted on; no charge, no stock change.
A line becomes FULFILLED the moment a department user bills or issues it through the normal Add Services / Direct Issue pages — the BHT is charged (and, for stock items, department stock deducted from the earliest-expiry non-expired batch at the batch's retail sale rate) at that point, not before. A line becomes REJECTED when a department user rejects the request's remaining lines with a reason. Fulfillment and rejection happen per line, so a single request commonly ends up PARTIALLY_FULFILLED for a while before becoming fully resolved.
Error Codes
| Code | Meaning |
|---|---|
| 400 | Validation error — unknown/retired item, quantity ≤ 0, invalid/discharged/settled BHT, unknown department, malformed JSON |
| 401 | Missing or invalid Finance API key |
| 404 | Unknown request id |
| 409 | Cancelling a request that already has a fulfilled or rejected line, or was already cancelled |
| 500 | Unexpected server error |
Error responses carry a human-readable message explaining what to fix.
Setup Prerequisites
Before integrating, the hospital must configure the requestable items:
- Service and investigation items — created as inward services or investigations with fees and a category configured, so department staff can bill them via Add Services & Investigations.
- Stock items — created as stock-tracked items and stocked into each target department through the normal GRN flow. Processing an inventory line deducts that department's stock and charges the batch retail sale rate.
- Privileges — department users who will process/reject lines need the Inward Service/Item Request Approval / Rejection privileges.
- API key — issued to the integrating system via Admin → API Key Management.
The external system needs the HMIS item ids and department ids to build requests; the hospital administrator supplies these after creating the master data.
Related Pages
- Inward Service and Item Requests (Approval Queue) — the in-app approval workflow for department staff
- Inward Online Payment API — companion API for online inpatient payments