> ## Documentation Index
> Fetch the complete documentation index at: https://muveya.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Security and privacy

> How muveya isolates each dental clinic, authenticates people, limits and hides information, protects patient references, records history, handles API keys, and where your data lives.

muveya holds the supply, stock and custody records of dental clinics. This page explains, in operational terms, how that information is protected on production today, and it names what is not available yet. The legal commitments are in the documents listed in [Legal documents](#legal-documents).

muveya is operated by Woku SpA.

## Each dental clinic is isolated

A **dental clinic** (tenant) is the customer account. Every operational record (locations, warehouses, catalog, stock, orders, approvals, custody) belongs to exactly one dental clinic.

* **The clinic comes from your session, never from what the browser sends.** On every request muveya checks that you have an active membership in the clinic your session points to, and only then reads or writes that clinic's records.
* **The data layer requires a clinic.** Every query for clinic records carries the clinic automatically; a query without one fails instead of reading everything, and a query that names another clinic is refused.
* **Another clinic's record looks like it does not exist.** Asking for an id that belongs to another dental clinic returns "not found", never "forbidden", so nobody can learn that it exists.
* **A person's access ends at a precise moment.** When a member is suspended or removed, muveya stamps a cutoff time: sessions, API keys and WhatsApp links issued before it stop working.
* **API keys belong to one clinic.** The public API derives the clinic from the key and never accepts a clinic selector.

## Authentication

* **Identity provider.** Passwords and Google sign-in are verified by Stytch, muveya's identity provider. muveya never stores your password: the account record has no password field.
* **muveya's own session.** After the provider verifies you, muveya issues its own session. The browser receives a random token in a `__Host-` cookie that is `HttpOnly`, `Secure` and `SameSite=Strict`; the database keeps only a hash of it.
* **Protection against cross-site requests.** Changes require the strict cookie, a same-site origin and a second token that is derived from the session and never stored.
* **Session length.** A session ends after 30 minutes without activity and 30 days after sign-in at the latest. Signing out ends it on the server.
* **Sign-in attempts.** After 10 failed attempts for the same email from the same network in 15 minutes, that combination is blocked for 15 minutes. The counter stores a hash, not the email or the address.
* **No account enumeration.** A wrong password, an unknown email, an unverified account and a missing verification code all get the same answer, and requesting a verification link always shows the same confirmation.
* **Email links.** Verification and invitation links carry their secret after the `#` of the address, which browsers never send to a server, and the console removes it from the address bar as soon as the page opens. Links are single use: verification and password links last 24 hours, invitation links 24 hours, and the invitation's own email check 10 minutes.
* **Invitations.** Opening an invitation shows the recipient's address masked (for example `a***@clinicanorte.com`), and nothing is accepted until the recipient reviews the access and selects **Accept invitation**.

See [Sign in](/docs/en/account/sign-in).

## Second factor (MFA)

* You can add an authenticator app (TOTP) from **Account security**. It is optional.
* Once you add one, every sign-in to your account, with a password or with Google, requires the six-digit code.
* Sensitive actions (team changes, approvals, stock corrections, catalog imports and exports) are marked for a second factor, but muveya does not currently ask for it before them.
* API keys never carry a second factor.

See [Account security](/docs/en/account/security).

## Least privilege and site scope

* **Roles include very little.** No role includes inventory, orders, approvals, delivery, report, cost, order value or patient reference permissions: each must be granted to the person.
* **Permissions are checked on the server for every request**, together with the person's locations and warehouses. A permission change applies from the person's next action.
* **Limits on the team screens.** An invitation can only offer permissions and sites the sender holds; nobody changes their own site access; only owners change roles, invite administrators, and suspend, remove or change the site access of an owner; a clinic always keeps an active owner. Anyone who holds `members.manage` can change any member's permissions, including their own, so grant it only to people you trust with every permission.
* **Separation of duties.** Nobody approves their own order, and a person whose locations cover both the supplying and the receiving side of an order cannot also confirm its receipt (unless they reach all locations).

See [Roles and permissions](/docs/en/account/roles-and-permissions).

## Information hidden without permission

muveya removes these values on the server, so they never reach a person, an API key or an MCP client without the right permission:

| Information | Permission |
| - | - |
| Supply costs (catalog, catalog CSV export, order lines) | `catalog.cost.read` |
| Order values and the value limits of approval rules | `orders.value.read` |
| Patient references and care references | `orders.patient_ref.read` |

One exception: a person who manages the approval policy (`approvals.policy.manage`) still receives its value limits from the server without `orders.value.read`; the console only hides them from view.

Custody history, pick lists, stock alerts, analytics reports and exports, and WhatsApp messages never include them for anyone. API keys can never read order values or patient references. Application logs redact credentials, cookies, request bodies, costs, totals, justifications and patient and care references.

## Patient references

muveya is not a clinical record. A clinical order can carry a **patient reference** (`patientRef`), and a stock exit can carry a **care reference**: both are opaque codes you take from your own clinical or practice management system, for example `ext-7f3a`.

* **Encrypted at rest.** Each reference is encrypted field by field with AES-256-GCM before it is stored. There is no searchable copy.
* **Shown only with permission.** It is decrypted only for people who hold `orders.patient_ref.read`. Creating a clinical order requires `orders.clinical.create`.
* **Never exported.** It is excluded from the public API, MCP, analytics exports and application logs.
* **Limits.** A patient reference is up to 200 characters. A care reference is up to 64 characters: letters, digits and `.` `_` `:` `/` `-`, starting with a letter or digit, without spaces.

<Warning>
  muveya does not check what you type in a patient reference. Never enter patient names, national identification numbers, diagnoses, clinical histories or other identifying or clinical data in a reference or in any free-text field. The order **justification** is checked automatically and refused if it looks like it contains an email address, a card number, a labeled identifier (such as RUT, DNI, CPF, MRN or "patient") or a run of nine or more digits; comments and notes are not checked.
</Warning>

## History you can trust

* **The stock ledger is append-only.** Every receipt, use, move, reservation, pick, dispatch, return, quarantine, expiry and disposal is a new movement; nothing is edited or deleted. A correction is a new compensating movement (`adjust_gain` or `adjust_loss`), never a change to the past.
* **Custody steps are separate events.** Approval, picking, dispatch, delivery, receipt and close are each recorded with who did them and when.
* **Audit trail.** muveya records audit entries for, among others: team changes (invitations created, resent, accepted and revoked; members suspended, removed and reactivated), approval decisions and automatic approvals, stock corrections and their requests and decisions, count reconciliations, lot quarantines and minimum changes, catalog imports and exports (including refused attempts), custody steps, WhatsApp confirmations, API key creation and revocation, and every MCP tool call and resource read. The content of an entry cannot be changed and entries are never deleted. The server clock sets the time.

<Note>
  There is no screen or API to read the audit trail on production yet, and the `audit.read` permission does not unlock anything today. If you need information from it, write to [team@muveya.com](mailto:team@muveya.com).
</Note>

## API keys and MCP

* **Format.** Keys look like `mvy_test_EXAMPLE...`, with a checksum at the end. muveya shows a key once and stores only its hash: it cannot be recovered.
* **One clinic, read-only scopes.** A key belongs to one dental clinic and carries one or more of seven read scopes; a key without scopes is refused. There are no write scopes. The only request that creates something is `POST /v1/analytics/exports`, which starts an analytics export.
* **Limits.** Up to 10 active keys per dental clinic, and 120 requests per minute per key on `/v1`; MCP uses OAuth separately.
* **Lifetime.** A key has no expiry date; it works until it is revoked, and revocation is immediate. A key stops working when the person who created it is suspended or removed, and reactivating that person does not bring it back.
* **Errors.** Failures are returned as RFC 9457 problem details, and an invalid, revoked or unknown key always gets the same answer.
* **MCP.** The `/mcp` endpoint uses a personal OAuth connection, limited by the member's current permissions and site access. Every tool call is audited without its content.

<Info>
  The console has no screen to create or revoke API keys yet. To get or revoke a key, write to [team@muveya.com](mailto:team@muveya.com). See [Authentication](/docs/en/api-reference/authentication) and [Connect MCP](/docs/en/mcp/connect).
</Info>

## WhatsApp

The WhatsApp channel is not part of the current pilot (see section 4 of the Terms of Service) and is not switched on in the production service today. It is built with these rules:

* A person links their WhatsApp number to their own membership with a one-time code that lasts 10 minutes; muveya stores only a hash of the code. One number links to one membership at a time.
* muveya stores the linked WhatsApp number to route messages.
* Messages carry box codes, supply and warehouse names and quantities, never costs, order values or patient references.
* Nothing changes stock until the person taps an explicit confirmation, and each step checks the same permissions and warehouses as the console.
* Menus and buttons are fixed. There is no AI in the conversation.
* A suspended or removed member's link stops working.

See [WhatsApp](/docs/en/whatsapp/overview).

## Artificial intelligence

muveya has no AI features on production. It does not send your data or your account data to AI model providers. The management briefing available through MCP is a fixed calculation over your metrics, not generated text. Before enabling any AI feature, Woku commits to updating the Privacy Policy and the list of subprocessors.

## Where your data lives

* **Application, files and cache:** Amazon Web Services, region `us-east-1` (United States). Stored files are private, encrypted and served only over TLS, through links that expire within an hour.
* **Database:** MongoDB Atlas.
* **Email:** sent from `no-reply@muveya.com` through Amazon SES.
* **In transit:** every public host (`console.muveya.com`, `api.muveya.com`, `muveya.com`) is served over HTTPS; plain HTTP is redirected.

The full list of subprocessors, their purposes and regions is in Annex III of the Data Processing Agreement.

## Legal documents

| Document | Link |
| - | - |
| Privacy Policy | [muveya.com/en/privacy-policy](https://muveya.com/en/privacy-policy) |
| Terms of Service | [muveya.com/en/terms-and-conditions](https://muveya.com/en/terms-and-conditions) |
| Data Processing Agreement | [muveya.com/en/data-processing-agreement](https://muveya.com/en/data-processing-agreement) |

Each document is published in English, Spanish and Portuguese (replace `en` with `es` or `pt` in the address). The current version is 1.0, effective 15 September 2026. The console links the Terms of Service and the Privacy Policy from its sign-in and registration screens.

To exercise a right over your personal account, including deleting it, follow section 12 of the Privacy Policy. If your request concerns records inside a dental clinic, contact that clinic first.

## Report a security issue

If you find a vulnerability or suspect an incident, write to **[diego@muveya.com](mailto:diego@muveya.com)**, the privacy and security address named in the Privacy Policy, the Terms of Service and the Data Processing Agreement.

* Describe what you found, where, and how to reproduce it.
* Do not include unnecessary personal data in the first message, and never include passwords, API keys or patient information.
* Do not access, change or delete data of dental clinics other than your own.

For general support, write to [team@muveya.com](mailto:team@muveya.com).

## Related pages

<CardGroup cols={2}>
  <Card title="Roles and permissions" icon="key" href="/docs/en/account/roles-and-permissions">
    Permissions, site scope and redaction.
  </Card>

  <Card title="Account security" icon="shield-halved" href="/docs/en/account/security">
    Add a second factor to your account.
  </Card>

  <Card title="Team" icon="users" href="/docs/en/account/team">
    Suspend or remove access.
  </Card>

  <Card title="API authentication" icon="code" href="/docs/en/api-reference/authentication">
    How API keys work.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.