System view

Cross-flow timeline

Each row is one persona; each column is a BRD step. Cells show what each persona sees on WhatsApp at that step.

Persona / BRD step
§1 KYC
§2 Financier setup
§3 Submit invoice
§4 Validate terms
§5 Delivery confirm
§6 Internal check
§7 Assign financier
§8 Fund
§9 Payment route
§10 Repay
§11 Monitor
🏪 Supplier
O1 picker → O2–O3 Signup Flow → O4–O5 Settlement Flow → S1 welcome (~2 min)
S2–S3: Upload + AI confirm
S4: Accept terms
S5: Awaiting buyer's procurement (status update)
— (silent)
— (silent)
S6: ₦4.41M sent to bank
— (no buyer contact)
S7: Cycle complete
Performance updated inline at S7
🏦 Financing Partner
microfinance bank
FO1–FO2 onboarding · F1 registration confirmation
— (silent)
— (silent)
— (silent)
— (silent)
F2: Receivable available for discount (discretionary approval)
F3: Disbursement instruction · F4: Disbursement acknowledged
F5: Receivable settled — exposure released
F6: Concentration-limit threshold alerts
🏭 Buyer
Bokku
— (no account; captured at invoice submission)
Phase 1 — Procurement
B1: Email to procurement@bokku.ng · B2: WA to procurement manager · B3–B5: Waybill, OTP, ack
— (silent)
Phase 2 — Finance
B6: Email to finance@bokku.ng · Cc financing partner
B8: Receipt email · invoice closed
B7: Reminder emails (T-3, T-1, T+0)
Performance

Speed budgets (SLA)

Every transition has a wall-clock budget. Breaching any of these in production triggers a P1 page. The rail is only useful if it is fast.

Transition Trigger p95 budget Failure mode Critical?
First inbound msg → O1 persona picker Welcome template + 3 reply buttons ≤ 800ms User doesn't engage; lost lead
O1 persona tap → O2 tailored welcome Pre-rendered persona-variant template ≤ 600ms User loses momentum between picker and Flow
O2 tap → O3 signup Flow opens Local modal render (no network) ≤ 200ms Perceived stutter on the entry tap
BVN entered → name verified NIBSS BVN lookup + name fuzzy match ≤ 1.5s User types again or abandons
CAC RC entered → business auto-fill CAC public registry fetch + cache write ≤ 1.5s Form feels stuck; abandonment
Signup submit → O4 settlement CTA Account creation + tenant schema + template ≤ 1.5s Supplier stalls between two Flows
O4 tap → O5 settlement Flow opens Local modal render (no network) ≤ 200ms Perceived stutter on the entry tap
Account number entered → name auto-fills NIBSS NameEnquiry + BVN fuzzy match (Flow data-source) ≤ 1.2s User types again, double-submits, or abandons
Settlement submit → S1 welcome Bank link write + transit wallet provisioning + template ≤ 1s Supplier abandons before first deal
Total: first message → ready-to-fund Sum of all onboarding hops (parallel where possible) ≤ 2 min Time-to-value missed; cold lead CRIT
S2 upload → S3 AI confirm Media download + OpenAI mini extraction ≤ 1.8s Perceived “app feels slow”
S3 confirm → S4 terms screen Pricing engine + template send ≤ 600ms Supplier cancels mid-flow
S4 accept → B1 procurement email + B2 WA nudge (parallel) Email send + WA template fan-out ≤ 30s Procurement recall drops; fraud window opens CRIT
B3 waybill submitted → vision pass Vision model + cross-check vs invoice JSON ≤ 3s Procurement manager walks away
B4 OTP entered → ack OTP validate (sent to corporate email) + state advance ≤ 500ms Procurement manager loses confidence in process
B5 ack → procurement receipt email Email assembly + send (Bokku procurement audit record) ≤ 5s Procurement chases for closure
DELIVERY_CONFIRMED → F2 financier decision arrives Notification template send ≤ 5s Financier feels out-of-loop on a confirmed deal
F2 "Approve & fund" tap → F3 wallet instruction Local screen render with supplier-wallet details ≤ 600ms Financier doubts the fund step worked
F3 confirm → F4 + S6 (WA) + B6 (email to finance) Capital wallet → supplier wallet transfer + fee deduction + bank remit + 2 templates + 1 email ≤ 8 min total (NIBSS) Supplier calls support; buyer finance never sees instructions CRIT
FUNDED event → B6 finance email lands Email assembly + ESP send + DKIM sign ≤ 5s Buyer AP team can't process; ageing starts CRIT
Buyer wallet inbound → S7 (WA) + F4 (WA) + B8 (email to finance) Wallet webhook + 2 WA templates + 1 receipt email in parallel ≤ 10s Supplier & financier anxious; buyer chases CRIT
Cap threshold crossed → F5 alert Limit watcher + template ≤ 5s Cap accidentally exceeded — regulatory risk CRIT
Buyer reply on email → ops first response Inbound parse + route to ops queue + auto-ack ≤ 1 business hour Buyer trust erodes; payment delayed
Reminder cron (T-3 / T-1 / T+0) Scheduled email send at 09:00 WAT ≤ 30s of 09:00 Reminder lands too early/late; ignored

