For the person who has to decide

Enough to rule us out.

Five questions a hospital asks in week three, answered here in week one. Every answer is marked with what we have actually built, and each one names the thing in the product that makes it checkable. Where we have not built something, the badge says so and the answer says why it should worry you.

The homepage brief covers what this replaces, what the first week would ask of you, and who should not bother. Read that first if you have not — this page assumes it.

Question one

What happens when the network drops?

Two different failures wear the same word, and they have opposite answers. Asking which one you mean is the first thing a serious evaluation should do, so the page does it for you.

  • Built

    The message cannot get out — the work still completes, and the screen says so.

    A confirmation, a dispatch or an issue to a ward is one action; telling somebody about it over WhatsApp or email is a second, separate one. When the second leg does not land, the first is not rolled back and it is not quietly reported as fine. The interface shows a notice telling the person in front of it to print the slip or call the ward, and it fails closed: only an explicit confirmed delivery hides that notice. A message accepted into the queue but not yet sent counts as not delivered, and the wording says exactly that.

    PaperFallbackNotice, rendered on the storekeeper dock and both supplier order screens. Its gate is signal_delivered === true and nothing else — false, null and undefined all render the notice. The four states it distinguishes (delivered, queued, failed, not attempted) live in src/lib/notifications/signal-status.ts, which exists because signal_delivered used to be set the instant a queue row was inserted, so the UI hid the paper notice precisely when the WhatsApp had not left the building.

  • Built

    The slip it tells you to print is a real document, not a screenshot.

    The ward indent issue slip is generated as a PDF carrying the indent number, the ward, the per-line quantities and signature lines — the same slip a paper-only hospital already trusts. It is the thing the product degrades toward on purpose, rather than a blocked screen and a phone call to us.

    GET /api/v1/ward-indents/[id]/pdf, generated by src/lib/pdf/ward-indent-slip.ts. Role-checked and tenant-filtered like every other route (ADR-021).

  • Not built

    Your own connection drops — the action does not happen, and nothing is saved for later.

    There is no offline mode. If the browser in the store room cannot reach the server, the confirmation is not recorded, nothing is buffered on the device to be sent when the signal returns, and the screen shows a plain network error asking the person to try again. This is the single most important limitation on the page. The one screen that has to work — a storekeeper confirming a delivery at the receiving door — is often the screen furthest from the router. If that corner of your building has no reliable signal, this is a reason to wait rather than a detail to manage.

    Searched for and absent: no service worker, no web app manifest, no offline cache, no navigator.onLine handling, no write queue anywhere under src. What exists instead is per-screen error text — "Network error — check your connection" on the storekeeper stock count, "Network error — try again" on the count sheet and the supplier screens. A retry-and-offline fetch wrapper does exist at src/lib/api-client.ts and is imported by nothing, so it changes the behaviour of no screen and is not claimed here.

  • Built

    Nothing half-writes. A connection lost mid-confirmation cannot leave the number and the ledger disagreeing.

    The stock figure and the ledger entry that explains it are written together or not at all, inside one database transaction that locks the row first. Application code is forbidden from changing a stock quantity any other way — not discouraged, forbidden: the build fails if it tries.

    Migration 077, fn_write_stock_movement(). The function locks the stock row, re-checks the guard against the locked value, and writes the level and the ledger entry in one transaction. An invariant scan runs in the deploy gate and fails the build on any application-side write of current_quantity (ADR-045). The rule exists because a read-compute-update in JavaScript once let five concurrent dispatches write five ledger rows all stamped with the same before-figure.

  • Built

    An alert is not lost because a provider was down at that moment.

    Notifications are rows in a queue with an attempt count and a next-retry time, drained on a schedule rather than sent inline. A send that fails is tried twice more at widening intervals before the row is marked failed — and failed is a state somebody can see, not silence. Hospital staff messages are additionally held overnight and released in the morning, which is a deliberate choice about not waking a ward, not a delivery failure.

    src/lib/notifications/send.ts — three attempts maximum, backoff of five then fifteen then forty-five minutes, queue drained by a scheduled job. Quiet hours are per-tenant and push next_retry_at to the window end without incrementing the attempt count, so an overnight hold never consumes a retry.

Question two

What happens to our spreadsheets and our existing records?

