---
title: Alerts and Expiry
description: Query low-stock, expiring, and expired batches and act on them before they cost you a sale or a write-off.
---

You watch three alert feeds defined in `apps/server/src/features/alerts/route.ts`, plus the expiring-batch query in `apps/server/src/features/purchases/route.ts`. Check them on a schedule and act the same day.

### Watch low-stock medicines

Call `GET /alerts/low-stock` with an optional `branch_id`. With a branch, the server recomputes sellable stock from the `stock_movements` ledger via `sumStockByMedicine` in `apps/server/src/features/alerts/queries.ts`; without it, the same sum covers the whole organisation. Every row carries `deficit = threshold - stock`, sorted worst-first.

```bash
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8000/api/v1/alerts/low-stock?branch_id=$BRANCH_ID"
```

Turn each alert into a purchase order line. Fetch `GET /purchase-orders/suggested?branch_id=$BRANCH_ID` for the ready-made `suggested_quantity`, then draft the order as described in `/inventory/purchasing-and-purchase-orders`. If a sibling branch holds surplus, transfer instead following `/inventory/transfers-and-stockcount`.

### Catch batches before they expire

Call `GET /alerts/expiry?days=90&branch_id=$BRANCH_ID` for batches expiring inside the window, ordered soonest-first with `days_to_expiry`, `qty_remaining`, and `unit_cost`. The default window is 90 days when you omit `days`. Recalled and quarantined batches are excluded automatically.

```bash
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8000/api/v1/alerts/expiry?days=60&branch_id=$BRANCH_ID"
```

Push soon-expiring batches to the front of FEFO dispensing, mark them down or return them to the supplier per your policy, and transfer slow movers to a higher-volume branch. The purchases feature exposes the same window as `GET /purchases/expiring?days=90&limit=6` when you only need the next few rows for a dashboard card.

### Clear expired stock

Call `GET /alerts/expired?branch_id=$BRANCH_ID` to list batches past expiry that still hold quantity. This endpoint requires the admin role, so run it from an admin session or ask one to pull it for you.

```bash
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8000/api/v1/alerts/expired?branch_id=$BRANCH_ID"
```

For each row, quarantine the physical units, then call `PUT /purchases/:batchId/write-off` to zero the batch and record the `WRITE_OFF` movement. Verify the batch disappears from the next expiry pull and the medicine movement history in `/inventory/medicines-and-batches` shows the adjustment.

### Build a daily alert routine

You check low stock every morning, expiry weekly, and expired daily until the shelf is clean. Start with the branch-scoped low-stock feed, convert deficits into purchase order lines or transfers, then scan the 60-day expiry window for batches to push through FEFO first.

```bash
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8000/api/v1/alerts/low-stock?branch_id=$BRANCH_ID"
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8000/api/v1/alerts/expiry?days=30&branch_id=$BRANCH_ID"
```

Log who reviewed the feeds and what action each row received. Unactioned alerts repeat tomorrow, so close every row with an order, a transfer, a markdown, or a write-off and confirm the count drops on the next pull. Keep `low_stock_threshold` values honest in `/inventory/medicines-and-batches` so the deficit math reflects real cover days.

```bash
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8000/api/v1/purchases/expiring?days=90&limit=6&branch_id=$BRANCH_ID"
```

Pin that short expiring list to your dashboard so the team sees the next six risks without opening the full report. Record expiry dates at goods receipt and reject deliveries with short dates that your supplier will not take back.

Reading the feeds changes nothing by itself. It is the actions you take on each row, like the `WRITE_OFF` below, that land in the movement ledger, so your future self can prove the stock was handled in time.

### Next steps

Return to `/inventory/purchasing-and-purchase-orders` to reorder what low-stock alerts surfaced, and use `/inventory/transfers-and-stockcount` to rebalance branches before you place a supplier order.
Schedule the next review before you leave so nothing slips a day.
