One change: every operation gets a record
Right now the website ends in WhatsApp and everything after that is done by hand. The new flow keeps WhatsApp as a help channel, but saves each operation in one place so people can check it, bill it and follow it.
Chat and spreadsheets
Clients quote on the website, then data, proof of payment and checks all happen in WhatsApp. Staff retype each order into the Dashboard and again into the invoicing tools.
Web App or chat → database → VIEW → Fact
The client enters data in a Web App, or chats with an AI assistant on WhatsApp or inside the app. A person checks the bank and approves in a VIEW. The Dashboard is not changed. A script (Fact) registers the sale and emails the PDF.
The normal path only
A standard operation from quote to PDF receipt. Special cases and AI come later.
How an operation works now
What gets in the way
- Nothing is saved by the website. Its only result is a WhatsApp message.
- Proof of payment is a screenshot checked by eye, with no record of who decided. That is a fraud risk.
- The same data is typed twice (Dashboard, then KeyFácil or Omni), which causes mistakes.
- There is no defined data structure. The board flags it in red: “dB Schema!!!”.
- Waiting. WhatsApp queues, and the receipt can take up to 3 days.
The business already lives on the phone
Next step: a web app built for the phone first and opened from a WhatsApp link; an Expo app later, once clients come back often.
- ~60 operations a day, mostly by chat (team estimate, not yet measured).
- The website ends in “Enviar por WhatsApp”: the client leaves the site to finish.
- Proof of payment is a phone screenshot: camera and gallery are already the upload step.
- Short support hours (Mon–Fri 7–18, Sat 8–14, Sun 12–14): outside them, only self-service works.
- WhatsApp reaches ~91% of Brazilian internet users aged 16–64 (GWI 2020 via Verint; dated, no Peru figure found).
- No install: a link opens the quote in seconds.
- One codebase fits the four-day MVP; the chat assistant and the form share one service.
- The app earns its place with weekly use: push alerts, camera, biometric login. The repo root is already reserved for Expo.
What Kambista does that we can borrow
Kambista is a Peruvian exchange app. I walked through an exchange on a real phone: everything happens inside the app, in three steps. Full walkthrough.
- Ask for the same three things: bank, destination account and source of funds.
- Show where to pay, with copy buttons and a reminder to use the right currency account.
- Ask for the operation number and proof, then show a status timeline.
- Keep saved accounts so returning clients do not retype them.
Who does what, and in which system
The whiteboard flow, drawn with its actors. Blue boxes are what we build, purple boxes are the AI chat assistant, the grey box is the Dashboard that stays as it is, and the dashed purple piece is optional AI (use the switch to hide it).
Show the original whiteboard
| # | Who | What happens |
|---|---|---|
| 1 | Client | Quotes with the current exchange rate (e.g. 1 BRL = 0.689 PEN on the free API; Brasper sets its own), in one of three ways: the Web App form, a WhatsApp chat, or the assistant inside the Web App (the same assistant as on WhatsApp); or by talking to a human seller, as today (large or negotiated amounts). See What people see. |
| 2 | Client | Gives the source of funds, source account, operation number and destination account, pays, and sends the proof photo, by form or by chat. All three ways save the same data in the database, one record per sale (“IV”, Intención de Venta). |
| 3 | Validator | Sees the pending operations in the VIEW, checks the bank (target about 15 minutes) and approves. |
| 4 | Operator | Marks the approved operations in the VIEW and exports a file of orders. |
| 5 | Operator | Imports that file into the Dashboard. The Dashboard is not changed: it only imports and exports orders. |
| 6 | Billing person | Exports the orders from the Dashboard and imports the file into Fact, which runs the whole set. |
| 7 | Fact | Registers each sale with the tax authority (SUNAT through KeyFácil, the Brazilian equivalent through Omni) and keeps the official XML. See Billing. |
| 8 | Fact | Builds the PDF from that XML and emails it to the client. The result goes back to the database and shows in the VIEW. |
The validator, the operator and the billing person may be the same people; the team should confirm (see Questions).
The client’s Web App and the team’s VIEW
Client: three new ways in, and the current human route
The first three save the same data in the same database, so the team always works from one VIEW. The chat is a core way in, with the AI assistant doing the talking. It is one assistant service behind both chat channels: the WhatsApp chat and the assistant inside the Web App run the same steps and save the same data, and only the channel differs. The assistant never approves anything: the validator does. The fourth is how it works today: a human seller attends the client and negotiates the rate before the operation is made. It keeps working as it is, and the assistant can hand a client over to it at any time.
1 · Web App form
Same calculator as the site. The help buttons jump to the other ways in (tap them to see each flow).
The fields from the whiteboard: source of funds, source account, destination account.
Where to pay, with copy buttons and a reminder to use the right currency account.
del comprobante
The operation number and the proof photo go straight to the database.
- Operación registradahora
- Verificando tu transferencia≈ 15 min
- Enviamos tu dinero
- Recibiste tu dinero
A simple timeline. The PDF receipt arrives by email.
2 · WhatsApp chat
The “Enviar por WhatsApp” button on the site opens this same chat. The quick replies let the client switch to another way in.
One question at a time, the same fields as the form.
Titular: BrasPer
Banco: {banco} · Cuenta: {cuenta}
Usa la cuenta de BRL. Tasa asegurada hasta las {hora}.
The photo and number go to the database; the assistant does not judge the proof.
💸 Enviamos tu dinero a la cuenta •••9956.
Status comes from the database. Problems go to a person with the context attached.
3 · Assistant inside the app
The calculator offers the form or the assistant. This is the same assistant and flow as the WhatsApp chat, in a different channel. Other ways in are one tap away.
The rate card always comes from the system, never from the assistant’s own words. “Hablar con una persona” is always there.
500 BRL → — PEN · Origen: Ahorros ✓
Destino: pendiente
One question at a time. The summary on top fills in as you answer.
It ends on the same review screen as the form, then moves on to payment.
The assistant stays one tap away on every screen, with the operation as context.
The photo and number go to the database; the assistant does not judge the proof.
- Operación registrada{hora}
- Verificando tu transferencia≈ 15 min
- Enviamos tu dinero
- Recibiste tu dinero
“Where is my order?” is answered from the database, so it matches the timeline in the form.
Same hand-over as on WhatsApp. From here it is the current human flow, shown in 4 below.
4 · Human attention (current)
This flow already works in the business: a human seller attends the client and negotiates the exchange before it is made. It is how large or negotiated amounts get done today. Example: a client who wants to change 5,000 USD, a long queue and many messages.
Gracias por escribir 🙌 Tienes 23 personas antes que tú. Un asesor te atenderá en orden.
Posición #11 · ≈ 25 min.
Today a client with 5,000 USD queues like everyone else. The numbers are examples. The new flow lets the client leave the queue for the assistant or the form at any time.
The seller’s side: a long queue and many unread messages, handled one by one, mostly by hand.
The client and the seller go back and forth until they agree on the rate. Many messages, all typed by people.
Titular: BrasPer
Banco: {banco} · Cuenta: {cuenta}
Cuenta de USD. Asegurada hasta las {hora}.
The seller collects the data and the proof photo by chat. Nothing is saved in a database; it only lives in the conversation.
Same data typed twice by hand, then the receipt can take up to 3 days. This is the part the new flow records once.
Team: the VIEW
| ID | Cliente | Envía → Recibe | Origen de fondos | N° trans. | Foto | Estado | |
|---|---|---|---|---|---|---|---|
| ☐ | BR-0001 | Cliente A | BRL → PEN | Ahorros | 000001 | 📎 | Pendiente |
| ☐ | BR-0002 | Cliente B | PEN → BRL | Venta de bien | 000002 | 📎 | En validación |
| ☑ | BR-0003 | Cliente C | USD → BRL | Ahorros | 000003 | 📎 | Aprobada |
| ☑ | BR-0004 | Cliente D | BRL → USD | Donación | 000004 | 📎 | Aprobada |
| ☐ | BR-0005 | Cliente E | BRL → PEN | Ahorros | 000005 | 📎 | Exportada |
The VIEW replaces searching through chats. The validator checks the photo and the bank and sets the status; the operator marks and exports; billing results appear here too. Sample rows are made up.
Where the rates come from, and what “17:18” means
Two numbers: the Brasper rate is what the client pays and is set by the business; the market reference comes from a free API and is used to check it and as a fallback.
| Free API (tested 11 Oct 2026) | BRL | PEN | USD | Refresh |
|---|---|---|---|---|
| open.er-api.com use No key, CORS open, attribution required | ✔ | ✔ | ✔ | Daily; gives last and next update time |
| fawazahmed0 currency-api backup | ✔ | ✔ | ✔ | Daily |
| Frankfurter no | ✔ | ✘ | ✔ | No PEN, so it cannot serve the main route |
A daily API cannot give an intraday time, so the time comes from Brasper’s own rate refresh (a server cache, not a table): next refresh = last refresh + interval.
The rate pressed at “Continuar” is frozen in the order (rate). If no rate loads, show “Tasa no disponible, habla con una persona”, never 0. Show Lima time (UTC-5).
Loading from open.er-api.com…
One table for orders, one for the Fact script
The team’s board (Operaciones > Contabilidad) is already one row per order. The MVP keeps only what is needed to validate, bill and send the PDF. Everything else (discounts, second proof, channel, rate history) waits.
orders VIEW and Web App
| Field | Why |
|---|---|
order_id PK, unique | Identity of the order everywhere |
created_at (UTC) | Order and invoice date |
status | pending, approved, exported |
client_name, doc_type, doc_number, email | Invoice client and PDF delivery |
destination_account | Bank + currency, e.g. “Nubank (BRL)” |
send_amount, send_currency | What the client pays |
receive_amount, receive_currency | What arrives |
rate | Rate frozen at quote (e.g. 1.425, 0.66) |
commission, tax | Taxable value and 18% tax (board: comisión, impuesto). Total = commission + tax |
funds_source, operation_no | Compliance data the client gives |
payment_proof_url | The validator’s evidence |
validated_by, validated_at | Who approved and when |
invoices written only by Fact
| Field | Why |
|---|---|
order_id PK, unique, = orders.order_id | Idempotency: one invoice per order, ever |
series, number unique together | e.g. B001-00000003, no repeats |
status | reserved, sent, accepted, rejected |
provider_message | Why a rejection happened |
xml_url, pdf_url | Official XML and the PDF built from it |
emailed_at | Proves the PDF was sent |
order_id before doing anything. No row: reserve a number and send. accepted: skip. sent: ask the provider for the result, do not send again. rejected: retry only after a fix. Re-importing the same file, or running it twice, never makes a second invoice.Left out on purpose: issuer (derived from the send currency: PEN → INGENITECH S.A.C. in S/, BRL → Brasper 21 in R$), document type and environment (Fact settings), IGV rate (18% setting), discount % (already inside the commission), second proof and channel.
From imported orders to a PDF in the client’s inbox
A person imports a set of orders into Fact. For each order, Fact registers the sale with the tax authority, saves the official XML it gets back, builds the PDF from that XML and emails it. This replaces today’s manual billing steps: re-check, register, consolidate, email.
| # | Step | In plain words |
|---|---|---|
| 1 | Check the batch | Fact reads the file and checks each order: ID number format, email, amounts that add up, not already invoiced. A bad order is set aside and the rest continue. Importing the same file twice is safe. |
| 2 | Register the sale | For each order Fact asks the invoicing service to register it: KeyFácil for Peru→Brazil (which reports to SUNAT) and Omni for Brazil→Peru (which reports to the Brazilian tax authority). The answer is accepted or rejected, and the service sends back an XML, the official electronic record. |
| 3 | Save the XML | Stored exactly as received and never edited. |
| 4 | Create the PDF | Built from that XML, so it always shows what was registered. Fact compares the totals before saving it. |
| 5 | Send | Emailed to the client only after the tax authority has accepted the sale. |
| 6 | Report | The result of every order (invoice number or error) is saved and shown in the VIEW, and the daily consolidated file is produced. The Dashboard is not involved. |
What Fact receives for each order
One orders row per order, exactly the fields of the first table, plus fixed settings (issuer by currency, 18% tax, document type). That is the whole input. Modeled on the boleta detail the team uses today (B001-00000003, provider APISUNAT):
{
"order_id": "BR-0003", // unique, idempotency key
"created_at": "2026-10-10T17:26:00Z", // UTC; shown in Lima time
"client": { "name": "MICHAEL CHECASACA MUÑOZ", "doc_type": "DNI",
"doc_number": "48602704", "email": "client@example.com" },
"commission": 11.98, "tax": 2.16, // total 14.14 = 11.98 + 2.16
"send_currency": "PEN" // → issuer INGENITECH S.A.C., S/
}
Fact checks total = commission + tax before sending, and writes back one invoices row. The screenshot names the provider APISUNAT while the diagram says KeyFácil: confirm which one Peru uses.
If something goes wrong
| Case | What happens |
|---|---|
| The order data is invalid | It is set aside and listed in the report. A person fixes it at the source (VIEW or database) and exports it again. |
| The tax authority rejects it | It is marked with the reason, with no automatic retry. A person corrects the data and resubmits. |
| The service is down or slow | Fact retries. Before resending it asks the service whether the first attempt went through, so a sale is never registered twice. |
| The email bounces | The team is alerted and the PDF stays available in the VIEW. |
- Peru: the signed XML is what SUNAT receives. The printed version must show a QR code and a hash, with the minimum content set by SUNAT (Res. 340-2017; I did not verify the exact field list).
- Brazil: the authorized XML is the only fiscal document. The PDF (DANFSe) has no fiscal validity (overview).
Operation ID, direction (PE→BR or BR→PE), document type, the client’s ID type and number (DNI, RUC, CE, passport, CPF or CNPJ), name, email, a description and amount, and the tax code. The accountant defines which amount is billed and every tax field; Fact checks and sends them, it does not decide tax treatment.
The chat assistant, and one optional extra
The chat assistant is part of the core flow. The rejection explainer is optional and experimental. I kept only AI that similar companies already use, and a person always decides: AI never approves a payment or changes tax data. Most sources are company write-ups, so their claims are unaudited.
| What it does | Seen in similar companies | Limits | |
|---|---|---|---|
| A1 | Chat assistant (core). One service, two channels: talks with the client on WhatsApp and inside the Web App with the same steps, asks one question at a time, fills in the same data as the form, collects the proof photo and operation number, answers “where is my order?”, and hands over to a person when asked or when unsure. | Félix Pago sends remittances through a WhatsApp chatbot (Stripe case study). Rextie, a Peruvian exchange house, has a chatbot, “Lucía” (Andina, undated). | Automating WhatsApp needs the official WhatsApp business service; the site only opens a plain chat link today. Rates, fees and account numbers always come from the system, never from the assistant’s own words. |
| A2 | Rejection explainer (in Fact, optional). Says in plain words why the tax authority rejected an order and what to check. | None found: experimental. The problem is real: some Brazilian rejections are generic, for example code E8214 (Omie help). | Build it only if real rejections show it is needed. It never edits tax data. |
Left out because I found no similar company using them: reading voucher photos automatically, a triage assistant for exceptions, asking the database questions in plain language, and a custom fraud model.
What we can ship in four days
The normal path only: an operation from quote to PDF receipt. Special cases and AI come later.
- Web App: quote, data, pay, upload proof, status.
- A database that keeps everything: clients and accounts, quotes, operations, proof photos, who validated and when, billing batches and invoices.
- The chat assistant: one service on WhatsApp and inside the Web App (same flow in both), for quote, data, proof and status, saving the same data as the form. Until the official WhatsApp account is ready, use a test number or Telegram.
- The VIEW: see the photo, set the status, mark and export orders.
- The Dashboard imports that file as today, with no change.
- Fact: imports the set, registers each sale in test mode, saves the XML, builds the PDF and emails it.
- Basics: staff login, private storage for photos, no secrets in public code.
- The optional rejection explainer (A2).
- Special cases: partial payments, payments made by someone else, wrong currency, refunds.
- More channels beyond WhatsApp (and Telegram as a fallback), rate tiers and coupons.
Suggested four-day path proposal
Day 1 · Agree and start
- Confirm what the database keeps
- Get a sample Dashboard file, test access to KeyFácil and Omni, and a WhatsApp number
- Start the Web App screens
Day 2 · Database and VIEW
- Save operations
- Build the VIEW: status, mark, export
- Start the chat assistant: quote and data
Day 3 · Fact
- Read the file, register in test mode
- Save the XML, build the PDF, email it
- Assistant: proof and status
Day 4 · Test and demo
- Run sample orders from start to end
- Fix, then prepare the demo
Needed before Day 1
- A sample import and export file from the Dashboard. Its format is the contract for the VIEW and for Fact, because the Dashboard cannot be changed.
- Test access or API keys for KeyFácil and Omni, ideally returning the XML. If they have none, Fact produces the files these tools already accept: one manual upload per batch, but no retyping.
- The accountant’s tax fields and what is billed.
- A WhatsApp business account, or a test number to start with.
- Who validates, who exports and imports.
How we will know it works
| Measure | Today | Target |
|---|---|---|
| Time to receipt | Up to 3 days | 20 minutes from confirmation |
| Typing | Same data typed twice | Typed once, by the client |
| Payment checks | By eye from chat screenshots, no record | A person checks the bank in the VIEW, with a record of who and when |
| People involved | 7 | About 3 (the team’s estimate; to re-estimate because validation stays with a person) |
Questions that block or shape the MVP
- What file format does the Dashboard import and export (type and columns)? A sample would settle it.
- Do KeyFácil and Omni offer an API and test access? Who at Brasper can request the keys? Do they return the signed XML (and, in Peru, the receipt confirmation)?
- Which document does Omni issue in Brazil (national NFS-e, municipal, or NF-e)? What exactly is billed, the commission or the full amount, and which document type applies on each route? (The accountant.)
- Who validates payments, who operates the VIEW, and who exports from the Dashboard and imports into Fact? Are they the same people? Who validates outside business hours (Mon–Fri 7–18, Sat 8–14, Sun 12–14)?
- Does Brasper already have an official WhatsApp Business account, and who can set it up? Does the client need a login in the Web App, or just a link?
- Are the 7 people and ~60 operations a day measured or estimated?
- Is the rates service the same one that failed on 10 Oct? Who sets the Brasper rate and the commission/variable discount (see Rates), and how often?
- Which tax applies to the Brazilian rows? The board shows the same 18% “impuesto” in R$ as in S/ (see Data model).
- Which SUNAT document type goes to clients with a CPF (no DNI/CE/RUC)? (The accountant.)
- Is the optional rejection explainer (A2) needed at all, once real rejections from the test mode are known?
Still unclear on the whiteboard: the “e” in the script box, the red circle at the end of its line, the first letters of the Dashboard label (“DAP”), and “<Expo>”. I read “IV” as Intención de Venta and “eje” as the central hub.