Your catalogue comes across. Your history does not. Those are different answers and a demo is the wrong place to discover the second one.

  • Built

    A supplier price list goes in as a CSV, and you control the mapping.

    The hospital administrator uploads the file, the page reads its headers and proposes which of your columns is the product name, the unit, the cost, the GST rate. You correct anything it got wrong before a single row is created. The mapping grid is reachable whether or not the automatic suggestion arrives, so a dead key leaves you able to finish by hand rather than stuck behind a retry button. When you confirm, the page reports what the server actually did — rows imported, rows skipped — including a zero.

    The screen is /admin/products/import; the write is POST /api/v1/products/import, hospital administrator only. Skipping is by SKU within a supplier, so re-running a corrected file does not duplicate what already landed. A batch is capped at one thousand products, which is a paging limit rather than a licence limit.

  • Built

    CSV rather than Excel workbooks, and that is a deliberate refusal.

    We do not accept .xlsx, because the library needed to read it carries a known high-severity vulnerability and we are not willing to install it in a system that holds your procurement records. Re-exporting a workbook as CSV is one menu item in Excel or Google Sheets. It is a small piece of work for you and it keeps a supply-chain risk out of your hospital.

    Recorded on the import page itself and tracked as BUG-011. The xlsx package is absent from the dependency list for this reason. Worth knowing that reports come back OUT as .xlsx — that path uses a different, maintained generator.

  • Built

    Two things your file will not contain, and what happens to them.

    A supplier price list does not say which supplier it belongs to, so you choose that on the page. It usually does not carry SKUs either, so one is generated per row using a readable convention. Rows the importer cannot turn into a product are reported back line by line with the reason — never silently dropped, which is the failure mode that makes people distrust an import six months later when the count is wrong.

    Supplier chosen from GET /api/v1/suppliers on the import screen. Required fields are checked in the browser before Confirm is enabled, so a missing mapping blocks the button rather than producing a rejection after the reviewer believed they were finished.

  • Not built

    Your historical stock, orders and invoices do not come across.

    There is no importer for past transactions. The ledger begins on the day you begin, and the first physical count is its opening balance. Your old records stay wherever they are now, and if an auditor asks about last year you will answer from those, not from this. We would rather you plan around that than discover it during the first month-end.

    Searched for and absent: the only bulk write into the system is the product catalogue import above. There is no route, no screen and no script that accepts historical stock movements, closed orders or past invoices.

  • Partly built

    Handwritten and printed ward indents can be read by machine — but your staff cannot reach it.

    An endpoint exists that takes a CSV, a PDF or a photograph of a ward indent, runs it through optical character recognition in English, Hindi and Kannada, and returns the parsed lines for a person to check before anything is created. It writes nothing by itself, which is the right design. What it does not have is a screen. Today it is reachable only by an API call, so treat it as unavailable to your ward staff and ignore it when you are deciding.

    POST /api/v1/indents/upload, role-checked, five-megabyte ceiling, covered by integration tests. No component under src calls it — verified by search, which is why it is listed here as partly built rather than as a feature.

Question three

How long until we are running?

