Skip to content

WorkInternal tool

Claims escalation and task portal

Commure · 2026

An internal application that runs the full lifecycle of a claim escalation, from staging through triage, assignment, review, and close, for a team spread across two timezones. It replaced end-of-day arithmetic done by hand with a report the system generates, and gave taskers the one thing they had never had: the complete history of what had already been tried on a claim.

RetoolPostgreSQLSQLJavaScript

Employer work, described in general terms. The screenshots have all real data blacked out: the tool handles patient information protected under HIPAA, and the rest is restricted. Internal names, counts and vendor names are hidden.

01

The problem

Escalations arrived faster than anyone could track them. Assignment lived in spreadsheets, progress lived in people's heads, and end-of-day SLA reporting was manual arithmetic performed by tired people at the end of a shift, which is exactly when arithmetic goes wrong. Worst of all, a tasker picking up a claim had no way to see what had already been attempted on it, so calls got made twice and the same portal got checked by two people on the same afternoon.

02

The system

  1. Staging

    arrives, unassigned

  2. Triage

    categorised, prioritised

  3. Tasker dashboard

    worked, with history

  4. Site owner QA

    reviewed

  5. Closed

    historical, searchable

Every claim walks this line once. Each stage is explicit rather than implied by a status column, which is what lets a permission, a timestamp, and a queue attach to it.

A five-stage lifecycle

Every claim moves through staging, triage, the tasker dashboard, site-owner QA, and close. Stages are explicit rather than implied by a status column, because an explicit stage is something you can attach permissions, timestamps, and a queue to. Role-based access decides which stages a given person can even see.

SLA tracking as a rolling window

The reporting engine evaluates each ticket against a rolling window (assigned more than fifteen hours ago, worked within the last twelve) rather than against a calendar day. It resolves fractional progress correctly, so a parent ticket with four of ten claims worked reports as four of ten rather than as done or not-done, and formats the result as text a tasker pastes straight into Slack.

Master–detail history

Selecting one or several claims loads their full chronological history into a second table: which portals were visited, which representatives were reached, call reference numbers, and prior rejection reasons. This is the feature that changed how the job felt, and it is nothing more than a well-indexed join surfaced at the right moment.

Bulk actions with guard rails

Leadership can assign, update, or delete across many rows at once. Every bulk control disables itself on an empty selection, so a mis-click cannot reach the database at all.

The tasker dashboard: a claims queue with rows coloured by age, a colour legend, and fields for portal and call remarks. All row data is blacked out.
The tasker queue. Row colour is age: yellow at two days, burgundy at three.
The site owner dashboard: claim and encounter ID inputs, a fetched claims table, owner and context fields, and a staged claims table. All row data is blacked out.
Staging. Claims are fetched by ID, given an owner and context, and queued.
User management: a table of email, name and role, with inputs to add a user. All row data is blacked out.
Users and roles. What each person can see follows their role.
03

Technical decisions

What was chosen, what was chosen against, and what the trade cost.

01
Chose
In-memory JavaScript transformers, using Map dictionaries for lookups.
Instead of
Joining the datasets in the browser with AlaSQL.
Why
The AlaSQL joins rescanned the right-hand array once per left-hand row, which is quadratic work, and the tab froze on a few thousand rows. Building a Map once and looking up by key is a single pass and constant-time per row, which took aggregations from minutes to milliseconds. The cost is that the join logic is now hand-written rather than declarative, so it needs comments a SQL join would not.
02
Chose
Store everything in UTC; convert at the edges with AT TIME ZONE in PostgreSQL and moment-timezone in the UI.
Instead of
Storing local timestamps, which is what everyone reaches for.
Why
The team spans Dhaka and Los Angeles, and the SLA is a rolling window rather than a calendar day, so the two never line up. Local storage is also wrong twice a year: daylight saving means the offset is not a constant, so one hour is duplicated and another does not exist. UTC in the database with conversion at the boundary is the only version that survives March and November.
03
Chose
Partial updates written through COALESCE.
Instead of
Writing the whole row on every save.
Why
Several stages write to the same claim, sometimes close together. A full-row write from a client holding a stale copy silently reverts whatever another stage just set, which is the worst class of bug, because nothing errors and the data simply becomes wrong. COALESCE lets a stage update only the fields it owns and leave every other field untouched.
04
Chose
Disabling bulk controls when the selection is empty.
Instead of
Validating on the server and returning an error.
Why
Server validation is still there, but a control that cannot be pressed is a better guard than an error message. A bulk statement built from an empty selection either fails loudly or, depending on how the predicate is assembled, matches every row, and the second outcome is not one you want to discover in production.
04

The result

End-of-day reporting stopped being arithmetic and became a button. Aged claims that had been invisible became a queue anyone could see, and the history panel meant taskers stopped repeating calls that had already been made. The honest summary is that none of this is clever software. It is ordinary software applied to a process that had been running on spreadsheets and memory, which turns out to be where most of the value in internal tools actually is.