Capxul · Delivery · 7 September 2026
Last week’s update described a new way of building: capabilities go into the SDK first, then the app picks them up and gets simpler each time. That rhythm moved five of the eight capabilities into the product. Since Friday the work has carried on at the same pace — it has simply stopped producing anything on a screen. Two assumptions underneath the money system turned out to be wrong, and they are worth more than a sixth feature.
Last Monday four capabilities had been built the new way, and two of those had reached the product. The hardest one had not moved at all. Sending money existed in the SDK and appeared in none of the app — Quick Pay still ran on code the app had written for itself months earlier, and four other pieces of work were queued behind it.
That is the one that came unstuck, and the rest followed.
The send capability lands. A pay run settles on staging for real — two payments, both proven on-chain.
Organisations get the contacts screen people already had. Payout addresses stop being one fixed field.
Quick Pay and personal Send both move onto the shared flow. The app deletes its own payment code.
Onboarding collapses into a single step. Then the visible work stops and the rebuild starts.
The published SDK went through two major versions in that time. That sounds alarming and is the opposite — we publish a major version when a capability replaces something older rather than sitting beside it. Two majors means two old ways of doing things stopped existing.
Last week this section showed contacts. This week it is the payment flow, for the same reason: the shape of it is the argument. This is one organisation paying somebody, as a developer writes it.
// Everything about sending money. Draws nothing.
<CapxulSendMoney actor={org} onSent={toast} onFailed={showError}>
// Who is being paid — a handle, a contact, or an address.
<CapxulSendMoney.Recipient>
{({ resolved, setHandle, isChecking }) => (
<YourRecipientField onChange={setHandle} name={resolved?.name} busy={isChecking} />
)}
</CapxulSendMoney.Recipient>
// How much, against what this account actually holds.
<CapxulSendMoney.Amount>
{({ value, setValue, available }) => (
<YourAmountInput value={value} onChange={setValue} max={available} />
)}
</CapxulSendMoney.Amount>
// The button. Off until this person is allowed to spend this much.
<CapxulSendMoney.Actions>
{({ send, canSend, isSending }) => (
<YourPayButton onClick={send} disabled={!canSend} busy={isSending} />
)}
</CapxulSendMoney.Actions>
</CapxulSendMoney>
Three things are true there, and they are why this one mattered more than the others.
Change actor from an organisation to a person and this is the personal
Send screen. There is no second implementation to keep in step. Until this week there
were two, and they had already started to differ.
canSend is false until the real answer arrives. Whether
somebody may spend this much is decided in one place, against a budget, and a screen
cannot forget to ask.
Adopting this deleted the hand-written payment machinery Capxul had been carrying. Same rule as last week: the app shrinks each time it picks a capability up.
The same eight as last week, in the same order, so you can see what moved — and what each one honestly does and does not do yet.
An address book where a person stays the same person after they sign up.
→ organisations now have the screen people already hadStanding, permissions and budgets, answered in one place instead of guessed per screen.
→ a 240-line hand-written guard deleted from the appThe money feed — filters, notes, receipts, and a payslip for the person who was paid.
→ was “next release”; now live in both seatsOne send flow serving both a person and an organisation. Works today, both seats.
→ was “building”; the stuck one, now unstuckGroups, pay runs, payslips, and amounts filled in from what each person is owed. A run pays now, when somebody presses the button.
→ was “to design”; a real run settled on staging, 2 Sep × scheduled runs, streaming pay and batch pay do not exist yetIssue and settle, and a payment link for the person being billed. First one back on the bench when the pipeline reopens.
Two small things: showing your Capxul deposit address so somebody outside can send you money, and storing an external address to send money out to. Both exist and mostly work.
× a copied address does not prove money arrived — that is the indexer, below × editing a saved address removes it then re-adds it, which can lose itAn organisation can already bill a person — issue an invoice or a payment request to their email, including to somebody who has not joined Capxul yet. That request is real: it exists, it is addressed to them, and it can be listed, approved or declined. The half that is missing is theirs. A person signs in and has nowhere to see what they have been asked to pay. Billing works; being billed has no screen.
× approving a request currently only prepares the payment — it does not make one, and there is no recovery if it fails × last week I said everything behind this already existed. That was wrongLast week’s update said we would not start a fifth capability while four were in flight. Four finished, so a fifth started, and finished too. The rule held and it is the reason the week worked.
Three came out of doing those five properly. The fourth is not code at all.
People and organisations now share a single set of handles. A handle can be paid, be the target of a request, and identify the same party across the product. Before this, one person could exist as several unrelated records.
A payout address can be saved on any chain we support, instead of one fixed field that only ever held one. The old single-address idea is retired outright.
Photo, logo and payout addresses now travel with the account as it is created, rather than several round trips afterwards. Username availability answers while you type instead of after you submit.
Our first external payout partner has approved us as a business. That is the gate that lets Capxul send and receive real bank payments. It is approval, not integration — the build starts now, and can run in parallel with the rebuild below.
It is worth being precise, because “nothing shipped” would be wrong. Work has shipped every day since Friday. What stopped is work that shows up on a screen.
Everything below is being built now, and none of it changes what a user sees. That is exactly why it is the right week to do it.
The money core was built around a single token, and “USD” in the system quietly meant that token. That is not how anybody actually holds money, and it is not how a payout partner settles.
So we are going back to something more conventional: separate accounts per stablecoin, and the ability to hold and be paid in more than one token. An amount then means that amount — whichever token or currency carries it. That is also the foundation underneath bank payouts and fees.
Contract agreed, test assets deployed, build under way.An indexer is the thing that watches the blockchain and turns it into records we can read. We hand-rolled ours. With one token and a handful of events on chain that was the right call, and we deliberately kept it small because we did not want to pay for anything extra.
There is a lot more on chain now, and the trade-offs have inverted. Reading the chain directly costs money per call, and a hand-rolled reader makes far more of those calls than a real indexer does — those tools exist for a reason. We will have to pay for a blockchain provider at go-live regardless; now that everything runs on Railway, running a proper indexer is cheaper than the thing we wrote.
So we are self-hosting Ponder, an open-source indexer, alongside the rest of the stack. The gap that matters most: our own reader could not properly record money arriving into Capxul from outside. That is the missing half of Receive.
The reader, the connection and payment tracking are done.Until recently, when something went wrong the honest answer was often it failed and we are not certain why. Reasons were discarded as a failure travelled through the system, and everything arrived on screen as one generic error.
We are investing heavily in PostHog to close that. Every action now writes a record of who did it and how it ended; every read of the chain leaves a trace; and a failure carries its real reason the whole way. When something breaks we can say what broke and at which stage — and the same instrumentation shows us where things are slow, not only where they are broken.
On top of that, the nine journeys that matter — create an organisation, recover a broken wallet, send a payment, settle it, settle it again when the receipt arrives late — now run themselves in a real browser against real staging. No more taking our own word for it.
All nine ran and passed yesterday. Gaps remain and are being filled.Turning that on surfaced around forty real failures the platform had been quietly swallowing. They were always happening. We simply could not see them.
The rebuild is being built as a release candidate: a complete, versioned build of the whole platform that lives on its own track, with its own staging environment, entirely separate from the version a customer would use.
That is why none of it is visible. It is not sitting unfinished — it is finished, published and running, just not on the copy anyone else touches. The version customers would see has not moved since Friday and will not move until the nine journeys pass against the candidate. Then the whole thing lands in one piece rather than in nervous fragments.
Sixty-seven pieces of work make up that candidate across the three lanes above. Twenty-seven were finished between Friday and last night. All of it is going to land.
Real defects, found in two days by the instrumentation and the journeys above. Each would have survived a demo and then embarrassed us in front of somebody real.
Reads meant for “the current person” were filed under a fixed label. Change account and, for a moment, you could be shown what the previous account was allowed to do.
The single most important moment in the product — a payment landing — was recorded nowhere we could count, chart or alert on.
The field everything reads to decide whether a payment is done was defined, and read, and never actually filled in.
A payment that ran out of time and was never re-sent sat in the feed as pending indefinitely, with nothing to move it on.
Twenty-five were filed on Sunday and twenty-five closed the same day. A hardening pass that finds nothing is a hardening pass that is not looking properly.
The candidate needs roughly another day or two. Telemetry and proof are furthest along and gate the other two lanes, so they finish first; money and the indexer then build against journeys that already check themselves.
Visible capability work restarts around Wednesday, with invoices first. The Chainrails integration can start in parallel now that KYB has passed — possibly over the weekend — and that is what turns “send money” into “send money to a bank account”.
Two of the eight are still untouched, one is half-built, and none of them is blocked by anything except our own order of work. We are not starting a sixth while the fifth is being proved — the same discipline that made these two weeks work, and whose absence cost us three weeks in August.