One idea: the operation lives in a database, not in a chat
Today the website is a calculator that ends in WhatsApp. Everything after that (data, proof of payment, verification, bookkeeping) happens in chat and by hand. The whiteboard moves that work into a structured flow; Kambista shows how a client-facing version of it can feel.
Calculator → WhatsApp
The calculator builds a pre-filled message and opens wa.me. No operation record is created on the website.
Web App → BD → VIEW → Dashboard → Script
Client data and voucher photos land in a database; a person validates each payment through a VIEW (about 15 min) and marks orders to export; the existing Dashboard imports and exports orders; a script registers the sales through the providers’ APIs.
Kambista’s 3-step wizard
Datos → Transferencia → Constancia, then a status timeline with receipts. The same shape fits the board’s fields almost one-to-one.
How a client is attended now
brasper.com/es promises “Envía dinero a Perú y Brasil por WhatsApp, seguro y en minutos”. The flow below is what the site and its code do. The back office behind it (sales and billing teams, KeyFácil and Omni) comes from the team’s diagnosis notes, because the whiteboard photo I have is cropped at the top.
Inside the back office today
Seven people, two teams, nine steps. The “Dashboard” (“eje” on the board) is the hub where sales types and billing re-checks.
What gets in the way
| # | Observation | Evidence | Effect on the client / team |
|---|---|---|---|
| 1 | No operation record is created by the site. The only output is a WhatsApp message. | code | No ID, no status the client can check, no data for reporting. |
| 2 | Proof of payment and account data are collected in chat. | site | Re-typing, missed or unreadable photos, long back-and-forth. |
| 3 | Rates did not load on 10 Oct 2026. Calculator showed 0.00 and “1 BRL = 0 PEN”; the calls coin/currencies, coin/tax-rate, coin/commission and automatic coupons failed with network errors. | site reproduced in the app’s browser pane and in headless Chrome; apibras.finzeler.com also fails the TLS handshake from curl | The main call-to-action cannot quote. Caveat: tested from one Mac only, so check from another network. |
| 4 | Hours are limited (weekday 7–18, Sat 8–14, Sun 12–14, holidays closed) and are only stated on the page, not in the flow. | site | A client outside hours gets no explanation of when the operation will be checked. |
| 5 | Back office is manual: validation by eye, double entry (Dashboard + KeyFácil / Omni), xlsx / pdf consolidation, receipt by email. | team notes board “(manual)” | Seven people for ~60 transactions a day; up to 3 days to the receipt. |
| 5b | Payment validation is 100% manual, from screenshots. | team notes | A forged or edited screenshot can pass; a person is the only control. |
| 5c | No defined data schema: the Dashboard is typed free-form (board flag “dB Schema!!!”). | team notes board | Reports and automation have nothing reliable to read. |
| 6 | Stale or inconsistent copy: promo “hasta el 09 de abril” still in the hero; “Envía en 3 pasos” shows four cards; “+45 mil operaciones” vs “+50 mil envíos”; leftover “¿Necesitas ayuda con tu donación?” near the WhatsApp button. | site | Weakens trust on a money product. |
| 7 | The public JavaScript embeds environment values, including an “admin redirect secret” with a trivially guessable value and the API host. | code | Anything in a VITE_ variable is public. Secrets and API keys must live on a server, which is exactly what the board’s “API key !” points at. Rotate that value and move the check server-side. |
What Kambista’s flow gives us to reuse
Walked end to end on a real device (Samsung, app v2.0.218). Kambista does everything in-app: calculate, fill data, transfer, report proof, track, get receipts. Each pattern below maps to a piece of the whiteboard.
| Kambista pattern | Brasper today | Brasper to-be (with board element) |
|---|---|---|
| Live calculator with rate and result | Has one, and shows commission + total (clearer than Kambista). Rates currently fail to load. | Keep it; make the rate source reliable. board: “tipo cambio” → Web App |
| Datos step: origin bank, destination account, origin of funds | Asked in chat; the “Programa” form collects DNI/CPF, name, phone, email only. | Same three fields in the app, plus DNI/CPF. board: Origen Fondo, C. Origen, C. Destino |
| Transferencia step: company account, copy buttons, “use the right currency account”, rate-lock time | Account is given in chat. | Show the Brasper account for the operation’s currency in-app, with the lock time and an out-of-hours notice built from Brasper’s own hours. suggested |
| Constancia: report the payment | Voucher sent as a chat attachment. | Operation number + photo upload straight into the database. board: N° Trans, Fotos → BD |
| Verification ~15 min and status timeline | No status visible to the client. | A person checks the bank (~15 min) and sets OK / Aprobado in the VIEW; client sees the timeline. board: Bank, 15 min, ok? Aprob |
| Receipts (boleta PDF/XML) | Manual. | Billing script generates them from approved rows. board: SCRIPT / Fact |
| Preferential rate for large amounts (Kambista: over $5,000 or S/18,000, via a link) | Rate negotiated with an agent in WhatsApp; notes mention “net” and “preferential” sales team notes to confirm | Rate tiers coded inside the quote tool, so a person is needed only for exceptions. suggested |
| Saved accounts, cancel, referral | Referral exists as step “Recomienda”. | Saved beneficiaries and a cancel path. suggested, not on the board |
The whiteboard flow, redrawn
Everything below the red line on the board, drawn with its actors: who does each step and in which system. Three rules shape it. Payment validation stays with a person who works in the VIEW. The Dashboard is the existing system and is not modified: it only imports and exports orders. And AI appears only where similar remittance and exchange businesses already use it, as optional pieces.
The original
What the board says
- Web App reads the exchange rate (“tipo cambio”) and collects Origen de fondo ✓, C. Origen, N° Trans, C. Destino.
- Fotos go from the Web App into the BD, labelled IV (read by the team as Intención de Venta).
- Bank with a person and 15 min: a person checks the bank and “ok? Aprob” goes back to the BD, through the VIEW where they change the order status (confirmed by you).
- The VIEW over the BD; a person marks to export.
- Export → the Dashboard, which already exists, is not modified and only imports and exports orders (confirmed by you).
- I/EXP → SCRIPT “Fact”: a person exports the orders from the Dashboard and imports them into Fact (confirmed by you). Fact has its own database and an API key (underlined and flagged with “!!”); it runs the set of orders, registers the sales through KeyFácil / Omni, and the flow ends with the receipt.
- Bottom left: WSP, Telegram, Discord o IA. In my drawing this is the optional chat agent A1 (Discord is not drawn: I found no use of it by similar businesses).
| # | Who does what | Source |
|---|---|---|
| 1 | Cliente quotes in the Web App, which asks for the current tipo de cambio (and commission). Optionally through the chat agent A1. | board arrow read as “Web App → rate” |
| 2 | Cliente enters origen de fondos, cuenta origen, N° de transacción, cuenta destino, transfers, and uploads the fotos of the proof; all of it is stored in the BD. Optionally A2 checks identity once, on the first operation. | board |
| 3 | Validador (a person) sees the pending operations in the VIEW, checks the deposit in the bank (target about 15 minutes) and sets OK / Aprobado; the status is written to the BD. Optionally A3 shows a risk score beside each order. | board confirmed by you |
| 4 | Operador marks the approved operations in the VIEW and exports the order file. They may be the same person as the validator. | board same person? to confirm |
| 5 | The file is imported into the Dashboard, the MYPE’s current system. It is not modified: it only accepts imports and exports of orders. | confirmed by you |
| 6 | A person from billing exports the orders from the Dashboard and imports that file into Fact, which runs the whole set (the board’s I/EXP). This is a manual hand-off by design. | board confirmed by you which person? to confirm |
| 7 | Fact runs the imported set: it validates the batch and registers each sale through the API: SUNAT via KeyFácil, the Brazilian equivalent via Omni, with an API key kept server-side. It stores the XML it gets back. Optionally A4 explains any rejection (detail). | board team notes |
| 8 | Fact builds the PDF from that XML and emails it to the client. It returns the invoice number and status to the BD, where the VIEW shows them; the Dashboard is not involved. | team notes return path: my reading |
Three AI pieces already used by similar businesses, and one experimental
I kept only the pieces for which I found a real deployment in remittance or currency-exchange companies, and each now sits in the graph as an actor that exchanges data with the flow. A1 to A3 meet that test. A4, for the Fact script, does not: I found no deployment, and the table says so. None of them approves a payment or edits fiscal data. The base flow runs with all of them switched off. Most sources are company, partner or vendor write-ups, so the performance claims are unaudited.
| Where it sits in the graph | What it does | Evidence in this context | What stays human or deterministic | What it needs, and its limits | |
|---|---|---|---|---|---|
| A1 | Cliente y canales → Web App API. Same API as the Web App; hands over to a person. | Conversation on WhatsApp, Telegram or web chat: quote, data capture, voucher upload, status. Detail in 4b. | Félix Pago sends remittances to Latin America through an AI chatbot on WhatsApp (Stripe case study). Rextie, a Peruvian exchange house, launched an AI chatbot, “Lucía”, for 24-hour customer advice (Andina; the article is undated, and it describes advice rather than operations). Tkambio’s assistant is described as experimental, so I do not count it. | Rates, fees and account numbers come from the API. No approvals; the validator decides. Handoff to a person on request. | A language model with tool calling, and the Web App API must exist first. Automating WhatsApp needs the WhatsApp Business Platform: today the site only opens a wa.me link. A Telegram bot works for a demo without that approval. Effort: medium. |
| A2 | Web App → BD, first operation only. A purple service attached to the BD. | Checks an identity document and a face match; returns pass or fail to the BD, and the result shows in the VIEW. | Remitly onboards customers with Onfido’s AI-based document and face checks (The Paypers, Onfido). | A person handles every failure. The vendor’s model is not ours to train or tune. | It is a paid external service. Open points: whether the vendor reads Peruvian DNI and Brazilian CPF documents, the cost per check, and legal review of ID and biometric data under Peruvian and Brazilian data-protection rules. It adds friction, so first operation only. Effort: low to medium. |
| A3 | BD → VIEW. A purple service whose score appears beside each pending order. | Returns a risk score for each operation to help the validator prioritize and look harder at some orders. | Remitly integrated Sift’s risk score into its review workflow: the score creates review tasks and is one input to human decisions (Sift case study, a vendor account). | The validator decides; the score never approves or rejects. | A vendor integration, and it needs operation history to be useful, while about 60 transactions a day is little data. A first version can be plain rules (first operation, unusual amount, new account), which are not AI. Effort: low to medium. |
| A4 | Fact · registra, on the rejected records. A purple actor under the step that talks to the tax services. | Reads the authority’s or provider’s rejection message plus the record, and says in plain language what is probably wrong and which field to check. A person corrects the data at the source and re-imports. | None found as AI. What I found is the problem it would address: some NFS-e rejections are generic, such as code E8214, which does not say which data is wrong (Omie help), and Brazilian rejections vary by municipality (Tecnospeed). E-invoicing vendors document rule-based validation in layers, technical then content (Sovos). The search result itself called an AI layer here a suggestion, not a fact. | Rules come first: schema, document numbers, totals and duplicates are checked before sending, with no AI. A4 only comments after a rejection. It never edits fiscal data, never resubmits, and its text is labelled as a suggestion. | Real rejection responses collected from the providers’ test environments, to see how many are generic enough to need it. A small language-model call with structured output. Limit: it can be wrong, so a person confirms. Effort: low. Unproven in this context. |
- AI never approves a payment and never changes a money value.
- Each piece has a feature flag; the base flow keeps working with all of them off.
- Store the input reference, model or vendor and the answer in
eventsfor audit. - Voucher and ID images are personal data: private storage, a provider with data controls, redacted logs.
- A1 on Telegram first (no approval needed), WhatsApp when the Business Platform is ready.
- A3 as plain rules first, so the VIEW already has the column; swap in a vendor score later.
- A2 only if a vendor covers DNI and CPF at an acceptable cost.
- A4 last, and only after the rule-based checks in Fact work and real rejections show it is needed.
What the client would see
Five screens modelled on Kambista’s wizard, using the board’s fields and BRASPER’s own rules (three currencies, commission shown up front, WhatsApp always one tap away). Illustrative wireframes with sample values, not final design.
Same calculator the site has, with “Continuar” as the main action and WhatsApp as the secondary one.
Board fields: origen de fondos, cuenta origen, cuenta destino. Destination can be a saved account.
From Kambista’s Transferencia step. The out-of-hours line comes from BRASPER’s own published hours.
del comprobante
Board: N° Trans and Fotos go straight to the BD, no chat attachment.
- Operación registradahora
- Verificando tu transferencia≈ 15 min
- Enviamos tu dinero
- Recibiste tu dinero
Timeline from Kambista’s finished-operation screen; receipt comes from the billing script.
WhatsApp with an AI agent A1 · optional
Clients will keep using WhatsApp, so the agent runs the same five steps inside the chat and writes to the same database as the web app. The “Enviar por WhatsApp” button stays: the pre-filled quote it already sends becomes the agent’s first input. The agent talks and collects; it never decides about money. Payments are validated by a person in the VIEW; the agent never approves anything. This is piece A1 of the optional AI layer (evidence).
Optimized flow (swimlanes)
get_rate_quotecheck_hoursget_rate_quotelist_accountscreate_operation → BD rowget_company_accountattach_proof → photo + N° Trans in BDget_status (reads BD; the validator updates it in the VIEW)send_receipt shares the linkWhat the chat would look like
Monto a Enviar: {monto} BRL
Tipo de Cambio: {tasa}
Total a Recibir: {recibe} PENpre-filled by the site
Today’s template is reused as input. The agent re-quotes because the pasted rate may be stale.
Board fields as questions. The agent writes each answer to the BD as it goes; the ID appears when the data is complete.
Titular: BrasPer
Banco: {banco}
Cuenta: {cuenta}
Usa la cuenta de BRL, no otra moneda.
Tasa asegurada hasta las {hora}.
Account data comes from a tool, never from the model’s own text. Hours-aware message (Brasper’s published hours).
Photo + N° Trans go to the BD (board “Fotos”). The agent only collects and stores; a person validates it in the VIEW.
💸 Enviamos tu dinero a la cuenta •••9956.
Status comes from the BD. Complaints and unusual cases go to a person with the context attached.
What the agent can do (tool layer)
| Tool | Does | Writes BD |
|---|---|---|
get_rate_quote | Rate, commission, total, lock time for a currency pair and amount | quote |
check_hours | Open / closed now and when a person will validate the payment | — |
list_accounts / save_account | Saved destination accounts of this client | yes |
create_operation | Origen de fondos, cuenta origen, cuenta destino → ID, status Pendiente | yes |
get_company_account | Brasper account for the operation’s currency | — |
attach_proof | Photo + N° Trans; status En validación | yes |
get_status | Timeline of an operation | — |
handoff_to_human | Opens an advisor thread with reason and summary | yes |
send_receipt | Receipt link produced by the billing script | — |
VITE_CHAT_API_URL) and has chat strings in ES / EN / PT. I did not verify what it does or whether it is live; it may be a starting point for this agent.Guardrails and handoff
- say a payment is received or approved; it only repeats the status the validator set in the VIEW
- treat the voucher image as proof: the validator checks it against the bank
- write a rate, fee or account number itself; those come verbatim from tools
- invent a rate when the rates service is down: it says so and hands off
- give financial or legal advice
- the client asks for one, or writes a complaint or claim
- the client reports a problem with the voucher, the amount or the account
- the rate lock expired mid-flow, or the amount is above a limit limit to define
- a first-time client needs identity checks process to define
Chat today vs optimized
| Today | With the AI agent | |
|---|---|---|
| First reply | Pre-filled quote, then a person answers during business hours | Instant, any hour, in the client’s language |
| Data | Person asks, client types, answers scattered in the chat | One question at a time, written to BD fields |
| Proof | Screenshot in chat, found by scrolling the conversation | Photo and operation number stored on the operation and shown in the VIEW, where the validator checks it against the bank |
| Status | Client asks “¿ya llegó?” | Messages sent when the BD status changes |
| Team effort | Every step: 4 in sales, 3 in billing | Validation in the VIEW, export and import, exceptions and supervision |
| Record | Chat history and spreadsheets | One BD row per operation, with the message log attached |
What the validator and the operator would see
The VIEW is the one screen we build for the team. The person validating payments changes order statuses here and marks which orders to export. The Dashboard is not touched: the exported file is imported into it as today. Wireframe with made-up rows.
| ID | Cliente | Envía → Recibe | Origen de fondos | N° trans. | Foto | Riesgo (A3, opcional) | 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 | 📎 | bajo | Aprobada |
| ☑ | BR-0004 | Cliente D | BRL → USD | Donación | 000004 | 📎 | medio | Aprobada |
| ☐ | BR-0005 | Cliente E | BRL → PEN | Ahorros | 000005 | 📎 | alto | Rechazada |
| ☐ | BR-0006 | Cliente F | BRL → PEN | Ahorros | 000006 | 📎 | bajo | Exportada |
What changes, element by element
Each row is one improvement from the board, with what it replaces today and the Kambista behaviour it can borrow.
| # | Improvement | Replaces today | Kambista reference | Source |
|---|---|---|---|---|
| 1 | Live “tipo de cambio” in the Web App | Calculator that currently shows 0 when the rates service is unreachable | Calculator + rate lock time | board |
| 2 | Structured data capture: origen de fondos, cuenta origen, cuenta destino, N° de transacción | Questions answered one by one in WhatsApp | Datos step | board |
| 3 | Proof photos uploaded to the BD | Voucher sent as a chat attachment | Constancia step | board |
| 4 | BD (“IV”) as single source of truth with a defined schema and a status per operation | Free-form typing into the Dashboard; chats and spreadsheets | Historial: En proceso / Finalizadas | board |
| 5 | Human payment validation in a VIEW: the person sees each case and the voucher, checks the bank (~15 min) and sets OK / Aprobado there | Validating by eye from chat screenshots, with no record of who decided | “Verificamos tu transferencia” | board confirmed by you |
| 6 | VIEW + “marcar para exportar” | Building the export by hand | — | board |
| 7 | Dashboard stays as it is: the VIEW exports an order file, the Dashboard imports it, and its order export feeds billing | Typing each order into the Dashboard | — | board confirmed by you |
| 8 | Billing script “Fact”: takes the exported batch, registers each sale via API (SUNAT / Brazilian equivalent), builds the PDF from the returned XML and emails it; API key kept on the server | Re-typing into KeyFácil / Omni, xlsx / pdf consolidation, manual email (up to 3 days) | Boleta PDF / XML per operation | board team notes |
| 9 | Channels around one BD: WhatsApp and Telegram, with an optional AI chat agent (A1) | WhatsApp as the only channel and the record | — (see 4b for the agent flow) | board role inferred |
| 10 | Status timeline and notifications to the client | “Did you receive my voucher?” messages | 4-stage timeline | suggested |
| 11 | Out-of-hours notice in the flow, using Brasper’s hours | Hours only in a page section | “Fuera de horario” toast | suggested |
| 12 | Saved beneficiary accounts and cancel path | Re-sending account data each time | Cuentas tab, cancel prompt | suggested |
| 13 | Optional AI, only where similar businesses already use it: A1 chat agent, A2 identity check, A3 risk score in the VIEW (evidence) | Everything by hand | — | optional |
| 14 | Rate tiers inside the quote tool (net vs preferential) | Rate negotiated per chat in a queue | Preferential-rate link for large amounts | suggested to confirm |
| 15 | Single data entry: one record feeds Dashboard and billing | Double entry, with typing errors | — | team notes |
Where AI helps, and what it should be worth
From the IATHON diagnosis (challenge 5.5, sales and billing at Brasper). Payment validation stays with a person, so AI is limited to three optional pieces with a track record in remittance and exchange businesses (the evidence is in A1 to A3 of the To-be section). The numbers below are the team’s baseline and hypotheses, not measurements I made.
| Piece | Where it acts | Seen in similar businesses | Main risk | Guard |
|---|---|---|---|---|
| A1 · Chat agent | Web chat, WhatsApp, Telegram (see 4b) | Félix Pago (WhatsApp remittances), Rextie (Peruvian exchange house) | Wrong numbers; WhatsApp automation needs the Business Platform | Money values only from tools; a person validates |
| A2 · Identity check | First operation of a client | Remitly with Onfido | Cost, privacy, coverage of DNI and CPF | Vendor verdict, a person handles failures |
| A3 · Risk score | Beside each order in the VIEW | Remitly with Sift | Little data at about 60 operations a day | Informs the validator, never decides |
| Plain automation team notes | Web App + BD, the VIEW and its export, Fact via API | — | No API key from Brasper; the Dashboard’s file format is unknown | Fallbacks in Data & MVP |
Baseline, hypotheses and what they imply
| Measure | Baseline | Team hypothesis / target | Implied my arithmetic |
|---|---|---|---|
| Sales effort | 4 people, ~60 trx/day, WhatsApp queues | −55% man-hours (the team’s hypothesis, built with automatic validation in mind) | 4 × 0.45 ≈ 1.8 FTE, only if the saving still holds with a person validating. Re-estimate. |
| Attention capacity | Limited by the queue | +25% with the chat agent, no extra staff | Capacity gain, not added to the line above |
| Billing effort | 3 people; Omni fully manual, KeyFácil partly | −80 to −90% man-hours (script via API to KeyFácil / Omni); 3 → 1 supervising | ≈ 1 FTE (the team’s plan) |
| Total team | 7 FTE | — | ≈ 2.8 FTE on the team’s figures; about 8.6 trx per person per day today vs about 21 (60 ÷ 2.8) |
| Time to receipt | Up to 3 days | 20 minutes from confirmation | Receipt generated by the script, no queue between steps |
| Payment validation | By eye from chat screenshots, no record of who decided | Stays with a person (owner decision); the VIEW records who decided and when | Measure minutes per validation instead of a share validated automatically |
| Typing errors | Double entry | 0 | One record, no re-typing |
The schema the board asks for, and a 4-day cut
The board flags “dB Schema!!!” in red and the notes list “define the BD schema” as pending. Below is a draft built only from the fields named on the board and in the notes, plus the MVP limits the team gave: four days, happy path only.
Draft schema suggested, to validate
| Entity | Key fields | Why it exists |
|---|---|---|
clients | id, name, doc_type (DNI / CPF), doc_number, phone (WhatsApp), email, country | One identity across web and chat |
client_accounts | id, client_id, bank, currency, account_number, holder_name, alias | Origin and destination accounts; “ambas a tu nombre” can be checked |
quotes | id, from_currency, to_currency, amount_send, rate, commission, amount_receive, tier (net / preferential), coupon, locked_until | The price the client accepted, frozen |
operations (“IV”) | id, client_id, quote_id, origin_account_id, destination_account_id, origen_de_fondos, channel, status, created_at | The Intención de Venta; the single source of truth |
proofs | id, operation_id, image_url, n_trans, uploaded_at | Voucher photo and the operation number the client entered |
validations | id, operation_id, validator, decided_at, decision (OK / rechazada), note | Who validated each payment and when; the human check, on record |
risk_scores optional | id, operation_id, source, score, created_at | Only if A3 is switched on; shown in the VIEW |
billing_batches | id, exported_by, imported_by, imported_at, file_checksum, record_count, status | One file exported from the Dashboard and imported into Fact by a person |
invoices | id, operation_id, batch_id, service (KeyFácil / Omni), authority_status, number, series, xml_url, cdr_url, xml_hash, pdf_url, emailed_at, message_id | Result per record: the XML is the source, the PDF is derived |
events | id, operation_id, channel, actor (client / agent / person), text, at | Audit trail and the chat history of each operation |
MVP cut for four days suggested
- Web App: quote → Datos → Transferencia → Constancia, with voucher upload
- BD with the schema above
- VIEW for the validator: see the voucher, set the status, mark and export the order file
- The existing Dashboard imports that file as today, with no changes
- Billing script: exported batch → API registration → PDF from the returned XML → email
- The optional AI pieces A2 and A3; A1 only as a Telegram or WhatsApp demo with quote and status
- Agent write-tools and Discord
- Edge cases: partial payments, third-party payers, wrong currency, refunds
- Rate tiers and coupons beyond one default
Dependencies and risks
| Risk | Why it matters | Fallback if it slips |
|---|---|---|
| API key for Omni / KeyFácil not delivered team notes | It is the critical dependency (“API Key !!” on the board). Omni has no integration today. | Script produces the xlsx / pdf in the exact format billing already uploads: still manual to upload, but no re-typing |
| Dashboard file format unknown new | The Dashboard cannot be changed and only imports and exports orders, so its file layout is the integration contract for both the VIEW and Fact. | Get a sample import and export file first; until then, export the VIEW as CSV and map it by hand |
| False or illegible vouchers team notes | A forged voucher can pass if the validator only looks at the image. | The validator checks the deposit in the bank, not just the photo; optional risk score A3 helps prioritize |
| Edge cases outside the happy path team notes | Real traffic will have them. | Handled by a person in the VIEW from day one (status rechazada, notes) |
| Adoption: clients used to WhatsApp team notes | If they skip the web app, data is not captured. | The WhatsApp agent writes to the same BD (4b) |
| Security, login and data in the MVP team notes | IDs, accounts and voucher images are sensitive. The live site already ships a guessable secret in its public JavaScript (see Today). | Server-side secrets only; rotate that value; limit staff access by role |
The flow behind “Script Fact”
Fact takes a set of orders that a person exports from the Dashboard and imports into Fact, which then runs the whole set. It registers each sale through the provider’s API with the tax authority (SUNAT in Peru through KeyFácil, the Brazilian equivalent through Omni), builds the PDF itself from the XML it gets back, and emails that PDF. It replaces today’s billing steps 6 to 9: pre-validate, register in KeyFácil / Omni, consolidate, email.
| # | Step (per batch, then per record) | Record state after |
|---|---|---|
| 0 | The operator marks approved orders in the VIEW and exports them; the file is imported into the Dashboard (unchanged). A person from billing then exports the orders from the Dashboard and imports that file into Fact (the board’s I/EXP), which runs the set. Every record has already been validated by a person. | lote recibido |
| 1 | Validar lote. Check the file schema and checksum, then each record: document number format and check digits, email, amounts that add up, route. Records whose operation_id is already invoiced are skipped, so importing the same file twice is safe. Invalid records are rejected on their own and the rest continue. | validado or rechazado_validacion |
| 2 | Registrar la venta vía API. Routed by direction: PE→BR to KeyFácil (registers with SUNAT), BR→PE to Omni (registers with the Brazilian tax authority; to confirm which document and authority). One call per record with the operation ID as idempotency key, bounded concurrency, API key from the server-side secret store. The service returns the authority’s answer and the XML. | registrando → aceptado, error_fiscal or pendiente_autoridad |
| 3 | Guardar el XML exactly as received (plus the CDR or authorization protocol) with its hash. It is never edited. | xml_guardado |
| 4 | Generar el PDF desde ese XML. Fact’s own template reads number, series, customer, amounts, hash and QR from the XML, then checks the PDF totals against it before saving. | pdf_generado or error_pdf |
| 5 | Enviar por correo the PDF (the XML can be attached too) to the customer; log the message ID. The WhatsApp agent can deliver the same link. | enviado or error_envio |
| 6 | Reportar. A batch report is written to the BD and shown in the VIEW (record, invoice number, status, error) and emailed to the supervisor; the Dashboard is not changed. The BD marks operations as invoiced, and the daily consolidated file is produced from it, replacing the manual “Consolidado”. | facturada |
What each exported record must carry
| Group | Fields |
|---|---|
| Identity | batch_id, operation_id (unique), exported_at, exported_by |
| Routing | direction (PE→BR / BR→PE), which gives country, authority and service |
| Document | document type (boleta, factura, or the Brazilian equivalent), issue date, currency |
| Customer | document type and number (DNI, RUC, CE, passport, CPF, CNPJ), name or razón social, email, address when the document requires it |
| Line | description (for example the exchange service or commission), quantity, unit value, tax code, total |
| Evidence | amounts sent and received, exchange rate, bank operation number, payment date |
Why the PDF comes from the XML
- Peru: the signed XML (UBL) is what SUNAT receives. The printed representation must show a QR code and the hash (the XML’s DigestValue), with the minimum content set by SUNAT resolutions (see Res. 340-2017). I did not verify the exact field list.
- Brazil: the authorized XML is the only fiscal document; the PDF (DANFSe for the national NFS-e) has no fiscal validity and its QR points to the public consultation (overview). One blog reported the national PDF-generation service offline in August 2026, which is a reason to render it ourselves; open-source renderers exist (example).
- Test environments: run against the providers’ test (homologación) mode first; Brazilian test PDFs carry a “no legal validity” mark.
When something goes wrong (per record)
| Case | What Fact does | Who acts |
|---|---|---|
| Invalid data in the file | Rejects that record in the report; the rest of the batch continues | Supervisor fixes the data at the source (VIEW / BD) and re-exports |
| Tax authority rejects it | error_fiscal with the authority’s code; no automatic retry | Supervisor corrects the data and resubmits; numbering follows the service’s rules |
| API down, timeout or 5xx | Retries with backoff; before resending, asks the service for the status of the idempotency key so no sale is registered twice | Alert only if retries run out |
| Authority answers later | pendiente_autoridad; polls until a final answer | — |
| PDF does not match the XML | Blocks the email | Supervisor; this should never happen |
| Email bounces or is missing | error_envio; the PDF stays downloadable from the VIEW and the WhatsApp agent can send the link | Supervisor |
| Same file imported twice | Skips records already invoiced | — |
Pseudocode of the job
# input: the set of orders a person exported from the Dashboard and imported into Fact batch = ingest(file) # 1 schema + checksum, skip already-invoiced ids for rec in batch.records: # bounded concurrency try: check(rec) # 1 country rules; DataError → rechazado_validacion svc = service_for(rec.direction) # 2 PE→BR: keyfacil → SUNAT · BR→PE: omni → BR authority res = svc.register_sale(rec, key=rec.operation_id) # idempotent; returns xml (+ cdr / protocol) if res.rejected: mark(rec, "error_fiscal", res.code); continue if res.pending: mark(rec, "pendiente_autoridad"); continue # polled later store_xml(rec, res.xml, res.cdr) # 3 untouched, hashed pdf = render_pdf(res.xml) # 4 from the XML, never from the DB assert totals(pdf) == totals(res.xml) # PDF ≠ XML → error_pdf, no email send_mail(rec.customer.email, pdf) # 5 log message id mark(rec, "facturada") except DataError as e: mark(rec, "rechazado_validacion", e) except Transient as e: retry_later(rec, status_check=True) # ask the service before resending report(batch) # 6 per-row result to the BD / VIEW + daily consolidated
- Idempotent: the operation ID is the key; re-running a batch continues the unfinished records and never duplicates a sale.
- XML is immutable: store it with its hash; the PDF can always be rebuilt from it.
- Secrets: API keys in a server-side secret store, never in the web bundle, rotated (the board’s “API Key !!”).
- Private storage for XML and PDF; links to customers are signed and expire.
- Audit trail: every call and the raw answer in
events. - Test mode first on both services before real sales.
- The script is fast when the services answer: seconds per record, minutes per batch.
- What decides the target is how soon operators export and confirm. Small, frequent batches (for example every 15 min or on each confirmation) beat one daily batch.
- If an authority answers asynchronously, the receipt waits for it; the client is told the status meanwhile.
What billing would see (in the VIEW, not in the Dashboard)
| Lote | Ruta | Registros | Aceptados | Rechazo validación | Rechazo autoridad | PDF enviados | Estado |
|---|---|---|---|---|---|---|---|
| L-0101 | PE→BR · SUNAT | 24 | 23 | 1 | 0 | 23 | Completo |
| L-0102 | BR→PE · fisco BR | 16 | 14 | 0 | 1 | 14 | Con errores |
| L-0103 | PE→BR · SUNAT | 18 | 17 | 0 | 0 | 16 | En proceso |
A phased path (my proposal)
The board does not set an order, so this is one that delivers value early: first stop the leaks on the live site, then capture data, then verify, then export and invoice, then widen the channels.
0 · Stabilize today’s site
Days
- Restore the rates / commission service and add a visible fallback
- Remove the expired promo, fix “3 pasos” and stale copy
- Move the admin check and any secret out of the public bundle
1 · Capture
Web App + BD
- Calculator → Datos → Transferencia → Constancia
- Photo upload, operation ID
- WhatsApp stays as a secondary button
2 · Validate
VIEW + human check
- Pending list with photo and N° Trans
- The validator sets OK / Aprobado (≈ 15 min target)
- Client timeline + notifications
3 · Export
VIEW → Dashboard
- Mark approved orders, export the file
- Import into the Dashboard as today, no changes
- Retire the manual typing and .xlsx / PDF
4 · Invoice
Script “Fact”
- A person exports from the Dashboard and imports the set into Fact; KeyFácil (PE→BR) and Omni (BR→PE) via API key kept on the server (flow)
- PDF built from the returned XML and emailed
5 · Channels + IA
Same BD, more doors
- Optional AI with a track record: A1 chat agent (4b), A2 identity check, A3 risk score
- A1 can start read-only (quote, status) earlier
What I read from the board, and how sure I am
Handwriting leaves room. These are the readings I used; correct any that are wrong and the diagram above will change with them.
| On the board | My reading | Confidence |
|---|---|---|
| Red line; “to be” below it | Above = today’s manual flow, below = the new one. | High |
| Box: Origen Fondo ✓ · C. Origen · N° Trans · C. Destino | Origin of funds, origin account, operation / transaction number, destination account. | High |
| “Fotos” into BD; “ok? Aprob” from Bank; “15 min” | Photos stored; a person checks the bank (~15 min) and sets OK / Aprobado through the VIEW. Confirmed by you: validation stays human. (An earlier version of this page, following the team notes, showed an automatic reconciliation; that is superseded.) | High |
| VIEW + person “Mark 2 export”, “export” | A database view of operations; a person marks which to export. | High |
| Dashboard, “confirmar / import”, “I/EXP” | The Dashboard is the MYPE’s existing system, is not modified, and only imports and exports orders (confirmed by you). The VIEW exports a file it imports; a person then exports its orders and imports them into Fact. The first letters of its label (“DAP / BAP”) remain unread. | High |
| SCRIPT + “Fact” + cylinder + “API key !” | Billing script with its own database; it reaches KeyFácil / Omni with an API key that must be protected (team notes). | Medium-high |
| “tipo cambio” with an arrow from Web App | The Web App fetches the exchange rate. I drew the arrow as drawn. | Medium |
| “<Expo>” beside the data box | Either “export” (this data is what gets exported) or Expo, the app framework. Not used in the diagram. | Low |
| “IV” over BD | Intención de Venta, the team’s interpretation. Plausible: it labels the table that holds each sale. I show it as “BD · IV”. | Medium |
| “eje” | The central hub, i.e. the Dashboard (team notes). | Medium |
| “x Asis”, “Ventas Prefe…”, “Ventas Netas”, “Cola” (in the part of the board I cannot see) | Per assistant / agent; preferential vs net sales; queue (team notes). Not verified by me. | Low-medium |
| “e” inside the script box; the red circle at the end of the script line | Unclear. I left them out of the diagram. | Low |
| WSP · Telegram · Discord o IA with a long arrow | Alternative channels (WhatsApp / Telegram) with an AI chatbot, writing to the same BD (team notes: “Diseño con IA”). I drew it as A1; Discord is not drawn because I found no use of it by similar businesses. | Medium-high |
apibras.finzeler.com API that is failing now? (6) Do the identity-verification vendors you would consider read Peruvian DNI and Brazilian CPF documents? (7) Are the 7 FTE and ~60 trx/day figures measured or estimated? (8) What exactly is invoiced on each route (commission or full amount) and which document type applies? (9) Do KeyFácil and Omni expose an API, who at Brasper can request the keys, and do they return the signed XML (and the CDR in Peru) in the response? (10) Which document and authority does Omni use in Brazil (NFS-e national, municipal, NF-e)? (11) Are the validator and the operator the same person, and who validates outside business hours? (12) Which person exports from the Dashboard and imports into Fact, and how often? (13) Does Fact’s first version need the optional A4, or is rule-based validation enough?