Results
We report what we built and how it behaves. We do not report the client's revenue.
| Metric | Value | Source |
|---|---|---|
| Ship window, Blueprint to production | 47 days | Qognition delivery record — scope locked at Blueprint, window committed before code |
| p95 latency target, transaction submission | 150 ms – 300 ms | Our standard load-test budget for this class of multi-tenant transaction path — an engineering target, not measured production traffic |
How we measured. The 47-day window is ours and it is the figure we own end to end: signed Blueprint to production traffic, in calendar days, not working days. The p95 figure is our standard engineering load-test budget for this class of multi-tenant transaction path, not a number drawn from this engagement's own Blueprint or Harden records — a target, not a measurement of the client's live traffic. Time to first production transaction, live tenant count at handover and uptime since go-live live entirely in the client's own production systems. We do not have them, they have no honest engineering-target framing — they are commercial and operational facts about the client's business, not a quantity we design toward — so they are removed from this page rather than estimated.
What we are not claiming. We do not claim the client's revenue as our result. An earlier version of this page led with the client's ARR. That is a category error: revenue belongs to the client's market, pricing and sales team. A 47-day build is a necessary condition for a product to earn anything; it is not the cause of what it earns. We are accountable for the ship window, the architecture, the isolation model and the platform's operating characteristics.
The question this engagement had to answer
*Can a fintech platform be built fast enough to matter without building the thing a regulator, a bank partner or an acquirer later forces you to rewrite?*
Speed is the easy half. Any MVP development agency can put a single-tenant CRUD app in front of users in six weeks. The GCC fintech problem is different: tenancy, residency and KYC are not phase-two concerns here. They are structural, and retrofitting any of the three means rewriting the data layer.
The constraint was never "ship in 47 days". It was "ship in 47 days and do not create the rewrite".
Why this was hard
Multi-tenancy is a data-layer decision, not a feature. Pick wrong on day three and every query, migration and export inherits it.
Data residency is jurisdictional, not a preference. A platform serving customers across the GCC cannot assume one region will do.
Arabic is not a translation task. RTL changes layout, iconography, numerals, date handling and text input. Bolting it on after the UI exists means rebuilding the UI.
KYC is an integration with a human failure mode. Verification is asynchronous, fails for mundane reasons, and every failed check is a real customer stuck at the door. The unhappy path is most of the work.
Architecture
flowchart TB
W["Web app<br/>LTR + RTL, i18n at the root"] --> GW["API gateway<br/>authN, rate limit, tenant resolution"]
M["Tenant admin console"] --> GW
GW --> TR["Tenant context middleware<br/>resolves tenant_id from token,<br/>injects into every request"]
TR --> ONB["Onboarding service"]
TR --> KYC["KYC orchestrator<br/>async, retry, manual-review queue"]
TR --> TXN["Transaction service<br/>idempotent, ledgered"]
ONB --> KYCV["KYC / AML vendor<br/>document + identity verification"]
KYCV -. callback .-> KYC
KYC --> MRQ["Manual review queue<br/>compliance operator decides"]
TXN --> PSP["Payment processor<br/>tokenised — PCI scope stays with PSP"]
ONB --> DB
KYC --> DB
TXN --> DB[("Primary datastore<br/>tenant_id on every row,<br/>row-level security enforced<br/>at the database")]
TXN --> AUD[("Append-only audit log<br/>actor, tenant, action, timestamp")]
KYC --> AUD
DB --> REG["Region-pinned deployment<br/>data residency per jurisdiction"]The isolation model. Shared schema, tenant_id on every row, enforced by database row-level security rather than application discipline. Tenant context resolves once at the gateway; no service builds a query without it. We chose this over database-per-tenant for one operational reason: at MVP scale, database-per-tenant multiplies migration cost by the number of customers, and migration cost is what kills velocity in month four. The escape hatch is deliberate — a large or regulated tenant can be lifted into its own database with no schema change, because the schema already carries the tenant key.
PCI scope. Card data never touches the platform. Tokenisation happens at the processor and the platform stores tokens. That decision was made at Blueprint and it is the difference between a manageable compliance posture and a very expensive one.
What we did
1. Blueprint: locked tenancy, residency and KYC before writing code. Three decisions were made and never revisited — row-level isolation with a database-enforced tenant key, region-pinned deployment with residency documented per jurisdiction, and KYC as an asynchronous orchestrator rather than an inline call. A 47-day window is not fast typing. It is the absence of mid-build architecture debates.
2. Built RTL and i18n into the first component. Locale and direction are set at the application root, and every component was built and reviewed in both directions from the first commit. Numerals, dates, currency and input direction are handled centrally. A few days at the start, a rebuild avoided at the end.
3. Made KYC an orchestrator with a human queue. Onboarding submits a verification, takes a callback, retries on transport failure, and routes anything the vendor cannot clear to a compliance operator with the evidence attached. The customer sees a status, not a spinner. Every transition writes to the audit log.
4. Made transactions idempotent and ledgered from day one. Every transaction carries an idempotency key and writes to an append-only ledger. Retries are safe and reconciliation is possible. You cannot retrofit this once real money has moved.
5. Hardened, then handed over. Harden is inside the window, not after it: load testing on the transaction path, failure injection on the KYC callback, isolation tests that attempt cross-tenant reads and assert they fail, runbooks, and a handover the client's engineers could operate.
How we evaluated it
Tenant isolation. The primary correctness question is not "does it work" but "can tenant A ever see tenant B". We tested it adversarially: a suite that authenticates as one tenant and attempts to read, write, list, export and enumerate another tenant's records across every endpoint. Every attempt must fail at the database layer, not the application layer. A test that passes because a service remembered to filter will fail the day someone writes a new query.
Transaction path performance. The metric that mattered was p95 latency on transaction submission under concurrent multi-tenant load — not average, because the average hides the tenant having a bad day. Target: 150 ms – 300 ms p95 — our standard load-test budget for this class of multi-tenant transaction path, not a figure drawn from this engagement's own Blueprint or load-test records. It is an engineering target, not the client's measured production traffic — we do not have the client's live p95 and will not publish one we have not measured ourselves. The KYC path is excluded from that budget by design; its metric is time-to-resolution including manual review, not request latency.
Failure modes, and what happens when the system is wrong.
| Failure | What the system does |
|---|---|
| Vendor rejects the document | Customer sees a specific reason and a re-submit path. No dead end. |
| Vendor ambiguous or times out | Routed to manual review with evidence attached. A compliance operator decides. |
| Callback never arrives | Reconciliation job re-polls. Onboarding does not silently stall. |
| Processor returns an unknown state | Transaction held, idempotency key preserved, reconciled against the processor rather than retried blindly. |
| Cross-tenant query attempted | Blocked at the database by row-level security and written to the audit log. |
The design rule throughout: when the platform does not know, it queues for a human. It never guesses on money or identity.
Regulatory posture
Regional fintech CTOs check this first, and its absence ends the conversation. Stated as posture, not as legal advice:
Data residency. Region-pinned deployment, with the residency position documented per jurisdiction served. A Blueprint decision, not a deployment-day one.
SAMA, CBUAE and DFSA. The applicable regime depends on the client's licence, jurisdiction and activity — a DFSA-regulated entity in the DIFC, a CBUAE-supervised activity in the UAE and a SAMA-supervised activity in Saudi Arabia carry materially different obligations. We built so those obligations map onto controls that exist: per-tenant segregation, append-only audit trail, region pinning, exportable records, role-based access with logged administrative actions. We are engineers, not your compliance counsel. We build to the requirements your regulator and your counsel set, and we make sure the architecture can satisfy them without a rewrite.
PCI DSS scope. Minimised by design. Card data is tokenised at the processor and never stored, processed or transmitted in raw form by the platform.
AML. Screening and monitoring run through the KYC/AML vendor. The platform orchestrates, evidences and logs. It does not make the determination.
What was hard
The tradeoff. Shared-schema row-level isolation over database-per-tenant. It is the less conservative choice and we took it knowingly. The mitigation is that the tenant key exists everywhere, so lifting one tenant out later is an operations task, not a redesign. If the first enterprise customer demands a dedicated database, the platform can give them one.
The alternative we rejected. Building our own verification flow across multiple identity providers to avoid vendor lock-in. Defensible, and we said no. Inside 47 days the real choice was a robust orchestrator around one vendor or a fragile abstraction over three. We built the orchestrator behind a vendor-agnostic interface and shipped with one provider.
What broke. RTL and the transaction table. Direction was handled at the root and the app was reviewed in Arabic throughout — but numeric-heavy tables mix LTR numerals into RTL layout, and our global alignment rules produced amounts that read correctly and aligned wrongly. Nobody caught it until a native Arabic reviewer looked at a real statement. We fixed the table primitives rather than patching screens, and added Arabic review to the definition of done for every numeric view.
What we would do differently
Instrument the client's production metrics ourselves. The gap on this page — first production transaction, tenants onboarded, p95, uptime — exists because those numbers were left to the client's stack after handover. Shipping our own telemetry inside the window would have cost two days and given both sides one source of truth. It is now part of Harden.
Bring the native Arabic reviewer in during Prototype. We brought one in during Harden. The RTL table problem would have surfaced weeks earlier and far cheaper.
Write the residency and PCI posture as a client-facing document from day one. We had the position and the controls, and wrote them up late. The client had to answer partner due-diligence questions before the document existed. It is now a Blueprint deliverable on every regulated engagement.
What is still running
The client's own engineering team operates the platform post-handover. Our current involvement is roadmap support and the second KYC provider integration.