We are not going to give you a number, and the reason is better than the excuse. No hospital has ever gone live on this product. Four different onboarding durations have been published on this website by people estimating in good faith, and all four were deleted once somebody asked where the figure came from. A fifth would be the same mistake in a different font. What we can give you instead is the list of things that have to be true before your first delivery can be confirmed — in order, each one a gate rather than an estimate.

  • Built

    Accounts, with roles, before anything else.

    Your hospital exists as a tenant and every person who acts has an account carrying a role. The role is not decoration: every route checks it before it runs a query, and the five roles see genuinely different screens. Nothing below can start until this is done, and it is the fastest of the five gates.

    Roles are hospital administrator, purchase manager, storekeeper, ward head and supplier. Route-to-role mapping is declared in one table in src/middleware.ts and enforced there; INV-3 requires the role check to precede any database query in every write route.

  • Built

    At least one supplier, and at least one ward.

    An order goes to the supplier attached to the product, so a catalogue with no supplier behind it cannot produce an order. A ward with a head who can raise a request is the other end of the same loop. One of each is enough to start; a narrow first loop is easier to fix and easier to abandon if it turns out to be wrong.

    The supplier list drives the import screen and the order routes. The ward head dashboard is the request end — quick order packs, the requester’s own pending indents, and a view of central store stock.

  • Built

    The catalogue. This is one of the two gates that costs you real time.

    The items you actually order, with your units and your costs. The import above does the mechanical part; the part nobody can do for you is deciding what belongs on the list and checking that the mapping is right. When you ask us how long this takes, this is one of the two honest places to look for the answer.

    POST /api/v1/products/import, as above.

  • Built

    An opening count. This is the other one.

    The ledger’s first number has to come off the shelf, because every figure after it is derived from that one by arithmetic the system performs. There is a stock count screen for exactly this. How long it takes is a question about your store room and your staff, not about our software, and anybody who quotes you a figure without having seen your store room is guessing.

    The storekeeper stock count screen writes through the same guarded path as every other quantity change, so the opening count is itself a ledger entry rather than a number typed over the top of one.

  • Built

    Then the first delivery, confirmed by a storekeeper, with the WhatsApp group still running.

    The parallel run is not a transition phase we tolerate; it is the plan. Nobody is asked to switch off the fallback while the new thing is unproven. The point at which you can say this worked is the point at which you compare the ledger against the shelf yourself and the two agree.

    The delivery confirmation offers full receipt, partial receipt with per-line quantities, and rejection — partial receipt is a Phase 1 behaviour, not a later addition. A short delivery or a rejection moves that stock line’s confidence score down, a full one moves it back up, and a line that has fallen far enough is flagged for review until somebody counts it by hand. The thresholds themselves are ours, not a hospital’s: they have never been confirmed with a working purchase manager, and the code says so where they are defined.

Question four

Who has to change how they work?

One person’s job changes materially. Two barely change. One can carry on inside WhatsApp and never open the product. And one of the five is not your employee at all, which is the part of this you cannot decide on your own.

  • Built

    The storekeeper changes the most, and this is the only change that really matters.

    Every delivery is confirmed on a screen as a full receipt, a partial receipt with the quantities that actually arrived, or a rejection. That single act is what writes the ledger. Everything else in the product is a queue waiting for it. If this one person will not do this one thing, none of the rest is worth buying — which is why they are the person we would train first, and why the receiving door is where a pilot succeeds or fails.

    The delivery screen is built for that environment rather than for a desk: a sunlight mode that enlarges every control, hold-to-confirm on the destructive action, and per-line quantity entry. It posts to the receive route, which writes the stock level and the ledger row in one transaction.

  • Built

    The purchase manager does not have to open the product to approve.

    An approval can be given by replying to the WhatsApp message itself. APPROVE takes the oldest pending order; APPROVE with an order number takes that one; REJECT with a number and a reason cancels it with the reason recorded; STATUS lists what is waiting. YES and OK are accepted as approval. This is the part of the product that most deserves the phrase WhatsApp-first, and it is the reason the busiest person in the chain is not the bottleneck.

    POST /api/webhooks/twilio. The reply grammar is parsed in src/lib/whatsapp/reply-parser.ts; the sender is resolved from their registered phone number and their role is checked before the action runs. The webhook verifies Twilio’s signature — with one hole we state plainly on our security page rather than here.

  • Built

    The ward head raises a request instead of sending a message, and can see what happened to it.

    The change is small and it is mostly a gain: the request stops being a message somebody has to remember and becomes a row with a state. Their screen shows quick order packs for the things they ask for repeatedly, their own pending requests, and what central store actually holds — so the question that usually costs a phone call is answered before it is asked.

    The ward head dashboard composes quick order packs, the requester’s own indents scoped to them, and a central-store stock view scoped to the hospital.

  • Built

    The hospital administrator gets a new job, and it is front-loaded rather than permanent.

    Catalogue import, accounts and roles, thresholds. Most of it happens before you go live and then stops. Afterwards this role is the one that adds a supplier, adds a person, or changes a reorder level.

    The administrator-only surface is the /admin prefix, restricted to that single role in the route table; the product import and threshold routes reject every other role with a 403.

  • Partly built

    Your supplier has to agree, and they are not yours to instruct.

    Suppliers get their own portal — separate from your hospital, scoped to them — where they acknowledge an order and mark it dispatched. That is half of the loop you are buying. This product was built with one distributor, Bhavani Enterprise, and any other supplier of yours has to be willing to use it. That is a conversation you would have to have with them, and it is the most common reason a pilot like this stalls. We would rather you have it before you sign than after.

    The supplier portal is a separate route group with its own acknowledge and dispatch routes, keyed to a supplier rather than to a hospital tenant. Bhavani Enterprise is the one real supplier relationship behind this product; there are no others, and we will not imply otherwise.

