What's actually left on your medical card
Corporate medical insurance existed in people's accounts as a name and nothing else. I built the endpoint behind it and wired the cabinet to it, so the page now answers the questions people actually have: what's covered, how much has been spent, and what happens next.
Corporate medical insurance is the policy people use most and understand least. Your employer buys it, you get a card number, and from then on you find out what it covers by turning up at a clinic and hoping.
In the cabinet it wasn't much better. The policy appeared in the list, and that was the end of it. Opening it told you nothing you didn't already know.
So the job was to build the page — and, first, the endpoint that could answer it.
The answers live in two different places
Everything a person wants to know about their medical card is split across two services that don't know about each other.
The first knows who you are on the policy: your card number, your programme, your employer, the contract dates, and — if it's a family policy — the relatives covered alongside you. The second knows what has happened on it: the sum insured, what's been spent, and every visit you've made.
The catch is that the second one won't talk to you until the first one has. It doesn't accept a name or a card number; it wants the internal object id that only the first service can give you. So the endpoint is deliberately sequential: find the person, then, only if that succeeded, ask what they've spent. When nobody is found, the second call never happens.
Whose PINFL is this, exactly
The first service is on the internal network and asks no questions. Hand it a national ID number and it returns that person's name, their employer, and their medical history. Anyone's.
Which makes exactly one design decision non-negotiable: the identity is read from the logged-in session, never from the request. The browser doesn't get to say who it's asking about. It sends no identifier at all — the server looks up the caller's own verified profile and uses that.
It tries the national ID first and the passport only as a fallback, because a national ID never changes and a passport can be reissued. A small thing that stops a card quietly disappearing after someone renews their documents.
The arithmetic that everyone gets wrong
The most interesting part of this work was the money, and it's the part where the obvious approach is wrong.
A medical policy has a total sum insured, and inside it a list of covered risks, each with its own ceiling: outpatient care up to X, dental up to Y, medication up to Z. The intuitive way to show "how much cover do I have" is to add those ceilings up.
Do that and you'll report roughly double what the person actually has. Those per-risk ceilings sit inside the total, not alongside it. They're limits on how much of your pot can go to one category — not extra pots. Sum them and you invent money.
So the figures come from the policy-level totals instead: the sum insured, what's been paid out, what's reserved for visits still being settled, and the remainder — which is the sum insured minus both, floored at zero. Money that's reserved is not money you can spend, so a claim under review reduces what the page says is available. That's honest, and it's what protects someone from planning around a number that's already committed.
The per-risk ceilings still appear, because they matter — you want to know dental has a cap even if your pot is full. They're shown as the limits they are, not added to anything.
Two lists, because zero doesn't mean nothing
Some covered risks come back with a limit of zero. That isn't "not covered" — it's "covered, no monetary cap": an annual check-up, a flu shot.
Printed as a number, "0 sum" reads as worthless, which is the opposite of the truth. So the endpoint returns two lists: risks with a real ceiling, and benefits included without one. Same data, split by what the number means, so the page never has to imply that free care is worthless care.
Names written by whoever typed them
The risk names arrive as free Russian text, typed per corporate programme. Inconsistent punctuation, inconsistent capitalisation, occasional grammar mistakes. Different employers describe the same coverage in different words, and there is no code to match on.
Translating them phrase by phrase produces the worst possible result: half a line in Uzbek and half in Russian. So the matching works on the whole normalised string — the entire wording maps to a set of concepts, each with a proper translation in three languages. A programme nobody has seen before matches nothing and is shown exactly as written, in Russian, untouched. Untranslated beats half-translated.
The page that started answering back
Then the page stopped being read-only. From it you can now request an appointment — region, clinic, specialist, what's wrong, when — and that request goes to the operators who arrange it.
Which raised the obvious question: where does it go afterwards? An appointment you can submit but never see again is a form, not a feature.
So the endpoint returns those too, next to the visits. And they're tied to the card, not to the account that submitted them — because one card covers a family, and a request made for a relative still belongs on that card. Visits already made and appointments still pending end up in one list, newest first, each labelled for what it is.
One request, one store
On the front end all of this is a single call, and the answer is kept in one shared store. Arriving from the policy list costs nothing because the data is already loaded; landing on the page directly fetches once. Anything that needs the card — the page, the appointment form's eligibility check — reads the same copy.
Two rules keep that honest. The store is cleared on logout, because a cache that outlives a session will happily show the previous person's medical history. And the card block is given a hard time limit: it's one section of a page, and it must never be able to hang the whole thing.
Failing in proportion
Not having medical insurance is normal — most people don't. So "no card" isn't an error; it's a successful answer that happens to be empty. The page says so plainly instead of showing a failure.
And when the spending service is unavailable while the identity service is fine, the page still renders: your card, your programme, your dates. The balance block is replaced by a short note saying the figures are temporarily unavailable and the cover itself is unaffected. Losing the numbers should cost you the numbers, not the page.
Why I'm happy with it
The page went from a name on a list to something you can act on: what you're covered for, what's been used, what's left after the claims still in flight, and how to get seen by a doctor.
None of it is stored on our side. Every figure is fetched, reconciled and rendered per request — so what it says today is what's true today.