How we get to these numbers

  • Templates pre-rendered. All Meta-approved WhatsApp templates and email templates are pre-cached with placeholders; the only work at send-time is variable substitution.
  • Parallel fan-out across channels. S7 (supplier WA) + F4 (financier WA) + B8 (buyer-finance email) are dispatched in parallel from a single REPAID event, never sequentially. Same pattern for S6 + F3 + B6 on FUNDED.
  • Two channels, one state machine. WhatsApp and email handlers both subscribe to the same domain events. Adding a third channel (SMS, in-app) is additive — never a rewrite.
  • OpenAI mini for extraction. Lower latency than alternative vision models on the invoice and waybill passes.
  • Webhook-driven, not polled. Wallet events push; we never poll.
  • WhatsApp Cloud API + transactional ESP. No on-prem hops. Direct Meta endpoint for WA, verified-domain sender (Postmark / SendGrid) for email with SPF/DKIM/DMARC pre-configured.
Error & recovery

Error flows

What the user sees when the rail fails. Each is a real WhatsApp screen, not a vague “something went wrong”.

E1
Buyer's procurement disputes the delivery
→ INVOICE_REJECTED
11:53

Tone

Direct, without accusation. The mistake might be benign (wrong invoice resubmitted). The fraud signal is recorded silently and routed to ops; we don’t broadcast suspicion to the supplier.

Money status spelled out

“No money has been disbursed” is a hard line — suppliers panic when a deal stalls.

E2
Buyer paid without reference
→ UNMATCHED_PAYMENT

Recovery, not rejection

An unreferenced payment is a reconciliation problem, not a structural break. The email proposes the most likely match and asks the buyer's finance team to confirm in writing — instead of returning the funds and confusing everyone.

Why this is an email and not a WhatsApp prompt

The buyer's finance team needs a paper trail for any reconciliation adjustment. Email is the audit-grade channel; WhatsApp is not. The financing partner is CC'd on this email too — they know there's an unmatched payment in flight before any close.

Buyer reply → ops triage, not auto-apply

The buyer's "yes" is logged but is not enough alone — an ops user must reconcile in the dashboard. Once applied, the parallel S7 + F4 + B8 fire as for a normal repayment.

Funds held in suspense

Until reconciliation, the unmatched amount sits in a Terracore suspense ledger account, not against any specific invoice. The financier still sees outstanding exposure on INV-23 because it hasn't been closed.

E3
Disbursement failed
→ DISBURSEMENT_FAILED
11:55

“Funds are safe” is the only sentence that matters

When money flow stalls, the supplier panics. The first three words in the bubble are designed to land that fear immediately.

Three buttons

Switch bank (active), wait (passive), call me (escalate). All three are legitimate; the bot doesn’t push one over the others.


Terracore Financing Bot · UI Flow Architecture · 2026-05-01
WhatsApp visual tokens reflect Meta UI as of Feb 2026. · Back to overview