AI · Africa

Why business AI should ask before it acts

A language model should extract facts from a conversation, but it should not decide whether a customer already exists, what a product costs, or when to write to your ledger. Those are database questions with exact answers. Umbra separates them: extract, check deterministically, propose, and execute only on explicit confirmation.

Last updated 2026-09-04. Published by Umbra ERP.

Key facts

Pipeline
Extract, check, propose, execute, in that order and as separate stages
Duplicate check
Exact email, last nine digits of the phone number, fuzzy name score at a 0.72 threshold
Repeat-enquiry check
Open quotes raised for that customer in the last 90 days
Proposal lifetime
24 hours, after which it expires and the checks must be run again
Price source
Re-read from the product record at the moment of execution, never taken from the model
Consent
An explicit confirmation of a specific proposal. Replying "ok" or "thanks" does not count.
Permissions
Re-checked at execution time, not only when the proposal was built

What the autonomous agent pitch is actually proposing

The current marketing position across business software is that an AI agent will read your messages, decide what needs doing, and do it. No approval step, no review queue, no human in the path. Stated as a capability it sounds like the removal of drudgery. Stated as a permission it is different: you are granting write access to your customer master, your pricing and your ledger to a component whose output distribution you cannot inspect and whose behaviour on next month's input you cannot predict from this month's.

That distinction matters because a business system is not a chat window with a database attached. Creating a customer allocates an identity that every future document hangs off. Creating an invoice consumes a number in a sequence, sets a tax point, and enters the receivables ageing. Recording a payment mints a receipt. Each of these is a commitment, and commitments are not symmetrical: creating one is a single call, undoing one is a project.

Language models are genuinely excellent at the part of the job that is linguistic. Reading a rambling WhatsApp thread and pulling out that the customer is Moyo Hardware, that they want 40 bags of cement and 12 sheets of IBR, that delivery is to Ruwa, and that the contact number appears halfway up the thread, is exactly the sort of work they do better than a rules engine ever did. That is extraction, and it is a language problem.

Deciding whether Moyo Hardware is already in your database is not a language problem. It is a set-membership question with an exact answer sitting in a table. The category error at the centre of the autonomous agent pitch is handing the second kind of question to the tool that is good at the first kind, and then being surprised by the failure mode.

Why writes to a ledger are not like writes to a to-do list

Software people reason about writes as reversible by default, because in most applications they are. Create a task, delete the task, nothing remains. Financial records do not behave that way, and three properties explain why.

The first is sequence. Invoice numbers are consecutive and their consecutiveness is itself evidence. A gap invites the question of what was removed and why. In Zimbabwe, where a fiscal tax invoice must be issued within 30 days of the supply and the fiscal record is submitted to a device rather than kept privately, a document that has been fiscalised cannot be quietly withdrawn. The correction is a credit note, which is another document, with its own number and its own trail.

The second is reference. Once an invoice exists, other records point at it: payments, receipts, credit notes, statements, the aged receivables report, and the customer's own accounts payable ledger on the other side. ZIMRA allows input tax to be claimed within 12 months of the date of the invoice, so your customer's tax position may already depend on a document your system generated by accident. Deleting the row does not retract the consequences.

The third is identity. A duplicate customer is not a duplicate row, it is a fork in a history. Two records, each holding part of the truth: some invoices on one, some payments on the other, a credit limit on one and the correct VAT number on the other. Merging them later is not a button, it is a set of judgements about which contradictory field wins, made by somebody who was not present for either transaction. This is the failure that naive automation produces most reliably, so it is worth taking apart properly.

The duplicate customer: the characteristic failure of naive LLM automation

Picture the straightforward implementation. A model is given the conversation and a set of tools, and instructed to create the customer and raise the quote. It calls a search tool, generates a query from the text in front of it, gets no result, and proceeds to create. This is not a bug in the model. It did exactly what it was told, and it had no way to know it was wrong.

