A blank canvas is a question
This started with an uncomfortable observation about our own product: you still need to be technical to build a workflow here.
That is a hard thing to notice about a tool whose marketing says "no scripts". The claim was true. It was also incomplete, because the honest version was "no scripts, and an analyst", and the second half was not written anywhere.
So before changing anything, we wrote down what it actually took to get one event into the warehouse. Five steps. And the thing that made it worth writing down was not the number.
None of the questions were about their data
Step two asked the person to declare a schema: name every column, and for each one decide whether it is a metric, a dimension, a historical dimension, or the key that identifies an entity across time. Twelve decisions, in a blank form, before seeing a single row of their own data.
Step three dropped them on an empty canvas with a palette of source nodes and no indication of which one was theirs. Step four asked the same modelling questions as step two, again, in a different shape.
Look at what those questions are about. Metric or dimension is a question about our storage model. Which of ten source nodes is a question about our ingestion architecture. That a warehouse needs a loader at the end is a question about our engine. Not one of them is about the customer's data, which is the only thing they arrived with and the only thing they are an expert in.
A blank canvas looks like freedom. It is a question, and we were asking three of them before the first useful one.
Recommend, and then wait
The first fix was to invert step two: instead of asking someone to describe their data, let them paste one event and describe it back to them.
Numbers become metrics. Text becomes dimensions. A timestamp becomes a candidate for the time column. Each proposal carries the reason it was made, next to the actual value that produced it.
The decision that matters most here is what it does not do: it never creates the bucket. It proposes and waits.
Two reasons, and the second is the important one. A wrong schema created silently is expensive to undo, because from that moment there is data written against it. And the confirmation step is where somebody learns the model without being sent to training. They see revenue proposed as a metric beside the number they actually sent, and the classification stops being our vocabulary and starts being obvious.
The endpoint behind it is pure. It takes a sample, returns proposed columns, and writes nothing at all. What writes is still the same create call it always was, with the schema the person confirmed.
The canvas, and the ten doors
Step three was two problems wearing one coat.
The first was the emptiness. A new workflow now opens with a source and a loader already wired together, because the destination of a source node on an empty canvas is always the warehouse. That is a step you can only skip if you already know the architecture, and it is where people got stuck without knowing they were stuck.
The second problem was harder, and we got the shape of it wrong at first. Ten source nodes in a list tell you nothing about which one is yours. cdc_node and db_reader_node are both "a database". webhook_node, http_node and probe_node are all "an API". Choosing between them is, again, a question about us.
So the canvas now offers the shapes people actually have, in their words: something calls us, a database I own, an API we poll, files in a folder. Picking one lays the whole chain out, wired, with every field at its own default. It configures nothing, because the connection and the bucket are decisions about their data, and that half we should never guess.
The part we got wrong
Then came the obvious next question. What about a person who has a file on their computer and nothing else? No FTP server, no database we can follow, no API. Just a CSV.
The proposal on the table was a direct import. Upload the file on the bucket screen, infer the schema from it, load the rows. No workflow, no canvas, no nodes. It was the shortest path from "I have a file" to "I have a dashboard", and it was argued for with some confidence.
It was the wrong answer, and the correction was two sentences: the entry point is always the ingestor, and the file might not even be a CSV, it might be something that has to be worked on.
The proposal had been designed for the happy path. A tidy CSV is the easy case, and a door built around it would have been a second write path into the warehouse that skipped the parser, the transformer, the filter, the validator and the schema check. The first file that was not a tidy CSV would have needed all of those built again, on the other side, in the place where they already existed.
So the file goes through the same door as everything else. Upload it, and it enters the pipeline as raw bytes, exactly like a file fetched over SFTP does, and everything downstream already knows what to do with it. Building the door cost a fraction of building the shortcut, because almost nothing was new.
We do not keep the file
One more decision, and it is the one with a consequence worth explaining rather than advertising.
The uploaded file is never written to disk. Not on the application server, not in object storage, not in the message bus that carries records between pipeline stages. It exists in flight and in one worker's memory, and then it is gone.
The privacy story is the easy part. There is no second copy of a customer's data living somewhere waiting to be deleted, and no upload appears in any retention inventory.
The uncomfortable part is what it costs. An SFTP source can fetch a file again. A webhook can rely on the caller retrying. An upload lost between our acknowledgement and the pipeline cannot be recovered by anybody, because the only copy was in memory.
The rule that resolves it without storing anything: do not answer "accepted" until the batch has actually been handed to the pipeline. The uploader's own copy, on their own disk, is the durable one. Anything short of success is retried by them, and the retry is harmless.
What this forbids is the instinctive shape for uploads, which is to accept the file, answer immediately, and process it afterwards. Here that would turn "we do not store it" into "we lose it silently", and those are very different promises.
What the three barriers had in common
Technically, nothing. One was a form, one was a canvas, one was a list. What they shared is that each of them asked the person to know something about our system before the system would help them with theirs.
Simplicity, it turns out, is not a smaller product. Every one of those nodes still exists, and there are 22 of them. It is the discipline of not making the first question a question about yourself.