Question five

What does it cost to leave?

A vendor who has not thought about this question has answered it. Here is what you can take with you today, what you cannot, and the one thing we would have to build if a clean exit matters to you.

  • Built

    Your ledger comes out as a file, for any period you name.

    The stock register — opening balance, receipts, issues and closing balance for each item over a stated period — downloads as a CSV. It exists because it is the shape an inspector asks for, and it happens to be exactly the shape you would hand to whatever you moved to next. It is not a summary we compute for the occasion: the four figures are the ones the ledger recorded at the time, and the export reconciles them by two deliberately different routes so a tampered file does not balance.

    GET /api/v1/compliance/stock-register with format=csv, reachable from the compliance panel in the product. Built on stock_movements, which has carried quantity_before, quantity_change and quantity_after on every row since migration 002 and is append-only.

  • Built

    Spending, supplier performance and order history come out as CSV or Excel.

    Three procurement reports, either format, available to a purchase manager or an administrator. The limitation worth knowing before you rely on it: these are requested one calendar month at a time, so a full year is twelve downloads rather than one.

    GET /api/v1/reports/export, with type of spending, suppliers or orders and format of csv or xlsx. The period parameter is a single month and defaults to the current one in Indian Standard Time.

  • Not built

    There is no single action that hands you everything.

    No button produces one complete archive of your hospital’s data. Leaving today means the register by date range and the reports month by month, which is real work even though every piece is obtainable. If a one-action clean exit is a condition of your procurement policy, raise it before you sign rather than after: it is a thing we would have to build, and we would rather be asked than assumed.

    Searched for and absent: the two paths above are the only exports in the codebase. There is no archive, bundle or account-level download route.

  • Not built

    For one person’s own personal data there is no self-service route either, and we have not built one.

    Our privacy notice describes access as a request we answer by hand rather than something you click, and that is an accurate description of what exists. Erasure is genuinely built — including a form on the privacy page for deleting a demo enquiry you might send us today — but the access side is manual. We say this here because our own security page already says it, and a claim that only appears where nobody looks is not a disclosure.

    The only route with export in its name serves the procurement reports above, which are a hospital’s business records rather than an individual’s copy of their personal data. This gap is recorded on /security as the reason one of our own published claims was withheld.

  • Not built

    What leaving costs commercially is not a question this page can answer.

    Notice periods, minimum terms and what happens to a part-year annual payment are contract terms, and there are none we can point you at in the product. Ask for them in writing, and treat any answer you get verbally as not having been given. That advice is against our interest and we mean it anyway.

    Stated as a gap rather than answered. Our pricing page lists tiers and an annual option; it does not publish contract terms, and this page will not invent them.

The short version

Five reasons to close this tab, from the five answers above.

A demo that ends in a polite no wastes your afternoon and ours. These are the disqualifiers this page creates, on top of the four already on the homepage.

  • A store room with no reliable signal at the receiving door. There is no offline mode, and the screen that must work is the one furthest from your router.
  • A hospital that needs its transaction history carried across. Nothing brings your past orders, deliveries or invoices with you, and the ledger starts at your opening count.
  • A procurement policy that requires a single complete archive on exit. Every piece of your data is obtainable today; one action that produces all of it is not.
  • A supply relationship where your distributor will not use a portal. You would be buying half a loop, and the half you cannot control is the other one.
  • Anyone who needs this decided this quarter on a fixed date. We will not quote you a go-live, and a vendor who does — for a product with no hospital live on it — is guessing at your expense.

If one of those five is you, we have saved us both an afternoon. If none of them is, the fastest next step is not a sales call — it is opening the product and trying to break it.

Prices and tiers are on the pricing page. Retention periods and your rights over personal data are on the privacy notice.