The reason it gets no result is that customer identity in the real world is written down inconsistently. The same business appears as Moyo Hardware, Moyo Hardware (Pvt) Ltd, T. Moyo Hardware, and Moyo Hdwe across four conversations with four different members of staff. The same phone number appears as +263 77 123 4567, 077 123 4567, 26377 1234567 and 0771234567. A search generated from one spelling against a table holding another spelling returns nothing, correctly, and a system that treats "nothing found" as "does not exist" then creates a second identity for a customer it already had.

A month later the same thing happens again, because the input varied again. The receivables ledger now holds three customers with one real debt distributed across them. The ageing report splits the balance into three positions, none of which crosses the threshold that would have triggered a collections call. The statement you send is incomplete and the customer knows it. The credit limit on the record you looked at is meaningless because two thirds of the exposure sits elsewhere.

The usual answer to this is to instruct the model more firmly: check carefully for an existing customer before creating one. That does not fix it, and understanding why is the whole argument. The instruction turns the check into a judgement made inside a token stream, with the same variance as every other judgement the model makes. It will be right most of the time. Most of the time is precisely the wrong property, because a check that fails rarely and silently produces a small stream of duplicates that nobody notices until the ledger is a year old and the cleanup is unaffordable. A check that failed loudly and always would have been fixed in week one.

There is a second, sharper property that rules out the model as the checker. Paste the same conversation twice and a language model may reach a different conclusion the second time. For a deduplication mechanism this is disqualifying. The one guarantee a duplicate check must offer is that identical input yields identical matches, because the most common real-world trigger for a duplicate is somebody re-running the same message.

Four stages, deliberately separate

Umbra's intake flow is built as four discrete stages rather than one agent loop. The separation is the design. Each stage has a single responsibility, a different implementation, and a defined output that the next stage consumes, so a failure in one is visible rather than absorbed.

  1. 1. Extract

    The language model reads the pasted conversation, the photographed order or the PDF, and pulls out structured facts: who the customer appears to be, their contact details, what they asked for, quantities, delivery details. This stage produces claims about the text, and nothing else. It writes nothing.

  2. 2. Check

    Deterministic database matching, not a model judgement. Umbra matches an existing customer on exact email, on the last nine digits of the phone number, and on a fuzzy name score with a 0.72 threshold, and it separately looks for open quotes raised for that customer in the last 90 days. The same input always produces the same matches.

  3. 3. Propose

    The extracted facts and the check results are combined into a set of pending actions: create this customer, or attach to that existing one; create a lead; raise a quote with these lines; add these invoice lines; send this follow-up message. The proposal is stored, shown to you, and expires after 24 hours.

  4. 4. Execute

    Only on an explicit confirmation of that specific proposal. At execution the permissions are checked again, an anti-double-execute claim ensures a proposal cannot be run twice, and catalogue prices are re-read from the product record rather than taken from the model.

Nothing here is exotic. It is the shape any careful engineer would give to an operation that mixes an unreliable input with an irreversible output: parse, validate against authoritative state, present, commit. The reason it is unusual in AI products is that it is less impressive in a demo, because there is a human in the middle of it, holding exactly the decision a human should hold.

Why the checking stage must be database matching, not model judgement

The checking stage in Umbra runs three matches against the customer table, and each of the three exists for a specific reason.

Exact email match is the highest-precision signal available. Email addresses are unique in practice, machine-normalised, and rarely mistyped in a way that still parses. When it hits, the answer is not probabilistic. It is the same customer.

Last nine digits of the phone number handles the format problem directly. A Zimbabwean mobile number is written with the country code, without it, with a leading zero, with spaces, with a plus, and occasionally with a hyphen. What varies is the prefix. What does not vary is the subscriber portion at the end. Comparing the final nine digits normalises across every one of those variants without a full international parse and without maintaining a numbering-plan database per country, which for a product carrying legal entity types for 52 countries is a meaningful simplification rather than a shortcut.

