Documentation
Everything you need to get from a fresh account to a voice-driven 3D visualisation in a VR headset.
What's in here
This page is a hands-on guide to using MediVerse end-to-end. It walks through creating an account, defining your own data tables, importing CSV files, asking the system natural-language questions, calling the REST API from your own code, and connecting a Quest headset to your data. Every section is self-contained, so you can dip in at the point you need.
The research context behind MediVerse - why voice-driven immersive analytics, why an edge-cloud architecture, why this evaluation framework - lives on the research abstract and publications pages. The roadmap covers where it's going next.
1. Create an account
Visit /Account/Register. Enter an email and a password (minimum 6 characters, at least one digit, no special characters required). On submit, MediVerse:
- Creates an ASP.NET Identity user row in
AspNetUsers - Generates a personal MediVerse API key (starting
mv_followed by 64 hex chars) and stores it inUserApiKeys - Signs you in and redirects to the admin area
Before you can use the AI Query or AI-Create-from-CSV features, head to /admin/ApiKey and paste an xAI / Grok API key from your own account. See section 7 for the details.
2. Log in
Use /Account/Login. Sessions last 30 minutes of inactivity, after which the auth cookie expires and you'll be redirected to the login form on the next request.
3. Define a data source
In the admin area, a "data source" is a SQL table you own. To create one:
- Click + Create new index on
/admin - Give the table a name (letters, digits, underscores - no spaces)
- Add columns. Each column needs a name and a data type. Allowed types:
INTEGER- whole numbersTEXT- any stringDATETIME- stored as ISO-8601 textBOOLEAN- stored as 0/1REAL- floating-point numbers
- Optionally add a description per column - this hint is passed to the LLM, so describing units / categorical values helps query accuracy
- Click Create index. MediVerse runs a validated
CREATE TABLEbehind the scenes.
4. Import CSV data
On /admin/Import:
- Select the target data source from the dropdown
- Pick a
.csvfile (max 15 MB) - Click Upload & import
The first row is treated as a header and skipped. Subsequent rows are inserted column-by-column in the order you defined them, inside a single SQL transaction (atomic + fast). The CSV parser handles quoted fields with embedded commas; embedded newlines inside a field are not supported.
5. Query with AI
On /admin/AiQuery you can type or speak a natural-language question. Examples:
- "Show all patients with risk stratification high"
- "List the 10 highest blast counts"
- "Why do some patients have low survival?" - the word "why" or "explain" triggers a second LLM call that produces a plain-language explanation alongside the table
Behind the scenes:
- Your prompt + a schema summary of your tables are sent to the LLM (xAI Grok-3)
- The LLM returns a JSON object with the SQL query and visualisation hints
SafeSql.EnsureReadOnlyrejects anything that isn't a single SELECT/WITH- The SQL runs against your SQLite database, scoped to your user
- Results render in the admin UI; a parallel JSON shape is what the VR client receives
6. Export data
On /admin/Export, pick a data source and click Download CSV. The file is named {IndexName}_Export_{yyyyMMddHHmmss}.csv and contains every row of the table, with CSV-escaped values (double-quoted, embedded quotes doubled).
7. Your API keys
On /admin/ApiKey you'll see two keys side by side. They serve different purposes.
MediVerse API key
Starts with mv_ followed by 64 hex characters (256 bits of entropy). This is the key your VR client (or any other client) uses to call the MediVerse REST API. Pass it as the ?key= parameter on the endpoint, or paste it into the VR app's settings.
If the key is exposed or you suspect it's been copied, click Regenerate - that invalidates the old key immediately. Any client using the old key will start receiving 401 Unauthorized until you update them. Your xAI key (below) is preserved across regenerations.
xAI / Grok API key
MediVerse calls the language model (xAI's Grok) on your behalf when you query data or AI-create an index from a CSV. Those calls are billed against your xAI account, not ours. To enable them:
- Sign up at console.x.ai
- Create an API key (starts with
xai-) - Back on
/admin/ApiKey, paste it into the xAI / Grok API key form and hit Save xAI key
Until you set one, AI Query and AI-Create-from-CSV return: "No xAI / Grok API key on file for this account."
Your key is stored in the same SQLite row as your MediVerse key, never logged, and used directly as the Authorization: Bearer header on outbound xAI requests. We never see the rendered prompt or response in a database table - the only state we keep on the server side is the in-memory conversation context described in section 8.
Exception: the public demo account (the one we ship for visitors to try the platform without signing up) falls back to a shared key we operate, so the demo always works out of the box. Every registered account supplies its own.
Use the Clear saved key button to remove your stored xAI key (e.g. before rotating to a new one). After clearing, AI features stop working until you save a new value.
8. API reference
Endpoint
POST /api/AiQueryApi2/run_v2?key=<your-api-key>
Content-Type: application/json
Request body
{
"UserPrompt": "Show patients with high risk stratification",
"Reset": false
}
UserPrompt is the current question. Reset is optional and defaults to false; set it to true to forget any tracked context before this call (start a fresh conversation).
Successful response (NORMAL path)
{
"HasCoordinates": true,
"Data": [ { "Id": 1, "Risk_strat": "High", "Initial_Blastsx109": 52.3 } ],
"Visualization": "Scatter Plot",
"XAxis": "Initial_Blastsx109",
"YAxis": "Survival_Months",
"ZAxis": "Age",
"Title": "Risk stratification vs. initial blasts",
"ColorColumn": "Risk_strat",
"Details": [ "Id", "PatientName", "Diagnosis", "DateAdmitted" ]
}
Successful response (WHY path)
Triggered when the prompt contains "why" or "explain" without the literal token x=.
{
"Explanation": "Patients with high risk stratification typically present with..."
}
Responses contain only the rendered answer - the LLM's raw output, the generated SQL, and the running conversation are intentionally not echoed back to the client.
Conversation context
The server quietly remembers the last few turns of each user's conversation, keyed off the API key, so a follow-up like "now filter to those over 18" works without the client having to manage history. The running context is held in memory only - it is never written to the database and is dropped 30 minutes after the user's last call (or on server restart).
To start a brand-new conversation, set "Reset": true in the next request body. The server will forget the prior turns for this user before processing the prompt.
Error responses
The endpoint returns these HTTP status codes:
400 Bad Request-UserPromptmissing, LLM returned malformed JSON, orSafeSqlrejected the generated query401 Unauthorized- missing or invalid?key=500 Internal Server Error- unexpected failure (LLM unreachable, DB I/O, etc.)
curl example
curl -X POST "https://<your-host>/api/AiQueryApi2/run_v2?key=mv_abc123..." \
-H "Content-Type: application/json" \
-d '{ "UserPrompt": "List high-risk patients" }'
curl example (start a fresh conversation)
curl -X POST "https://<your-host>/api/AiQueryApi2/run_v2?key=mv_abc123..." \
-H "Content-Type: application/json" \
-d '{ "UserPrompt": "Show all patients", "Reset": true }'
9. VR client integration
The Oculus Quest / Quest 2 application is a Unity build that consumes the JSON response above and renders it as an interactive 3D scene. To connect it to your data:
- Open the MediVerse VR app on your headset
- Open the Settings panel (default key binding: hold A)
- Paste your API key from
/admin/ApiKey - Speak a query into the headset's microphone
The VR client picks the rendering mode based on the Visualization field in the response: Table, Scatter Plot, Line Chart, Bar Chart, or Bar Chart Heatmap. For Scatter Plot and Bar Chart Heatmap, the Details array (at least 4 columns starting with Id) is used as the data drawer on point-click.
10. Security model
MediVerse applies defence-in-depth across several layers:
- Authentication. Cookie-based for the web; per-user API key for the VR endpoint. Keys are looked up in constant time via SQLite's unique index.
- SQL identifier validation. Every table/column name interpolated into a SQL statement is validated against
^[A-Za-z_][A-Za-z0-9_]*$viaSqlIdentifier. Anything else (spaces, brackets, semicolons, Unicode) is rejected. - SQL data-type allowlist. Only the five types listed above are allowed via
SqlDataType. - LLM-SQL gate. The SQL the LLM returns is passed through
SafeSql.EnsureReadOnly: must be a single statement, must start withSELECTorWITH, must not contain comments or DDL/DML keywords. - Per-user scoping. Every query the LLM sees is built from the calling user's schema only. The schema summary for user A does not include user B's tables.
11. Troubleshooting
"Invalid login attempt"
The email exists but the password is wrong, or the email isn't registered. Use /Account/Register if you need a new account.
"Error: invalid index name"
Your table or column name has a space, a punctuation character, or non-ASCII letter. Rename it using only letters, digits, and underscores.
"No xAI / Grok API key on file for this account"
You haven't saved an xAI key yet, so MediVerse can't call the language model on your behalf. Visit /admin/ApiKey, paste a key from console.x.ai, and hit Save xAI key. AI Query and AI-Create-from-CSV start working immediately - no restart needed.
"Refusing to execute generated SQL"
The LLM returned something other than a SELECT/WITH query, or multiple statements, or used a disallowed keyword. Rephrase the prompt or check the server log for the rejected SQL.
"401 Unauthorized" from the API
Your API key is missing or doesn't match a user. Get the current key from /admin/ApiKey and update the VR client.
"Cannot login" right after deployment
If you deployed to a fresh environment and your old users aren't there, run tools/MigrateAzureSqlToSqlite to bring them over - see the DEPLOYMENT.md file.