FreeITSM has quietly grown a lot of AI. The Knowledge module answers questions from your articles. The CMDB writes plain-English summaries of configuration items. The Workflow builder will co-author an automation from a sentence. The Forms builder drafts a whole form from a paragraph. The Tickets module tidies a one-line reply into a proper email.

Every one of those started life hard-wired to a single vendor — Anthropic's Claude — each with its own little copy of "call the API, handle the errors, store the key". Five features, five copies of nearly the same plumbing, drifting apart over time.

Then someone asks the obvious question: "Can I use my own OpenAI account instead? Or run it through OpenRouter so I'm not locked to one vendor?" — and you really don't want to answer it five separate times.

So the work over the last day was to stop answering it five times. There is now a single drop-in building block that any module can use to offer a choice of AI providers — Anthropic, OpenAI, or OpenRouter — without re-implementing a line of provider plumbing. And the headline new option, OpenRouter, is the one that changes the maths the most.

Pick a provider. Pick a model. Paste a key. The same panel, on every AI feature.

This piece is about three things: why a shared building block beats five bespoke ones, what OpenRouter actually buys you, and how the keys stay safe — plus the one part deliberately left untouched, and why.

One: the building block

The whole thing is four small pieces that fit together, and a module only ever touches the first one.

A panel you drop onto a settings page

On the back end it's literally one line: renderAiSettingsPanel('knowledge_ai'). That renders the same tidy panel everywhere — a provider dropdown, a model field, an API key box, an SSL-verify toggle, and Save / Test buttons. No per-module markup, no per-module JavaScript. Every panel on the page is driven by one shared script.

A registry that says who's allowed

Each AI feature registers a short namespaceknowledge_ai, cmdb_ai, workflow_ai, forms_ai, tickets_reply_cleanup — with a sensible default provider and model. Adding a new AI feature to the whole system is, genuinely, one line in that registry plus dropping the panel on the page. That registry isn't just convenience, though; it's a security boundary, which we'll come back to.

Shared endpoints and a shared client

Behind the panel sit a handful of generic endpoints — get settings, save settings, test the connection, list OpenRouter's models — and a single provider-agnostic client that knows how to actually talk to each vendor. That client is the only code in the system that knows the difference between an Anthropic request and an OpenAI-shaped one. Everything above it just says "send this system prompt and this user message, give me back the text".

Knowledge — Ask AI CMDB — summaries Workflow — co-author Forms — AI Assist Tickets — reply cleanup

The payoff is concrete. Rolling the block onto CMDB and Workflow deleted far more code than it added — the bespoke "call Claude" helpers in those modules collapsed into thin wrappers around the shared client. Five features now share one well-tested path instead of each maintaining its own. And every feature is configured independently: you can point ticket reply-cleanup at a cheap, fast model and the workflow co-author at a stronger one, each with its own key.

Two: what OpenRouter buys you

Anthropic and OpenAI are the obvious two. The interesting one is the third.

OpenRouter is an aggregator: a single API, a single account, a single key — that proxies to hundreds of models from many different vendors. Claude, GPT, Gemini, Llama, Mistral, DeepSeek and a long tail of others, all reached by a namespaced model id like anthropic/claude-3.5-sonnet or google/gemini-2.0-flash. Because it speaks the same OpenAI-compatible wire format as OpenAI itself, FreeITSM reaches all of them through one integration.

One OpenRouter key, and the model field becomes a menu of the entire market.

To make that menu usable rather than overwhelming, the panel fetches OpenRouter's live model catalogue and turns the model field into a searchable list — each entry showing the model id alongside its price per million tokens, in and out. Type to filter, click to pick, or just type any model id you already know. That catalogue is cached for 24 hours in your own database so the picker stays instant, and if a refresh ever fails it quietly falls back to the last good copy rather than showing you an empty list. (No key is needed to read the catalogue — it's public — so the dropdown works before you've even pasted credentials.)

The practical effect is that you are no longer locked to one vendor's roadmap or pricing. Want to A/B a cheaper model on ticket cleanup? Change one field. Want to try the newest frontier model the week it lands? It's in the list. FreeITSM also tags its requests so they show up attributed to FreeITSM on your OpenRouter dashboard, which makes spend easy to read.

Three: keeping the keys safe

An AI-provider panel is, fundamentally, a place where people paste secrets. So three things matter more than the features.

1
Encrypted at rest, never returned. Every API key is encrypted in your own database with AES-256-GCM. The browser never receives a key back — the settings panel only ever shows a mask and a "a key is saved" flag. The plaintext exists only for the moment a request is actually sent to the provider.
2
Saving can't silently wipe a key. A classic footgun: you change the model, hit Save, and accidentally blank the stored key because the field was showing a mask. The save logic treats the masked-or-empty value as "no change", so you can re-save provider and model all day without ever disturbing the key. You only overwrite it by deliberately typing a new one.
3
The namespace registry is an allowlist. The generic save/load endpoints only ever read or write settings keys derived from a registered namespace. They can't be coaxed into reading or overwriting some other secret in your settings table — an unregistered namespace is simply rejected. The convenience layer and the security boundary are the same list.

There's also a pragmatic SSL-verify dial. Outbound calls verify the provider's certificate by default, as they should in production. But the per-feature toggle is ANDed with a global kill switch, so a developer on a box without a CA bundle can switch verification off in one place without weakening anything in production. And every outbound call retries with exponential backoff on rate-limits, 5xx errors and network blips, so a momentary hiccup at the provider doesn't surface as a failed summary.

Four: the part left deliberately untouched

Two of these features — the Forms builder and the Tickets reply cleanup — stream their output. The text appears live, word by word, the way the Claude app does it. That streaming engine is some of the most carefully-tuned code in the project, and the golden rule of the rollout was: don't touch what already works.

So the provider branch is surgical. When the chosen provider is Anthropic, those features keep using the existing, proven streaming path — not a line of it changed. When the provider is OpenAI or OpenRouter, the request goes through the new shared client and the result is delivered as a single update instead. You gain the provider choice on streaming features without putting the streaming experience at risk for the people already happy with it.

The RFP Builder — which leans hardest on long, live-streamed generations — was held back from this round on purpose. Switching its provider properly means building a real streaming path for OpenRouter first, and that deserves its own careful pass rather than being rushed in alongside everything else.

Five: the detail that nearly didn't make it

One small story, because it's the kind of thing that separates "shipped" from "shipped well". The first version of the model picker used a native browser <datalist>. Fine for the short curated lists — but OpenRouter's catalogue is hundreds of entries, and when that long popup opened, the browser tried to scroll the page to fit it. On a settings page whose body is deliberately locked against scrolling, that shoved the top navigation clean off the screen, with no obvious way back. Most visible, naturally, on the very page people would use most: Tickets settings.

The fix was to throw out the native control and build a self-contained dropdown inside the panel — its own capped, scrollable menu that can never move the page, with the same type-to-filter behaviour and still accepting any model id you type. It's invisible when it works, which is the point. A long list shouldn't be allowed to break the page around it.

What this earns the right to do

Most software either picks one AI vendor and welds itself to them, or bolts on a half-hearted "you can change the API URL" setting that only a developer could love. FreeITSM now has a proper provider layer: a clean choice of three, hundreds of models a click away through OpenRouter, keys handled like the secrets they are, and a single building block so the next AI feature inherits all of it for free.

Bring your own key, bring your own model, keep it all on your own server. That's the version of AI features a free, self-hosted ITSM ought to ship — and now every part of FreeITSM that's clever speaks it the same way.