The fuzzy name score with a 0.72 threshold covers the residue, where the email is absent and the number was typed differently. The important property is not the algorithm, it is the number. 0.72 is a stated, inspectable, tunable constant. You can run it over a real customer table, count the false positives and false negatives it produces, and move it deliberately with evidence. A model's opinion that two names "look like the same company" cannot be measured, cannot be tuned, and cannot be reproduced, and therefore cannot be improved.

The fourth check is about the situation rather than the identity. Umbra also looks for open quotes raised for that customer in the last 90 days. The case it catches is common and expensive: a customer who enquired three weeks ago, received a quote, went quiet, and has now come back. The naive system raises a second quote, and now two live documents with two numbers and possibly two prices are in the customer's hands. The correct action is usually to revise the existing quote, and a system that surfaces "there is already an open quote from 22 days ago" lets the person make that call in a second.

Taken together, this stage answers a question that has a right answer, using a method that produces the same output for the same input every time. That is the whole distinction. Extraction is inference and belongs to the model. Matching is lookup and belongs to the database.

Prices are read from the catalogue at execution, never from the model

The second predictable failure is subtler than duplication and costs more per occurrence. A conversation contains prices. The salesperson quoted 18 dollars a bag from memory, or the customer typed the price they paid last year, or a figure appears in the thread that was a competitor's number being discussed. A model asked to build a quote from that conversation will faithfully carry those numbers through, because they are in the text and the model's job was to read the text.

The moment that happens, your price list has been bypassed by a chat message. Margin is no longer set by the catalogue, it is set by whatever number was most recently mentioned in a conversation nobody is auditing. This is not a hallucination problem, which is what people expect from AI. It is worse, because the model was accurate: it correctly reported a wrong price.

Umbra never invents a price and never accepts one from the extraction. Catalogue prices are re-read from the product record at the moment of execution. The quote that gets created is priced from your inventory, using the product you confirmed, at the price that is live when the document is written.

The second half of that sentence matters as much as the first. Time passes between a proposal being built and a person confirming it, sometimes minutes, sometimes most of a day. If a price changed in that window, the price that must win is the current one, not the one that was correct when the proposal was drawn up. Reading at execution rather than at proposal makes that automatic. It also means a proposal can display one figure and the executed document can carry another, which looks like an inconsistency until you notice it is the system refusing to sell at a price you have withdrawn.

Why a proposal has to expire

A proposal is not a document, it is an assertion about the state of the world at the moment it was constructed: no matching customer exists, there is no open quote, this product is available at this price, this user is allowed to raise invoices. Every one of those assertions decays.

Umbra gives pending actions a 24-hour lifetime. After that they expire and the checks have to be run again. The failure being prevented is concrete. A proposal built on Monday says no matching customer was found. On Tuesday a colleague creates that customer by hand. On Wednesday somebody opens Monday's proposal and confirms it, and the deduplication that the whole pipeline exists to provide is defeated by a stale assertion. The TTL bounds how far the world can drift from the check that justified the proposal.

A pending action is also a stored intent to write, which makes an unbounded queue of them a standing liability. A shared workstation, a phone left unlocked, an account compromised weeks later: any of these turns a backlog of never-confirmed proposals into a backlog of one-click writes into your ledger. Twenty-four hours keeps that surface small.

Two further guards sit at execution. Permissions are re-checked at that moment rather than trusted from proposal time, so a user whose rights were reduced in between cannot ride an old proposal through the gap. And an anti-double-execute claim ensures a single proposal cannot be executed twice: a double tap on a slow connection, a retried request, or two people working the same shared inbox and both pressing confirm. Without that claim, the most careful pipeline in the world still ends the week with two identical invoices.

What this costs you, and what it buys

The honest cost is the demo. A pipeline that stops and asks cannot show a business running itself while the owner sleeps. Somebody has to look at each proposal and confirm it, which is a real second of real attention, several times a day.

