How the AI works
A model at the engine, not bolted on as a feature
Vael routes each financial read across language models, grounds every figure in your data, and recomputes the headline runway in code so a model slip never becomes your number. Here is exactly how.
Reading messy money is language work
A rules engine breaks the moment a vendor renames itself or a charge lands in the wrong month. A language model reads intent: it knows that Notion and Coda do the same job, that fourteen seats for six people is waste, and that a one-off legal bill is not next month's burn. That distinction, between a recurring cost and a one-time event, is only possible with a model that understands language, not just numbers.
Vael was designed around the model from the first line of code. The model is the engine. The math is verified in code, separately, so the two layers check each other.
You never pick a model
Every read is routed through an ordered table. A standard read goes to a fast, inexpensive model. A hard read goes to a stronger one first. If any model is unavailable, the next one in the table takes over.
Trigger: Clean ledger, fewer than 30 lines, no conflicting entries
// route table (standard)
primary → openai:gpt-4.1-mini
fallback 1 → openai:gpt-4o-mini
fallback 2 → openai:gpt-4.1Trigger: Messy or contradictory ledger, context note mentions a spike or something off
// route table (hard)
primary → openai:gpt-4.1
fallback 1 → openai:gpt-4.1-mini
fallback 2 → openai:gpt-4o-miniAnthropic adapter
If an ANTHROPIC_API_KEY is present in the environment, an Anthropic model is inserted into the route table as an alternate primary. This activates automatically, with no configuration required, and demonstrates that the routing layer is genuinely cross-provider rather than OpenAI with extra steps. You can also pin any single model by setting the VAEL_ANALYSIS_MODEL override.
The model reasons. The math is verified.
Every figure comes from your data. The headline runway is calculated in code from the model's own extracted numbers, so the two layers check each other.
Model extracts the figures
The model reads the data you provide and extracts: cash on hand, monthly revenue, monthly expenses, and each line item. It returns these as a structured JSON object, not prose.
Code recomputes the headline numbers
Runway is not taken from the model. It is calculated in code: cash divided by net burn. If the model extracted a figure that does not add up, the code produces the right runway anyway. A model arithmetic slip never becomes your runway.
Every figure is anchored to a line
The model is instructed to ground every number in the data provided. It cannot invent a line item that is not there. If the data is too thin to support a leak flag, the model returns fewer flags and says so in the summary.
// The model extracts cash, revenue, expenses as numbers.
// Runway is always recomputed from those numbers in code.
// A model arithmetic slip cannot become the founder's runway figure.
const netBurn = monthlyExpenses - monthlyRevenue;
const runwayMonths =
netBurn <= 0 ? null // profitable
: cash > 0 ? Math.round((cash / netBurn) * 10) / 10
: 0;What the model is told to do, and not do
The system prompt is a set of strict rules that shape every read. They are not defaults. They are hard constraints.
US English only
All reads are returned in plain US English. No em dashes.
Banned words
The model is never allowed to use: unlock, seamless, revolutionize, elevate, harness, cutting-edge, supercharge, unleash. These were banned explicitly in the system prompt.
Not advice
Vael is a copilot, not a financial, investment, tax, or accounting advisor. The system prompt states this and instructs the model to describe what the numbers say and suggest operational moves only.
Plain language a founder understands
No finance jargon, no hedging. If a figure is uncertain, the model says 'about' rather than presenting a false precision.
You are Vael, an AI CFO copilot for startup founders. You are a copilot, not a financial, investment, tax, or accounting advisor, and you never present yourself as one. Rules: - Ground every number in the data. Do not invent line items. - Plain language a founder understands. No finance jargon. - US English. No em dashes. - Never use: unlock, seamless, revolutionize, elevate, harness, cutting-edge, supercharge, unleash. - Do not give investment or tax advice.
What goes to the model, and what does not
The model sees only the numbers you provide. Nothing else in your account goes to the model. Your data is not sold and not used to train shared models.
Only the numbers you provide
The data sent to the model is exactly the text or numbers you paste or the transaction export you authorize. No names, no emails, no contacts, no documents outside the financial data field.
Not sold, not used to train shared models
Your data is not sold to third parties. Model providers are contractually prohibited from using your data to train or improve their shared models. Read the full details on the security page.
Provider keys are server-side only
API keys for model providers are stored as server-side environment variables. They never appear in the client bundle. The model call is made from the server, not the browser.
What the model receives, exactly
FINANCIALS (messy is fine, read carefully):
"""
[the text or numbers you pasted or the export rows]
"""
CONTEXT (optional):
"""
[the note you typed, if any, up to 800 chars]
"""That is the entire user message. No account metadata, no IP, no identifiers. The system prompt above is prepended. Nothing else is sent.
The output is validated before it reaches you
Every response from the model is passed through a validation layer before it is shown. The validator:
- Re-extracts the key figures from the model's JSON and recomputes runway, net burn, and runway-gain-days for each cut in code. If the model's arithmetic is wrong, the right number is shown.
- Rejects empty outputs. If the model returns no cash figure and no expenses and no leaks, the system falls back to a deterministic offline parse of the raw numbers rather than showing a blank card.
- Clamps and coerces data types. Severity values, direction fields, and currency codes are checked and defaulted. A model returning an unexpected severity label does not break the read.
- Limits output length. Leaks are capped at four, cuts at four, anomalies at three. The model cannot produce a read that overwhelms you.
The full validation logic is in lib/analyze.ts, in the coerce() function. It is not hidden. The code is the contract.
Know your runway by the end of the day.
Paste a month of numbers or connect an account. Vael reads the whole picture and tells you what to do about it.