Overview
Rosetta is a clinical writing app in the browser.
These docs describe the current Rosetta codebase.
Rosetta Is Organized Around Three Systems
Editor
The editor is built on Lexical. Notes stay readable as plain text, and Rosetta adds nodes for template anchors, fields, and suggestion previews.
See the Editor section for the document model, lock behavior, and suggestion flow.
Workflows
Workflows are preset note actions such as summaries, Assessment & Plan drafts, de-identification, red flag checks, and presentations. They run from the right sidebar and return output to chat.
See Workflows for workflow behavior.
RAG
Rosetta can use uploaded files, pasted text, saved account sources, and PubMed results. The retriever can pull sources into an answer and format citations as [Source N].
See RAG for retrieval behavior.
What You Can Do In The App
- Organize work into folders, projects, and notes.
- Draft notes with or without a template.
- Use a local template override when one note needs different headings.
- Save templates and SmartPhrase templates.
- Add sources from files, text, images, and PubMed.
- Run workflows from the note.
- Review AI suggestions before they modify the note.
- Inspect accepted AI edits in the revision history.
Recommended Reading Order
Current Implementation Notes
- Workspace state is local first. Projects, folders, notes, and most template state load in the client.
- The app uses a right sidebar with dedicated tabs for Chat, Revisions, Workflows, and RAG.
- During beta, Rosetta is a bring your own key service for supported AI features.
Start with Getting Started. For implementation details, see Editor.
Getting Started
Sign in, set up your profile, and write your first note. Takes about two minutes.
Prerequisites
- A current version of Chrome, Firefox, Safari, or Edge.
First Time Setup
1. Configure Your Profile
On first sign-in, set your name, role (Physician, Nurse, Resident), and specialty. Rosetta uses these to pick which templates and agents to show by default.
2. Learn the Interface
- Header: template picker, save, export.
- Editor: the document you’re writing.
- Sidebar: Chat, version history, quick actions.
Basic Workflow
- Create a new document or start from a template.
- Write in the editor.
- Open Chat with
Ctrl/Cmd + Kto expand, format, or cite a passage. - Review each pending suggestion and accept or reject it.
- Save or export. Export is blocked if required template fields are empty.
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Ctrl/Cmd + S | Save document |
Ctrl/Cmd + N | New document |
Ctrl/Cmd + K | Open Chat |
/ | Command palette |
Ctrl/Cmd + B | Bold text |
Ctrl/Cmd + I | Italic text |
First Document
Three things you’ll use on most notes: version history, right-click actions on selected text, and Chat with citations.
Version History
Every save creates a new version. Open history from the header to compare any saved version to your current draft, and restore it if you want. Restoring also creates a new version, so the draft you were comparing against stays in the list.
Right-Click Actions
Select text and right-click to run an agent on just that selection (explain, expand, simplify, cite). The agent returns a pending suggestion. Your original text stays until you accept or reject it.
RAG Workflow
Ask Chat a clinical question. The Citations agent pulls supporting passages from your library and from the literature. Review the sources, pick the passages you want to use, and insert them. Each insertion brings its citation along.
Frequently Asked Questions
What Is Rosetta?
Rosetta is a clinical text editor with built-in agents. You write notes as usual. Agents do the tedious work: expanding abbreviations, pulling references, formatting citations, and drafting reasoning. Every change an agent proposes has to be accepted before it modifies your document.
How Do I Get Access?
Sign in at philipshih.org/apps/rosetta . Account creation is access controlled during beta.
What Can I Upload Into RAG?
The RAG panel accepts PDFs, plain text, Markdown, JSON, CSV, TSV, HTML, RTF, and common image formats. You can also search PubMed in the app.
What Is The Difference Between Plan And Act?
Plan reasons through a request first. Act returns an answer, draft, or edit right away.
How Do Templates Work?
Rosetta supports shared templates, workspace defaults, and local overrides for one note.
How Do I Use Chat Tools?
Press Ctrl/Cmd + K to ask a free-form question. Or select text and right-click to run an agent on just that selection, such as explain, expand, simplify, or cite. In both cases the result comes back as a pending suggestion you accept or reject.
What Happens When Rosetta Suggests An Edit?
Rosetta stores the edit as a suggestion first. You review it in the editor and accept or reject it. Only accepted suggestions enter revision history.
What Appears In Revisions?
The Revisions tab shows accepted AI changes. Each entry records the original text, the accepted text, the reason, and the time accepted.
Can Rosetta Search PubMed?
Yes. The RAG panel can query PubMed through the NCBI E utilities APIs and add selected articles as sources.
Does Rosetta Support General Document Import And Export?
The app is focused on drafting and review in the browser. These docs do not describe a separate import or export system for notes.
Which AI Models Does Rosetta Use?
During beta, Rosetta is a bring your own key service. You connect your own provider key in settings, and Rosetta uses that key for supported AI features.
Questions about the current docs can be sent to philip@philipshih.org.
Editor
The editor is built on Lexical 0.39. Documents are stored as a node tree. Agent edits show up as pending suggestions first; you accept or reject each one before it changes the document. Template sections can be locked so agents can’t write to them.
Components
| Area | What it does |
|---|---|
EditorCore | Mounts Lexical, tracks selection, dispatches commands. |
NoteEditorV2 | Sets up the composer, owns the live Lexical editor instance, and bridges debounced snapshots back to app state. |
TemplateManager | Holds template anchors and fields. Blocks writes to locked regions. |
SuggestionOverlay | Renders pending agent diffs in the editor and routes review to the revisions surface. |
SyncBridge | Persists debounced plain-text and Lexical JSON snapshots, with blur/unload flushes. |
Runtime Topology
Core Rules
- All document mutations go through
editor.update(). There is no other way to write to the document, so every change is transactional and observable. - Lexical is the live typing source of truth. React note state and persisted JSON are snapshots, not keystroke-by-keystroke control loops.
- Agents and automation cannot modify locked template anchors. Only a user edit can.
- Each suggestion is tagged with the revision it was generated against. If the user edits in between, the suggestion’s range is remapped onto the new revision, or marked stale if remapping can’t resolve it.
- Undo/redo only covers changes that actually landed. Rejected and stale suggestions never wrote to the document, so they don’t appear in history.
- Every applied change, whether from a user or an agent, emits the same change event. Sync, logging, and other subscribers read from one stream.
Document Model
Documents are serialized as node trees, not HTML strings:
interface SerializedEditorState {
root: {
type: "root";
version: number;
children: SerializedNode[];
direction: "ltr" | "rtl" | null;
};
}
interface SerializedNode {
type: string;
version: number;
children?: SerializedNode[];
text?: string;
format?: number;
detail?: number;
mode?: "normal" | "segmented" | "token";
}Node Types
class RosettaTextNode extends TextNode {
__format: number;
}
class RosettaParagraphNode extends ParagraphNode {
__indent: number;
}
class AnchorNode extends ElementNode {
__label: string;
__isLocked: boolean;
}
class FieldNode extends DecoratorNode<JSX.Element> {
__fieldType: "SELECT" | "MULTISELECT" | "DYNAMIC_SELECT";
__options: string[];
}Change Tracking
Rosetta keeps two separate records of changes:
- Lexical history stack: what undo/redo walks through. Only contains changes that were applied.
- Operational log: what sync and agent bookkeeping read from. Contains every transaction with its source, revision, and ranges.
Transaction Shape
type ChangeSource = "user" | "ai" | "template" | "sync";
type ChangeKind = "insert" | "replace" | "delete" | "format";
interface ChangeTransaction {
id: string;
revision: number;
parentRevision: number;
source: ChangeSource;
kind: ChangeKind;
createdAt: number;
ranges: Array<{
start: number;
end: number;
beforeText: string;
afterText: string;
anchorId?: string;
}>;
metadata?: {
suggestionId?: string;
agent?: "chat" | "shorthander" | "reasoner" | "reformatter" | "citations" | "ingester";
batched?: boolean;
};
}Keystroke-To-Commit Flow
During typing, Rosetta computes one derived editor snapshot per Lexical update. That snapshot carries the plain text, selection offsets, cursor position, slash-command context, SmartPhrase context, and context-menu selection geometry. JSON serialization is deferred until the persistence debounce or an explicit flush point such as blur, unload, save, or note switch.
Diff Strategy
The diff runs in three passes. Each pass only runs if the previous one didn’t produce a usable result:
- Node-level: compare subtrees by identity. Unchanged subtrees are skipped.
- Token-level: diff by sentence or field chunk. Most edits stop here.
- Character-level: used when token precision drops below 98%. Needed so overlays can highlight the exact changed characters.
function computeRanges(prev: string, next: string): RangeDelta[] {
const tokenPass = diffByTokens(prev, next);
if (tokenPass.precision >= 0.98) return tokenPass.ranges;
return diffByCharacters(prev, next).ranges;
}Range Remapping
A suggestion points at specific character offsets. If the user types before the suggestion is applied, those offsets now point at the wrong place. The fix is to shift each offset by the net length change of every user edit that happened at or before it:
function remapRange(range: { start: number; end: number }, deltas: RangeDelta[]) {
let { start, end } = range;
for (const delta of deltas) {
if (delta.pos <= start) start += delta.netLength;
if (delta.pos < end) end += delta.netLength;
}
return { start, end };
}Overlap and Conflict Rules
Agent Suggestion Lifecycle
Suggestion Object
type SuggestionStatus =
| "pending"
| "focused"
| "accepted"
| "rejected"
| "stale"
| "superseded";
interface AISuggestion {
id: string;
revision: number;
type: "replace" | "insert" | "delete";
start: number;
end: number;
replacementText: string;
reasoning: string;
confidence: number;
status: SuggestionStatus;
}Suggestion State Machine
Safe Apply Logic
function applySuggestion(suggestion: AISuggestion) {
editor.update(() => {
if (isLockedRange(suggestion.start, suggestion.end)) return;
const remapped = remapSuggestionAgainstLatestRevision(suggestion);
if (!remapped) {
markSuggestion(suggestion.id, "stale");
return;
}
replaceText(remapped.start, remapped.end, remapped.replacementText);
appendChangeTransaction({
source: "ai",
kind: suggestion.type === "insert" ? "insert" : "replace",
metadata: { suggestionId: suggestion.id }
});
markSuggestion(suggestion.id, "accepted");
});
}Visual Indicators
| Type | Color | Behavior |
|---|---|---|
| Insert | Green | Preview as additive text |
| Replace | Yellow | Original highlighted with replacement preview |
| Delete | Red | Original shown with remove indicator |
Template Anchors
interface TemplateAnchor {
id: string;
label: string;
isLocked: boolean;
nodeKey: string;
start: number;
end: number;
}Guard Rails
- Agents cannot modify locked anchors.
- Bulk accept skips suggestions touching locked anchors.
- Template updates can move anchors but cannot silently unlock them.
- Anchor deletion requires explicit user confirmation.
Anchor Navigation
| Shortcut | Action |
|---|---|
Ctrl + ] | Next anchor |
Ctrl + [ | Previous anchor |
Ctrl + Enter | Fill current anchor and advance |
Template Fields
SELECT single choice:
{{SELECT: medication | aspirin, clopidogrel, warfarin}}MULTISELECT multiple values:
{{MULTISELECT: symptoms | chest pain, dyspnea, diaphoresis}}DYNAMIC_SELECT options resolved from query:
{{DYNAMIC_SELECT: drug | query: medications for hypertension}}Field Node Rendering
class FieldNode extends DecoratorNode<JSX.Element> {
decorate(): JSX.Element {
return (
<FieldComponent
type={this.__fieldType}
options={this.__options}
onSelect={this.handleSelect}
/>
);
}
}SmartPhrases
Expansion rules for high-frequency sections:
const smartPhrases: SmartPhrase[] = [
{ trigger: ".cc", expansion: "Chief Complaint:\n" },
{ trigger: ".hpi", expansion: "History of Present Illness:\n" },
{ trigger: ".pe", expansion: "Physical Examination:\n" },
{ trigger: ".ap", expansion: "Assessment & Plan:\n" }
];Expansion is treated as a user transaction. Pending agent suggestions touching the trigger span are remapped; if remap fails, they are marked stale.
Plugin Order
<LexicalComposer initialConfig={config}>
<RichTextPlugin />
<HistoryPlugin />
<RosettaStateSyncPlugin />
<TemplateLockPlugin />
<TemplateReconciliationPlugin />
<EditorCommandPlugin />
<ClinicalHighlightPlugin />
<IndentGuidesPlugin />
</LexicalComposer>RosettaStateSyncPlugin owns the external load path and the shared derived editor snapshot. EditorCommandPlugin owns keyboard commands, suggestion commands, and template-field commands. Clinical highlighting and indent guides run after editor mutations, but both are scheduled outside the immediate typing path.
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Ctrl/Cmd + B | Bold |
Ctrl/Cmd + I | Italic |
Ctrl/Cmd + U | Underline |
Ctrl/Cmd + Z | Undo |
Ctrl/Cmd + Shift + Z | Redo |
Tab | Accept focused suggestion |
Esc | Reject focused suggestion |
Ctrl/Cmd + Shift + A | Accept all pending |
Ctrl/Cmd + Shift + X | Reject all pending |
Ctrl + ] | Next anchor |
Ctrl + [ | Previous anchor |
Ctrl + Enter | Fill anchor and advance |
Performance
- Keep JSON persistence off the keystroke path. Typing updates the Lexical tree immediately; persisted JSON is generated after the debounce or at a flush boundary.
- Compute editor-derived state once. Plain text, selection offsets, cursor position, slash commands, SmartPhrases, and context-menu selection data share one update listener.
- Cache clinical tokenization. The clinical highlighter reuses matches for unchanged text nodes and ignores selection-only updates.
- Schedule visual work. Clinical highlights, indent guides, minimap sampling, and row metrics run through timers, animation frames, and idle callbacks.
- Virtualize layout chrome. The gutter renders only visible line numbers plus spacers, and line metrics cache per-block row measurements.
- Debounce sync writes. Group rapid keystrokes into one transaction before sending. One PATCH per burst, not one per keypress.
Data Model
Rosetta stores documents as Lexical node trees, not HTML strings. The live node tree is the canonical form while the user is typing. Plain text and persisted JSON are debounced snapshots derived from that tree. Everything else (pending edits, template anchors, sync payloads) references positions inside it.
Core Entities
- Document nodes: block-level containers (paragraphs, headings) and inline marks (bold, italic, code).
- Pending edits: agent-proposed changes that haven’t been applied. They live outside the document until the user accepts them.
- Template anchors: named regions defined by a template. A locked anchor can’t be modified by any agent.
- Derived editor snapshot: the plain text, selection offsets, cursor position, slash-command context, SmartPhrase context, and context-menu geometry emitted from a single Lexical update listener.
Persistence Shape
The app persists two synchronized views of the same document:
content: plain text used by non-Lexical workflows, search, exports, and legacy integrations.jsonContent: serialized Lexical state used to restore decorators, template fields, suggestions, and editor-specific metadata.
Typing does not regenerate jsonContent on every keystroke. Rosetta updates the live Lexical tree immediately, emits a lightweight derived snapshot for UI consumers, then writes content and jsonContent after the persistence debounce or an explicit flush boundary.
Pending Edit Shape
A pending edit is self-describing. It carries where it applies, both sides of the diff, the agent’s reasoning, and which agent produced it. That’s enough to render, audit, or re-rank the edit without touching the document.
interface PendingEdit {
id: string;
type: "insert" | "replace" | "delete";
start: number;
end: number;
original: string;
replacement: string;
reasoning: string;
agent: string;
}For the full lifecycle (how a pending edit is remapped when the user types, how conflicts are handled, when an edit is marked stale) see the Editor Guide.
Keyboard Shortcuts
| Key | Action |
|---|---|
Tab | Accept current edit |
Esc | Reject current edit |
Ctrl+Shift+A | Accept all pending edits |
Ctrl+Shift+X | Reject all pending edits |
Notes
- Shortcuts apply when the editor has focus.
- Hover a pending edit to preview the diff before accepting.
Workflows
Rosetta puts note drafting, templates, and agent tools in one editor.
Document State
Document state is local-first. Every change is written to the device first and synced in the background.
- Local writes first: keystrokes hit the local store before any network call, so typing is never blocked on the network.
- Offline editing: you can keep writing without a connection. Queued changes are flushed when the device is back online.
- Deterministic merge: when edits arrive from multiple devices, they’re ordered by operation timestamp and source. Every client ends up with the same document.
Templates
Templates define note structure and field behavior.
Interactive Fields
- Text fields:
{{chief_complaint}} - Select menus:
{{SELECT:severity|mild,moderate,severe}} - Dates: Auto-formatted date pickers
Locked Anchors
Template regions that agents can’t write to: legal disclaimers, required section headers, billing codes. Only a user edit can change a locked anchor. This keeps compliance-sensitive content stable no matter how many agent runs a note goes through.
Agents
Run agents from Chat (Ctrl/Cmd + K) or by right-clicking selected text. Each run produces a pending suggestion. Nothing writes to the document until you accept it.
| Agent | What it does |
|---|---|
Shorthander | Expands medical abbreviations (SOB becomes shortness of breath). Skips locked anchors. |
Ingester | Parses uploaded PDFs and text files into searchable chunks. Keeps the original metadata so you can trace any chunk back to its source. |
Reasoner | Generates ranked differentials and problem-based plans. Weighs claims that have retrieved evidence over ones that don’t. |
Formatter | Rewrites selected text to match a requested format (SOAP, bullet list, table). The meaning stays the same. |
Citations | Searches PubMed, Embase, and Google Scholar. Attaches references in the configured style. Vancouver is the default. |
SmartPhrases
Custom text expansion shortcuts:
| Trigger | Expansion |
|---|---|
.cc | Chief Complaint section |
.hpi | History of Present Illness |
.ros | Review of Systems |
.pe | Physical Examination |
.ap | Assessment & Plan |
.dc | Discharge instructions |
State Flow
Rosetta uses a review-first workflow. The app separates drafting, AI output, suggestion review, validation, and revision history.
Typical Flow
Rejected suggestions leave the note unchanged. Workflow results return to chat unless the user explicitly accepts an edit.
State Rules
- Opening a note restores the current note content and template state.
- While drafting, Lexical owns the live edit. Plain text, selection metadata, and serialized JSON are emitted as debounced snapshots so typing stays responsive.
- Running a workflow returns output to chat, not directly into the note.
- Suggestions remain pending until the user accepts or rejects them.
- Accepting a suggestion updates the note and creates a revision entry.
- Rejecting a suggestion leaves the note unchanged.
Validation Points
Export is blocked unless all three are true:
- Every required template field has a value.
- Locked anchors match the template source exactly.
- Any claim that requires evidence has a citation attached.
Folder Workflow Flow
Folder presentation workflows follow a separate branch:
These workflows do not require a single active note, but they do depend on notes existing in the selected folder.
Template Pipeline
Templates define both the structure of a note and the rules it has to follow. Rosetta loads templates from a registry, applies them at workspace or note scope, enforces anchors in the editor, and can save edited variants back to the template library.
Template Sources
| Source | What it contains |
|---|---|
| built in templates | Templates shipped with the app |
| user templates | Templates saved in versioned local storage |
| workspace template | Default template selected for the current workspace session |
| local note template | A local override of the template anchor set |
Apply Flow
Pipeline Steps
- Select template: load the structure and its anchor and field definitions.
- Bind field values: resolve
SELECT,MULTISELECT, andDYNAMIC_SELECTinputs and insert them at their anchors. - Run agent transforms: agents propose edits against the bound document.
- Validate locked regions: any proposed change that overlaps a locked anchor is dropped before it reaches the review queue.
- Queue pending edits: remaining suggestions wait for the user to accept or reject them.
Global Vs Local Template Modes
| Mode | What it means |
|---|---|
global | The note follows a shared template definition |
local | The note stores its own anchor list and optional custom name |
Local mode is useful when a note needs custom headings but you do not want to change the shared template for every future note.
Template Inference
Rosetta can infer template state from the note content itself. If the note headings match a known template anchor sequence, the app can attach that template state. If headings no longer match, the app can clear or replace the template association.
This keeps template behavior aligned with the actual note text instead of assuming the original template still applies.
Template Editor
The template editor supports two formats:
| Format | What Rosetta stores |
|---|---|
natural | Template content with readable headings |
smartphrase | SmartPhrase content with a reusable trigger |
When you save template edits, Rosetta can:
- overwrite the current template
- save a new template
- store SmartPhrase metadata alongside the template
- save the SmartPhrase into the SmartPhrase registry
Structured Fields
Templates can include field metadata such as:
textselectmultiselectdynamic_select
These fields are rendered by the editor and can be extended while editing a template.
Guardrails
- Agents cannot write to locked regions at any step.
- Required fields are checked at export, not at drafting. You can fill sections in any order.
Promotion Path
Rosetta supports a practical upgrade path:
- start with a shared template
- customize one note locally
- save the local template as a reusable global template later
That is the main reason the app distinguishes between global template state and local note template overrides.
RAG
Every agent response in Rosetta is backed by retrieved sources: your uploads, your account library, and public medical databases. Agents don’t answer from memory alone.
Architecture
Source documents are split into passages, and each passage is converted into a vector that represents its meaning, not the exact words. A query is turned into a vector the same way. Retrieval returns the passages whose vectors are closest to the query vector (by cosine similarity). That’s why a search for “heart failure treatment” can match a passage that says “HFrEF management”: the two mean roughly the same thing, so their vectors sit near each other.
Semantic vs Keyword
Keyword Search (Traditional):
Query: "heart failure treatment"
Matches: Exact text "heart failure" AND "treatment"
Misses: "HFrEF management", "cardiac dysfunction therapy"Semantic Search (Rosetta):
Query: "heart failure treatment"
Matches: Any semantically similar concepts
- "HFrEF management"
- "cardiac dysfunction therapy"
- "GDMT for reduced EF"Source Types
- Local: sources attached to the current note. Good for one-off uploads tied to a specific patient.
- Account: guidelines, protocols, and papers in your persistent library. Indexed once and available to every note on your account.
PubMed Integration
PubMed is a third source type. Unlike local and account sources, it’s queried live:
- The agent rewrites your request into a PubMed search string.
- Top results come back with their abstracts and metadata.
- Those abstracts are embedded and mixed into the same retrieval pool as your local and account sources. Citations can come from any of the three.
Usage Examples
Evidence-Based Treatment
Add a guideline PDF to your Account library. Ask “heart failure reduced ejection fraction treatment” in a note. The agent retrieves guidance and generates a plan with citations from the uploaded PDF.
Literature Review
The agent searches PubMed for recent abstracts on a topic and generates a summary with current scoring criteria.
Institutional Protocols
Upload a hospital protocol to the Account library. Query it by name (e.g., “UCSF sepsis bundle timing”) and the agent returns the relevant details.
Retrieval Pipeline
Rosetta retrieval has two stages: ingest and search. Before an agent generates a response, Rosetta pulls supporting passages from your library and the literature, then sends the retrieved passages with the question.
Ingest
1. Add A Source
Sources come from uploads, pasted text, account sync, or PubMed search.
2. Normalize The Payload
Rosetta normalizes the source into a RagSource record.
3. Extract Text When Needed
PDF uploads are parsed in the browser with pdfjs-dist. If Rosetta cannot extract usable text, the source is still stored, but it is marked so it does not enter normal text ingest.
4. Preserve Scope
Each source is treated as either:
localaccount
Account sources can be merged into the current folder when the user is signed in.
Search
Retrieval Defaults
The semantic search flow:
- chunks source documents into passages
- embeds text content with
text-embedding-004 - uses cosine similarity for matching
- returns a small result set
- builds the prompt with
[Source N]labels
Retrieval Controls
- Source filters: limit retrieval to a specific library, such as local, account, or public sources.
- Date windows: limit to a time range, for example guidelines from the last five years.
- Relevance thresholds: drop chunks below a minimum similarity score so weak matches do not dilute the context.
PubMed Path
PubMed follows a parallel path:
- Rosetta searches PubMed
- loads summaries and abstracts
- converts articles into RAG sources
- lets the user add the selected articles to the active context
Practical Implication
If you want the retriever to use a source, make sure Rosetta has usable text for it and that the source is included in ingest.
Citations
When retrieval returns supporting passages, each one is attached to the response as a citation. If nothing was retrieved, the response comes back without citations. Rosetta won’t make one up.
Citation Payload
Each citation is self-contained. title is human-readable, url points at the document the agent retrieved, and relevance_score is the similarity score after re-ranking (0–1) so clients can sort or filter.
{
"title": "IDSA/ATS CAP Guidelines (2019)",
"url": "https://www.idsociety.org/...",
"relevance_score": 0.98
}Best Practices
- Prefer primary literature. Guidelines and peer-reviewed papers outrank summaries and review sites.
- Use stable URLs. Link to DOIs or canonical publisher pages. Avoid search-result URLs that break when the session ends.
- Show the score. Displaying
relevance_scoremakes the difference between a 0.98 match and a 0.62 best-effort visible to the user.
API Reference
Rosetta provides a REST API for documents, templates, tool requests, and export workflows.
Base URL
https://philipshih.org/apps/rosetta/api/v1Authentication
Bearer token in the Authorization header:
Authorization: Bearer YOUR_API_KEYRate Limiting
| Tier | Limit | Burst |
|---|---|---|
| Free | 100 requests/hour | 10 concurrent |
| Professional | 1,000 requests/hour | 50 concurrent |
| Enterprise | 10,000 requests/hour | 100 concurrent |
Documents API
List Documents
GET /v1/documentsQuery Parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Results per page (default: 20, max: 100) |
offset | integer | Pagination offset (default: 0) |
sort | string | created_at, updated_at, title |
order | string | asc or desc |
search | string | Full-text search query |
tag | string | Filter by tag |
Example
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://philipshih.org/apps/rosetta/api/v1/documents?limit=10&sort=created_at&order=desc"Get Document
GET /v1/documents/:idCreate Document
POST /v1/documentsRequest Body
{
"title": "New Progress Note",
"content": "Document content in markdown...",
"tags": ["progress-note"],
"template_id": "tpl_123",
"metadata": {
"document_type": "progress_note",
"date_of_service": "2026-01-20"
}
}Update Document
PATCH /v1/documents/:idDelete Document
DELETE /v1/documents/:idDelete Response
{
"success": true,
"message": "Document moved to trash",
"recoverable_until": "2026-02-19T09:15:00Z"
}Templates & Tools
Templates
List Templates
GET /v1/templatesGet Template
GET /v1/templates/:idTool Requests
Ask Question
POST /v1/ai/askRequest Body
{
"question": "What is the treatment for community-acquired pneumonia?",
"context": "Outpatient, no comorbidities",
"include_citations": true
}Generate Text
POST /v1/ai/generateRequest Body
{
"prompt": "Expand on COPD exacerbation",
"context": "Progress note, moderate severity",
"max_tokens": 500,
"temperature": 0.7
}Export & Webhooks
Export Document
POST /v1/documents/:id/exportRequest Body
{
"format": "pdf",
"options": {
"include_metadata": true,
"include_citations": true,
"page_size": "letter",
"orientation": "portrait"
}
}Supported Formats
pdfdocxhtmltxtmarkdown
Webhooks
Subscribe to account events from Settings -> Webhooks.
Available Events
document.createddocument.updateddocument.deletedexport.completedai.query_completed
Payload Example
{
"event": "document.created",
"timestamp": "2026-01-20T10:30:00Z",
"data": {
"document_id": "doc_abc123",
"title": "New Progress Note"
}
}Errors & SDKs
Error Response Format
{
"error": {
"type": "validation_error",
"message": "Invalid request parameters",
"details": [
{
"field": "title",
"issue": "Title is required"
}
],
"request_id": "req_abc123"
}
}HTTP Status Codes
| Code | Meaning | Description |
|---|---|---|
| 200 | OK | Successful request |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource does not exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Contact support |
Error Types
authentication_errorvalidation_errorpermission_errorrate_limit_errornot_found_errorserver_error
SDKs
Python
pip install rosetta-clientfrom rosetta import RosettaClient
client = RosettaClient(api_key="YOUR_API_KEY")
documents = client.documents.list(limit=10)JavaScript / TypeScript
npm install @rosetta/clientimport { RosettaClient } from "@rosetta/client";
const client = new RosettaClient({ apiKey: "YOUR_API_KEY" });
const documents = await client.documents.list({ limit: 10 });