Your dental clinic
A dental clinic (tenant in the API and code) is your customer account: an independent clinic or a whole dental network. It is the isolation boundary of muveya.
- Every operational record (locations, warehouses, supplies, boxes, movements, orders, decisions, deliveries) belongs to exactly one dental clinic. Nothing is ever shared or moved between two of them, and a request that does not resolve to your dental clinic is refused.
- A person can belong to several dental clinics. Use Choose a dental clinic on Home, or the switcher at the top of the sidebar, to change the one you are working in. Role, permissions and site access are set separately in each one.
- An API key belongs to exactly one dental clinic. The public API derives the dental clinic from the key and never accepts it as a parameter. See Authentication.
Locations
A location (clinic, clinicId) is an operational site of your dental clinic, the place where supplies are requested, received and used. The console lists them under Locations.
A person’s Location access decides which orders, approvals and deliveries they see. See Locations.
Warehouses
A warehouse (warehouse, warehouseId) is a place where boxes are stored. Every box is in exactly one warehouse at a time.
Rules the server enforces:
- An express warehouse must name an existing location of your dental clinic; a central warehouse must not name one.
- The name is required, 1 to 120 characters.
- The status is
activeorinactive. An inactive warehouse takes no new stock (receiving into it or moving a box to it is refused) and cannot be chosen on new orders. - In the console, a new order’s Deliver to warehouse offers only the active warehouses that belong to the chosen location, so a location needs an express warehouse before it can order. The server never accepts another location’s express warehouse as destination. Take stock from offers central warehouses.
Rooms and areas
A room or area (destination, destinationId) is a place inside a location where supplies are used: a treatment room (treatment_room) or any other area (area). Rooms and areas are managed from the location’s page.
Room names are unique within a location. A room is
active or inactive; deactivating it keeps its name on past records.
When you record use of a box you can say where it went and how:
- What happened (
usage): Used directly (use) or Delivered to the room (issue). A delivery to a room leaves inventory once; who used it can be attributed later under Usage by room without discounting stock again. - Purpose (
purpose):procedure,cleaning,administrativeorother. - Responsible (
responsibleUserId): the team member responsible, who may differ from the person recording it. - Care reference (optional) (
careRef): the code from your clinical system, never a patient’s name. Letters, digits and. _ : / -, no spaces, up to 64 characters. It is stored sealed.
Supplies and the catalog
A supply (CatalogItem, itemId) is an item your dental clinic has authorized. It describes what can be ordered and received; it is not stock.
Status. A supply is born
draft (Draft). Activating it (Activate and fix unit) makes it active (Active) and fixes its unit. inactive (Inactive) is a soft retirement: nothing is deleted. Only active supplies can be added to orders, and the console only offers active supplies when receiving.
Units. The base unit comes from a closed list:
Presentations. A presentation (
presentationId) is how you buy the supply, for example “Box of 100”. Its Base units it contains is a whole number from 1. Receiving 3 of a presentation that contains 100 adds 300 base units. Presentations are versioned: correcting one publishes a new version, and boxes already received keep the version they were received under. A presentation is active or retired; a retired one can no longer be received.
Codes. A presentation can carry codes printed on the package: gtin (GTIN (barcode), 8 to 14 digits), supplier (Supplier code) and internal (Internal code). A code identifies what the article is, not which box it is, and points to one presentation at a time. See Presentations and codes.
Boxes
A box (StockBox, boxId) is a physical container of one supply. It has:
- a box code (
code) unique in your dental clinic. You can type your own label at receipt, or muveya generates one; - the supply it holds and its quantity, always in the supply’s base unit;
- the lot, serial numbers and expiry date when the supply tracks them;
- Received as: the presentation, version and count it arrived as, when it was received as a presentation;
- the warehouse it is in, and its status.
How a box changes status:
activetodepleted: recording use, or a correction, that brings it to zero.activetoquarantine,expiredordisposed: Take the box out of use, shown only for active boxes. muveya also accepts disposing of a quarantined or expired box, but the console has no button for that. Lot recall quarantines every active box of a lot at once.activetoin_transit: dispatch.in_transittoactivein the destination warehouse: an accepted receipt.in_transitback to the origin asactiveorquarantine: a disputed receipt.
active. The expiry date counts until the end of that calendar day in UTC.
Separating part of a box creates a new container with the same supply, lot, expiry and reception date, linked to its parent. Only free (unreserved) units can be separated, never the whole content, and not from a box that tracks serial numbers. Picking an order separates a box automatically when it holds more than the order needs. See Boxes.
The ledger
Every change to stock is a movement (StockMovement) appended to an immutable ledger. A movement records its type, the box, the supply, the quantity change, who did it (actorId), when it happened (occurredAt, by the server clock) and, when relevant, the order, the warehouses, a reason, the second approver or the room.
Nothing in the ledger is ever edited or deleted. A mistake is fixed with a new, compensating movement, and the original stays visible.
A retried operation never counts twice: each one carries an idempotency key, and a repeat returns the first result. See Inventory overview.
Balances
For each box, muveya derives from the ledger:
Replenishment also shows Usable now: the stock of active, unexpired boxes that can actually be used.
FEFO and FIFO
When muveya reserves stock for an order it ranks the candidate boxes of each supply:- FEFO (
fefo, first expired, first out) when any candidate box has an expiry date: the earliest expiry first, boxes without expiry last, then the oldest reception. - FIFO (
fifo, first in, first out) otherwise: the oldest reception first.
Orders
An order (Order, orderId) is an internal request for supplies from a location, delivered to one of its warehouses. Each order has a number unique in your dental clinic (number, shown as #12).
Rules:
- Only the requester can add, change or remove lines, and only while the order is a draft. Each line is an active supply and a whole quantity of at least 1, in the supply’s base unit. When a line is added, it keeps a snapshot of the supply’s cost, category and high-value flag.
- Only the requester can submit the order, and it needs at least one line. The order value is frozen at submission.
- The Reason (optional) is up to 2000 characters and is refused if it looks like personal data.
- Only the requester can cancel, and only from
draft,submittedorpending_approval. - Submitting and cancelling check the order’s version: if the order changed a moment before, the action is refused and the screen shows the latest version.
Any other change of status is refused. See Create and track orders.
Approvals and separation of duties
The approval policy decides which submitted orders need someone’s decision. See Approval policy.- Versions. Publishing creates a new version (
policyVersion) that replaces the current one. Published versions are never edited. Until the first version is published, submitted orders staysubmitted; after that, they are processed the next time any order is submitted. - Rules. Each rule has conditions and a requirement. Conditions: order value range (
minValue,maxValue, in minor units, inclusive), order types (orderTypes), categories (categories) and high-value supplies (highValue). An empty condition matches every order; a value condition never matches an order that has no value. The requirement is a Stage name (stage, up to 64 characters), the permission a decider must hold (requiredScope,approvals.decidewhen the console writes the rule) and People who must approve (minApprovers, 1 to 10). A policy has at most 100 rules. - Evaluation. At submission, muveya applies the current version to the order and stores the resulting plan with the order. If no rule matches (or the policy has no rules, as with Publish without approvals), the order is approved by the system at once (
auto_approve) and the automatic approval is audited. - Decisions. A decider picks a stage and chooses Approve or Reject. A rejection ends the order at once. The order becomes
approvedonly when every stage has its required number of different approvers. Each decision is an immutable record (approvedorrejected) with the person, time, stage, policy version and optional comment (up to 500 characters in the console).
- The requester can never decide their own order. The attempt is refused and audited, and the approval inbox never lists your own orders.
- A person decides a given stage of an order only once.
- The decider must hold every permission the stage requires and have access to the order’s location.
- A decision made on an outdated view of the order is refused without writing anything.
- Stock corrections above their threshold need a second, different person (see Counts and corrections).
- Delivery is confirmed from the source side (
delivery.confirm) and receipt from the destination side (receipt.confirm). A person limited to specific locations who has access to both the source and a different destination location cannot confirm the receipt or close the order.
Fulfillment and custody
Once approved, an order is executed by a fulfillment (fulfillment), which follows the boxes from the source warehouse to the destination. Custody is whole-box: a box travels complete, and a box that holds more than the order needs is separated first.
The fulfillment itself also has a status:
allocating (Reserving stock) while the reservation runs, then the same values as the order from allocated to closed.
Rules worth knowing:
- Allocation is all or nothing. If a line cannot be fully reserved, everything reserved in that attempt is released and the order stays Approved. muveya tries again on a later allocation run.
- Pick only accepts boxes reserved for that order; a box can be recorded once.
- Dispatch: the console offers Dispatch order once every reserved box is picked, and you can note the carrier. Each picked box must hold exactly the order’s quantity, which picking guarantees by separating larger boxes.
- Delivery problems cover the whole shipment. The console offers It could not be delivered, It arrived damaged, It went to the wrong place and Something else, plus optional details. An order in
exceptioncannot be received or closed from the console, and its boxes stay In transit; write to team@muveya.com to resolve it. - Receipt must decide every delivered box once. A disputed box needs a reason (the console offers Damaged, Missing, Wrong supply, Expired and Other) and a description in Evidence of the problem; you can ask to keep it in quarantine when it returns.
- Close moves no stock; it is available once every box was accepted or returned.
- Each of these steps writes an immutable record with the person and the time. The order’s History under Deliveries shows them together with the box movements.
Counts and corrections
Physical checks and corrections all needinventory.adjust.
- Correct this box records a gain or a loss with a quantity and a reason (Count correction, Damaged, Use not recorded, Other).
- Count a supply counts the active boxes of one supply in one warehouse. Start counting opens a count before you measure, so anything that moves meanwhile is noticed. A count (
cycleCount) isopen,submittedorcancelled. On submission, each box ends as no difference, corrected, waiting for approval, or changed while counting (count again). - A count campaign (
countCampaign) groups the counts of one warehouse and isopen(In progress) orclosed(Closed). Counts also lists boxes not counted recently. - Approval threshold. A correction larger than the threshold does not change stock yet: it becomes a request under Stock corrections that another person with
inventory.adjustmust approve from their own session. The threshold is 100 base units by default; a minimum per warehouse can lower it (0 to 100), never raise it. For high-value supplies every correction needs another person. A box has at most one waiting request.
See Corrections and Counts.
Replenishment and alerts
A minimum per warehouse (stockPolicy) is set for one supply in one warehouse, in its base unit:
Replenishment compares Usable now with the minimum: Below minimum (
below_minimum), Enough (ok) or No minimum (no_minimum).
Stock alerts are raised automatically: low_stock (Below minimum) and expiry_approaching (Expiring soon, shown as Expired once the date has passed). An alert is open until the condition clears, then resolved. See Replenishment and alerts.
Permissions and site access
A person’s access in a dental clinic has three parts. 1. Role (roleTemplate): a template.
2. Permissions (
scopes): everything else is granted one by one, to any role. inventory.adjust also includes inventory.receive, inventory.consume and inventory.transfer.
audit.read and integrations.manage can be granted, but no console screen uses them yet.
3. Site access: Location access (clinicScopeMode, clinicIds) and Warehouse access (warehouseScopeMode, warehouseIds), each set to all (including the ones created later), a selected list, or none. Location access limits the orders, approvals and deliveries a person sees; warehouse access limits the stock they see and move. The founding owner starts with access to all. Nobody can change their own site access, and an invitation always grants at least one location.
The console hides what you cannot use, but the server checks every request on its own. See Roles and permissions.
Server-side redaction
Some fields are removed by the server when the reader lacks the permission. They are absent from the response, not blanked, so no screen, export or integration can reveal them.
The public API never returns order values or patient references. The patient reference is stored encrypted and is never sent to AI, and WhatsApp messages never show costs, order values or patient references. See Security and privacy.
Audit
Besides the ledger and the immutable decision, delivery, receipt and closure records, muveya writes audit records for sensitive actions and refusals, for example approval decisions, a refused self-approval, automatic approvals, stock corrections, custody steps and refused catalog imports. Audit records are never edited. There is currently no screen or API that searches them; use a box’s Movements and an order’s History to follow what happened.Languages, time zones and currency
- Languages. The console, server messages and invitation emails are available in English (
en), Spanish (es) and Portuguese (pt). Choose with Language in the sidebar, in the phone header or on the sign-in screens. The choice is remembered in your browser; the first time, the console follows your browser’s language and falls back to English. Codes, statuses and permission names stay in English in every language. - Time zones. muveya stores every time in UTC. Most screens show dates and times in your device’s time zone. The consumption report groups days and weeks in UTC, and analytics results declare
timezoneasUTC. There is no time zone setting per dental clinic. - Currency. There is no currency setting per dental clinic. Each supply cost carries its own ISO 4217 code (three uppercase letters, such as
USDorCLP) and an amount in whole minor units:1200means USD 12.00 or CLP 1200. An order’s value only adds lines in the same currency as its first costed line, so keep your catalog in a single currency.