Bonuxo: a multi-tenant loyalty platform in production
I designed and run Bonuxo: points, stamps, rewards and Apple and Google Wallet cards, with a public API for POS systems. This case covers its architecture, its reliability and Nux, the AI assistant that is still in construction.

What it is and its status
Bonuxo is a multi-tenant loyalty platform: points, stamps, rewards, a prize wheel, statistics and cards in Apple and Google Wallet. It has a public API for POS systems, e-commerce integrations and recurring billing with MercadoPago. It is in production at bonuxo.com, with the UI in 4 locales.
The platform is in production. Nux, the AI assistant in the dashboard, is in construction: its LLM layer is built and tested but switched off in production. There is a section of its own below with its status.
The problem it solves
A business that wants to retain customers needs three things at once: configurable reward rules, that loyalty living in the customer’s phone wallet, and a connection to the till where the payment happens. Bonuxo solves all three on a single platform, shared by many businesses with each one’s data kept separate.
Architecture
The backend follows Clean Architecture in 4 projects: Domain, Application, Infrastructure and Api. It runs on .NET 8 with PostgreSQL 16 and Redis 7; the frontends are React.
Isolation between businesses is done with EF Core global query filters on every tenant entity, composed with soft delete and guarded by an architecture test. The tenant comes from a JWT claim, and the POS API-key scheme emits the same claim, so both entry paths reach the domain with the same shape.
- Redis is used for cache behind a resilient decorator (a Redis outage becomes a cache miss, not a 500), rate-limit counters, an anti-fraud counter, a distributed lock and health probes.
- Deployment is 9 Coolify apps: backend, 6 frontends, Postgres and Redis. If a required secret is missing, the deploy aborts. There are liveness and readiness endpoints.
Loyalty engine
There are several loyalty strategies: stamps, cashback, progressive discount, membership and points (per visit or per amount). All of them go through a single accrual core.
- FOR UPDATE row locking.
- An exactly-once guarantee with an economic key and a partial UNIQUE index.
- An outbox and typed reversals.
- A points expiry job: a minimum of 30 days, with notices at 30 and 7 days.
- The prize wheel uses a cryptographic random generator, a Redis anti-fraud gate and a Postgres audit.
Apple and Google Wallet cards
Apple Wallet: the .pkpass is signed as detached PKCS7 with SHA-256. It implements Apple’s device registration web service, with an APNs HTTP/2 push after each transaction and a certificate expiry monitor.
Google Wallet: it uses a save-to-wallet JWT signed with RS256, object patch updates and a periodic geo sync.
API for POS and e-commerce
- API keys with live and test prefixes. Only the SHA-256 hash is stored, and the auth scheme is separate from JWT.
- Limits per key (600 per minute) and per IP, an optional Idempotency-Key with 24-hour retention, exactly-once sales and a 256 KB payload cap.
- An OpenAPI 3.0.3 contract, version 1.3.0, with 10 paths.
- Integrations verified in code: Fudo (POS), TiendaNube (e-commerce) and a MercadoPago POS adapter, with polling and reconciliation.
I ran a load test against production with a sandbox key on 30 September 2026: up to 900 requests per minute, 3084 requests and 0 errors, with a p95 of 66 to 126 ms per endpoint. The test found that the per-key limit was not being applied; I fixed it in code.
Billing with MercadoPago
Recurring billing uses preapproval subscriptions. The webhook signature is verified with HMAC-SHA256 in constant time, and each webhook is claimed in two idempotent phases, handling duplicate and out-of-order events. A read-only reconciliation service compares the stored subscriptions with MercadoPago’s.
Security and observability
- 15-minute JWT access tokens and 7-day refresh tokens stored hashed, with zero clock skew. BCrypt passwords and per-tenant lockout.
- TOTP 2FA with recovery codes.
- RBAC with 4 roles plus per-member permissions and plan capability gates.
- An audit trail of privileged writes, without bodies.
- Security headers including HSTS preload and CSP. AES-256-GCM for API keys and personal data. Personal data is scrubbed before it reaches Sentry.
- Configuration fails fast on startup if something is missing.
- Observability with Sentry, tagged by tenant and correlation, and Serilog structured logs.
Own MCP server to operate the database
I wrote my own MCP server, in TypeScript, for database operations. I use it in development and operations. It has 5 tools: guarded DML writes, explain, script generation, whitelisted DDL and checksum-tracked migration apply.
The guards are an environment gate, a production block, a confirmation token, a DDL blocklist, DML-only, statement and lock timeouts, and automatic rollback.
Nux: the AI assistant, in construction
Nux is the dashboard assistant for the owner and staff. It is in construction. The deterministic layer (guided tours, contextual help and a 23-intent router) is live for all tenants.
The LLM agent uses tool calling with 19 tools: 12 read, 5 confirm and 2 strong-confirm. Writes need human confirmation and respect the user’s permissions, with at most 8 tool rounds per turn. The provider is interchangeable between OpenAI and Anthropic, and there are per-plan cost caps and a usage log.
What is in production and what is not
- In production: the loyalty platform, the wallets, the POS API, MercadoPago billing and the MCP server.
- In production for all tenants: Nux’s deterministic layer.
- In construction: Nux’s LLM agent, switched off in production, and its integration with LangGraph, LangChain, RAG and Langfuse.
Stack
- .NET
- PostgreSQL
- Redis
- React
- Docker
- Coolify