What that second buys is a set of properties that are difficult to retrofit. Every write has an author and a reviewed proposal behind it. Re-running the same input does not produce a second customer. The catalogue remains the only source of price. A stale proposal cannot be executed against a changed world. A confirmation cannot be manufactured by the content being processed. None of these are AI features. They are the ordinary invariants of a financial system, preserved rather than traded away for the appearance of autonomy.

This posture is also where formal guidance has settled. The NIST AI Risk Management Framework, released on 26 January 2023 as voluntary guidance, is organised around four functions, GOVERN, MAP, MEASURE and MANAGE, and exists to help organisations build trustworthiness into how AI systems are designed, used and evaluated rather than assuming it. A pipeline whose checking stage is deterministic and whose write stage requires a human decision is measurable and governable in exactly that sense. An agent loop that writes on its own judgement is neither, because there is no stage at which you can state what it will do.

Within those limits Umbra's assistant does a substantial amount. It reads pasted text, a photographed order or a PDF. It can propose a customer, a lead, a quote, quote lines, invoice lines and a follow-up message. Seven conversational profiles cover sales, finance, CRM, HR, marketing, procurement and inventory, so the same confirm-before-act discipline applies whether you are raising a quote or looking at aged receivables.

And when the requirement genuinely is unattended automation, the right tool is not a language model with write access. It is a program with scoped credentials. Umbra exposes a public REST API across 87 endpoints with per-scope API keys, webhooks on customer, invoice, quote, payment, lead and contract events, and an MCP server with 31 tools. In that arrangement the thing acting is deterministic code you wrote, the permissions are explicit, and the behaviour is the same on Tuesday as it was on Monday. That is what automation should look like. An agent improvising against your ledger is something else.

Frequently asked questions

Can AI create invoices automatically?

It can draft one, and it should not post one unattended. Umbra reads a pasted conversation, checks the details against your existing records, and proposes an invoice or quote with priced lines. Nothing is written to your data until you confirm that specific proposal. Prices come from your product catalogue at the moment of execution, not from the conversation.

Is it safe to let AI access my accounting system?

It depends entirely on whether the AI can write. Reading and summarising is low risk. Writing is not, because ledger entries are numbered, dated and referenced by other records, so undoing one is a correction rather than a deletion. Umbra separates the two: the model proposes, a person confirms, and permissions are re-checked at execution.

What stops the AI creating a duplicate customer?

A deterministic check that runs before anything is proposed. Umbra matches on exact email address, on the last nine digits of the phone number, which normalises across every way a mobile number gets typed, and on a fuzzy name score with a 0.72 threshold. It also flags open quotes raised for that customer in the last 90 days.

Can the AI change my prices?

No. Catalogue prices are re-read from the product record at the moment the document is created, so a price mentioned in a conversation never becomes the price on a quote. This also means that if a price changes between a proposal being drawn up and you confirming it, the document carries the current price.

What happens if I do not respond to a proposal?

It expires after 24 hours and nothing is created. A proposal is an assertion about the state of your data at the moment it was built, and that assertion goes stale. Expiring it forces the duplicate and open-quote checks to be run again against current records rather than confirmed against a picture of last week.

Does replying "ok" confirm a proposal?

No, and this is deliberate. "ok" and "thanks" are not treated as consent. In a chat context those words acknowledge receipt as often as they authorise anything, and because Umbra processes pasted conversations, treating them as consent would let the content being read contain the token that authorises acting on it.

Can it read a photo of a handwritten order?

Yes. The intake flow accepts an attachment as well as pasted text, so a photographed order or a PDF can be read and turned into a proposal. The same four stages apply: the contents are extracted, checked against your existing customers and open quotes, proposed, and written only when you confirm.

What if I actually want unattended automation?

Use the API rather than the assistant. Umbra exposes a REST API across 87 endpoints with per-scope API keys, webhooks on customer, invoice, quote, payment, lead and contract events, and an MCP server with 31 tools. The thing acting is then code you control, with explicit permissions and behaviour that does not vary between runs.

Sources

Regulatory figures on this page are taken from the following primary sources. Tax rates and thresholds change; check the source before relying on a figure.