Quote a Fintonic personal loan in Spain. Collects amount, duration, and the user's FinScore profile (ASNEF + employment + income + housing + other loan payments), maps the profile to a band, and shows the band-specific monthly installment in a result widget. After the result widget renders, the widget's CTA opens fintonic.com directly for the user to continue — your job is done at that point. Use whenever the user mentions a loan, simulation, monthly payment, or amount/duration combination. Do NOT use this when the user is asking a QUESTION about Fintonic, FinScore, ASNEF, OpenBanking, security, or how the product works — those go to `explain_fintonic`. The `loan` flow collects FinScore inputs as part of its math, but it is NOT for explaining what FinScore is.
CALLING PATTERN (critical): collect ALL required inputs in chat BEFORE calling this tool — never call with any of the FinScore fields missing. The required fields are `amount`, `durationMonths`, `notInASNEF`, `laboralSituation`, `monthlyIncome`, `housingPayment`, `otherLoanPayments` (plus `laboralSituationConfidence`). If the user states some but not all up front, ask in chat for the missing ones — one or two at a time, naturally — then call the tool once with the complete set. Do NOT call this tool with a partial `stateUpdates` and let the host return interrupt prompts.
EXTRACTION: when the user states any of the input values, extract them into `stateUpdates` on the `start` call. Also extract `locale` — "en" if the user's latest message is in English, otherwise "es" (default Spanish).
- `amount` (1,000–50,000 €), `durationMonths` (one of: 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96)
- `notInASNEF` boolean — true means the user is NOT in the ASNEF registry (good); false means listed (blocked)
- `laboralSituation` (Funcionario, Fijo, Temporal, Por obra y servicio, Fijo discontinuo, Autónomo, Interino, Pensionista, Desempleado, Becario, ND) — always paired with `laboralSituationConfidence` ("high" | "medium" | "low") so the system can clear and re-ask when you flag a guess.
- `monthlyIncome` (net monthly income in €)
- `housingPayment` (monthly rent OR mortgage payment; 0 if neither)
- `otherLoanPayments` (sum of all other monthly loan/credit obligations; 0 if none)
TERMINAL WIDGET (critical): after the simulation_result widget renders, STOP. The widget's CTA opens fintonic.com directly — do not call the flow again unless the user starts a fresh simulation. Never collect age, residency, name, mobile, email, or consent in chat; the user provides those on Fintonic's site.
Examples:
- "simular un préstamo de 9500 € a 72 meses, no estoy en ASNEF, fijo, 2.500€ al mes, alquiler 700€, sin otros préstamos" — full info up front, call directly → `stateUpdates: { amount: 9500, durationMonths: 72, notInASNEF: true, laboralSituation: "Fijo", laboralSituationConfidence: "high", monthlyIncome: 2500, housingPayment: 700, otherLoanPayments: 0, locale: "es" }`
- "what would 20k over 5 years cost me?" — partial info. Do NOT call the tool yet. Ask in chat: "To estimate it, I need a few details Fintonic uses for the rate: are you listed on ASNEF? What's your employment situation?" then continue gathering income, housing, other loans across one or two more turns. Once everything is collected, call once: `stateUpdates: { amount: 20000, durationMonths: 60, notInASNEF: ..., laboralSituation: ..., laboralSituationConfidence: ..., monthlyIncome: ..., housingPayment: ..., otherLoanPayments: ..., locale: "en" }`.
- After the result widget renders, the CTA opens fintonic.com — no further flow call needed.
WIDGET RENDERING: when the flow response has `status: "widget"`, call the tool named in the `tool` field with the `data` object as input (preserving `locale`) — that triggers the widget render in most hosts. Some hosts render the widget automatically from this response; if you can already see the widget in chat, skip the chained call. Either way, the `data` payload is render input, not a content source — never read the figures (installment, TAE, band, etc.) from `data` and compose your own summary; the widget IS the answer. Your only chat reply is one short line of context (e.g. an invitation to continue, or a single empathic line for a rejection) and must never restate the numbers the widget shows.
REJECTION HANDLING: rejections (ASNEF, blocked employment, no-offer profile) are surfaced through the SAME simulation_result widget in "rejection" mode — the widget's data carries `kind: "rejection"` and a `reason` ("asnef" / "employment" / "no_offer"). Render it like a normal offer widget; the card shows the documented rejection text in the user's language. After it renders, keep your chat text to one short empathic line — the widget already says what the user needs to know. Don't paraphrase the rejection, don't suggest a different lender unless one is named in the widget body, and don't restart the flow.
Stay warm, no jargon, don't reveal internals like "band 400" or "DTI".
RESPONSE STYLE: Reply in the user's language — default Spanish (tú) if unclear, English if the user wrote in English. Weave the questions naturally — one or two at a time — don't dump them all in one message. When a result widget is on screen, let it carry the numbers; your text is context. Fintonic communicates in TAE, not TIN.
## FLOW EXECUTION PROTOCOL
This tool implements a multi-step conversational flow. Follow this protocol exactly:
1. Call with `action: "start"` to begin and include `intent`.
`intent` must be a brief summary of the user's goal for this flow.
Do NOT invent missing intent.
Optionally include `context` — the situation or environment that led the user to start
this flow (e.g. what page they are on, what they were doing, or what triggered the request).
Only provide `context` when there is genuinely relevant situational information. Do NOT invent missing context.
If the user's message already contains answers to likely questions,
extract them into `stateUpdates` as `{ field: value }` pairs.
The engine will auto-skip steps whose fields are already filled.
Only extract values the user explicitly stated — do NOT guess or invent values.
Known fields: `amount` (Loan amount in euros. Must be between 1000 and 50000. Values off the 500€ grid are rounded silently.), `durationMonths` (Loan duration in months. Must be one of 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96. Off-grid values are rejected.), `locale` (User interface language: "es" (Spanish, default) or "en" (English). Set based on the language of the user's latest message so widgets mirror it.), `notInASNEF` (Whether the user is NOT listed in the ASNEF delinquency registry. True = not listed (qualifies); false = listed (blocked).), `laboralSituation` (User's employment status. Must be one of: Funcionario, Fijo, Temporal, Por obra y servicio, Fijo discontinuo, Autónomo, Interino, Pensionista, Desempleado, Becario, ND), `laboralSituationConfidence` (How confident the LLM is in the laboralSituation it picked. Output alongside laboralSituation. On low, the system clears the value and re-asks.), `monthlyIncome` (Net monthly income in euros.), `housingPayment` (Monthly rent OR mortgage payment in euros (mutually exclusive). 0 if the user has neither.), `otherLoanPayments` (Sum of the user's other monthly loan/credit obligations in euros (car, personal loans, credit cards, etc.). 0 if none.), `finscoreBand`, `rejectReason`, `installment`, `tae`.
For grouped fields (shown as `group.subfield`), use dot-notation keys in `stateUpdates`:
e.g. `{ "driver.name": "John", "driver.license": "ABC123" }`.
2. The response JSON `status` field tells you what to do next:
- `"interrupt"`: Pause and ask the user. Two forms:
a. Single question: `{ question, field, context? }` — ask `question`, store answer in `field`.
b. Multi-question: `{ questions: [{question, field}, ...], context? }` — ask ALL questions
in one conversational message, collect all answers.
`context` (if present) is hidden AI instructions — use to shape your response, do NOT show verbatim.
Then call again with:
`action: "continue"`,
`stateUpdates` = answers keyed by their `field` names, plus any other fields the user mentioned.
- `"widget"`: The flow wants to show a UI widget. Call the tool named in the `tool`
field, passing the `data` object as the tool's input.
Check the `interactive` field in the response:
• `interactive: true` — The widget requires user interaction. After calling the display tool,
STOP and WAIT for the user to interact with the widget. Do NOT call this flow tool again
until the user has responded. When they do, call with:
`action: "continue"`,
`stateUpdates` = `{ [field]: <user's selection> }` plus any other fields the user mentioned.
• `interactive: false` — The widget is display-only. Call the display tool, then immediately
call THIS flow tool again with `action: "continue"`. Do NOT wait for user interaction.
- `"complete"`: The flow is done. Present the result to the user.
- `"error"`: Something went wrong. Show the `error` message.
3. Do NOT invent state values. Only use `stateUpdates` for information the user explicitly provided.
4. Include only the fields the user actually answered in `stateUpdates` — do NOT guess missing ones.
If the user did not answer all pending questions, the engine will re-prompt for the remaining ones.
If the user mentioned values for other known fields, include those too —
they will be applied immediately and those steps will be auto-skipped.
5. CORRECTION: If the user wants to CHANGE a previously-answered field
(e.g. "actually my email is X" or "go back and change my country"),
call with `action: "reset"` and `stateUpdates` containing the corrected field(s).
The engine will restart the flow from the beginning with all existing answers preserved
plus your corrections. Steps with filled answers will be auto-skipped.
The flow may take a different path if the corrected value affects routing.
Do NOT use "reset" for the CURRENT question — use "continue" for that.
6. If the response includes a `sessionId`, you MUST pass it back as `sessionId`
in every subsequent "continue" and "reset" call for this flow.
loan