Skip to content

Internal Apps

Describe the screen. Skopx already has the data.

Most app builders can reach your data and then design the screen from column names and types alone. Skopx runs the query first, measures what actually came back, and designs for that. Which is why the first draft fits your business instead of a guess about it.

Nearly 1,000 live integrationsBuilt from one sentence in chatRead-only to your databasesEvery write asks first

The layer nobody talks about

Your database stores what you decided. Your tools are where you decided it.

A record says the deal renewed. It does not say the customer threatened to walk over payment terms, that someone agreed to net-60 in a reply, or that the exception never made it into any system. That is not an edge case. That is most of what a company knows, and it lives in the movement between tools, where no single tool owns it.

One renewalFive systems
StripeInvoice 4471, $18,400, due Mar 14A record
HubSpotStage: Negotiation. Owner: Dana.A record
Slack, #renewals"they want net-60 or they walk"Six days ago
Gmail"confirming we can do net-60 this year"Sent four days ago
Zendesk3 open tickets, 1 escalatedStill open

Two of those are records. The other three are what actually happened, and no screen in your company shows all five at once. Skopx is already connected to all five, because that is what it has been doing for everything else. An internal app is the first time you get to see them together, and each panel reads its own system at the moment you open the page rather than serving you a copy taken last night.

Why the first draft fits

Most builders design from your schema. Skopx designs from your data.

When an AI builder generates a screen, it is usually working from column names and types alone. Retool describes its own approach plainly: the model “works with schemas, column names, and data types, not the records themselves.” Skopx makes the same call about the result set: 3,412 records never reach the model. The difference is what it sends instead of nothing.

What a schema tells you

account_name text

owner text

status text

mrr numeric

created_at timestamp

Five names and five types. Enough to write a query. Not enough to know that one of these columns will destroy a bar chart and another is mostly empty.

What Skopx measured

3,412 rows

account_name ~53 chars, all unique

owner 61% empty

status 6 distinct, repeating

mrr currency, 0 to 84,000

Measurements, plus a single example row so the model can see the shape of a value. Never the result set. Enough to design correctly, and a rounding error next to handing over the data.

And these are the conclusions it hands itself

"account_name" averages 53 characters. Never use it as a chart x-axis, the labels overlap and become unreadable. Use it as a table column or a list title instead.

"owner" is 61% empty. Avoid building a metric or chart on it.

"status" has 6 distinct values (open, pending, blocked, waiting, closed, ...). It is a good select filter, and a good x-axis or grouping column.

"created_at" is a date and "mrr" is numeric, so a time series (line chart) is available.

Not a paraphrase for this page. That is the literal text Skopx writes for itself before it draws a single panel.

Then it checks its own work

When the runtime overrules the model, it says so on the chart.

A definition is authored against a profile taken before the app was saved, and the shape of a query drifts. So the renderer measures the rows again at paint time and treats the requested chart as an intent rather than an instruction. A chart quietly showing 25 of 340 rows is worse than one that looks crowded, so every override is captioned in the open.

Shown horizontally so the labels fit

The x labels averaged over 24 characters, so a vertical bar chart would have stacked them on their sides.

Same value in all 340 rows, so a chart would be flat

Every row carried the identical number. One figure says it; a chart would have drawn a straight line.

Shown as a table because there is no numeric column to chart

Nothing in the result held a number. A chart here would have plotted zeroes and looked like an answer.

Labels shortened, hover to read them in full

The label budget shrinks as bars multiply: 20 characters up to six bars, 14 up to twelve, 10 beyond.

Samples

Five consoles. One sentence each.

Each of these began as a single request typed into chat. The tools underneath are the ones the company had already connected for everything else.

Everything waiting on someone. And exactly how long it has waited.

Asked for: What is waiting on an approval right now, who is sitting on it, and what is it worth?

Approval queueReads Slack, Gmail, NetSuite

12

Awaiting approval

$84k

Value held up

4

Waiting over 3 days

Held up by type

Vendor contracts$38k
Expenses$31k
Discount requests$15k
RequestAmountWaitingApproverAction
Datadog renewal$22,0006 daysMartaNudge approver
Q2 contractor invoice$9,4004 daysUnassignedNudge approver
Enterprise discount, Acme$15,0002 daysDanaNudge approver

Every figure reads live from the tool it came from. Every button acts through that same tool, on a click, after it asks.

How it works

Four steps, and none of them are drawing.

01

Your tools are already connected

Slack, the inbox, the CRM, billing, tickets, a database. You connected them for everything else Skopx does, and that is the part that actually takes time. An internal app inherits it.

02

You describe the screen in a sentence

Not a schema, not a form builder, not a canvas. You say what it should show, the way you would ask a colleague to pull it together.

03

It measures, then writes a definition

Skopx runs the draft query, measures what came back, and authors a declarative definition its own runtime renders. There is no generated app with its own database, its own auth and its own holes.

04

You correct it in the same chat

Tell it what is wrong in plain words and the console repaints in place. Every save keeps a version you can restore.

Who sees what

A shared console is not a shared login.

An app reads from the systems that run your company and its buttons write back into them. That is worth taking seriously, so the parts that could go wrong are constrained by the runtime rather than by good intentions.

Read-only, checked twice

A query must be a single statement starting with SELECT or WITH. The check runs again the moment before execution, not only when the app was saved, because a definition can be edited through the API or restored from an older version.

Values never touch the SQL text

Filter and reader values arrive as bound parameters. If a connector cannot carry parameters, the query is refused rather than served an assembled statement. A standalone test compiles that module alone and asserts a hostile value never reaches the query text.

Owner data or reader data, on purpose

A shared console runs as its owner by default, so everyone sees the same numbers. Switch it to run as the reader and every panel executes as whoever opened it, against sources they connected themselves.

Every write asks, then is logged

The only write path is a button, on a click, into a connected tool, with a confirmation by default. Each run is written to an append-only log with who clicked it and what it ran.

The log keeps a digest, not your data

Arguments are stored as a sha256 digest rather than in full. Attributing a write is not a reason to keep a second copy of the customer data inside it.

Private, or your organization

Those are the only two states, enforced by a database constraint. An app you cannot reach answers exactly as an app that does not exist, so an id nobody shared never confirms itself.

The honest part

What an internal app is not.

You will hit these in the first week, so you may as well have them now.

It stores nothing

There is no form, no input, no upload and no record creation. An internal app is a view over systems that already hold your data, not a new place to put it.

It does not run while you are away

No schedules, no alerts, no emailed reports. Refresh ticks only while the tab is open, and an action fires only when a person clicks it.

It is not a drag-and-drop builder

There is no canvas to arrange. Every change is a sentence in chat, which is the point, but it is worth knowing before you arrive expecting a designer.

It has no public link

No embeds, no anonymous access, no client-facing portal. Every route requires a signed-in session that owns the app, or belongs to the organization it was shared with.

Charts are bar, line, pie and area

No scatter, no gauges, no maps, no dual axes. The four that read well, chosen and corrected against your actual values.

A board shows status, it does not move it

The kanban view is read-only. Cards cannot be dragged between lanes, because the lane belongs to the system the card came from.

An internal app is a console over systems that already exist. When what you need is a place to keep new records, the honest answer is that this is not it.

Internal Apps

A builder that designs before it looks is just drawing.

The screen was never the hard part. Being connected to everything the screen needs to show is the hard part, and that connection was already made before anyone asked for an app.