Architecture & data model.
PerkUp is one shared data model rendered by a React app, served by TypeScript Cloud Functions, with a Go service in maintenance mode for payments. Here is how the pieces fit, in product terms.
Proto-first data model
Every core entity is defined once as a Protocol Buffers message in proto/ and generated into TypeScript (for the web app and Cloud Functions) and Go (for the backend service). One definition, one vocabulary, everywhere — the admin platform, the recipient experience, and the backend cannot drift out of sync on what an Individual or a Program is.
| Entity | Represents | Defined in |
|---|---|---|
| Organization | A customer company — billing, settings, brand, HRIS config. | proto/perkup/v1/organization.proto |
| Individual | An employee or recipient — identity, role, status, labels, manager. | proto/perkup/v1/individual.proto |
| Program | A reward, recognition, or perk send, with enrolled members. | proto/perkup/v1/program.proto |
| ProductVariant | A catalog item — swag, gift card, or curated gift. | proto/perkup/v1/product_variant.proto |
| Account | A pool of funds an organization draws against. | proto/perkup/v1/account.proto |
| BalanceTransaction | A hold, capture, release, or refund against a balance. | proto/perkup/v1/transaction.proto |
| Order / OrderFulfillment | A purchase and its shipment and tracking. | proto/perkup/v1/order.proto |
| Rule | An automation trigger for birthdays, anniversaries, and leave. | proto/perkup/v1/rules.proto |
| Production / Warehouse | Bulk-swag manufacturing state and inventory. | proto/perkup/v1/production.proto, warehouses.proto |
For the human-readable definition of each term and its states, see the Glossary.
The three codebases
| Codebase | What it is | Where new code goes |
|---|---|---|
| Web app | A single React + Vite + TypeScript SPA that renders both the admin platform and the recipient experience. | apps/frontend/ |
| Cloud Functions | Firebase Functions (TypeScript) — the default home for new backend logic: RPC services, webhooks, Firestore triggers, scheduled jobs. | functions/ — yes, default |
| Go service | A Go API server on App Engine, in maintenance mode. Still owns Stripe payments, the account ledger, and a few integration clients. | backend/app/ — maintenance only |
New backend services are written in TypeScript in functions/. The Go service is maintained for what already lives there (payments, ledger, Printful/Plum/Shopify clients) but takes no new RPC services. See backend/AGENTS.md.
Data stores & infrastructure
The platform runs on Google Cloud. The product-relevant pieces:
- Firestore — the primary database; every entity above is a Firestore document.
- Cloud Storage + CDN — serves the web app and stores assets, designs, and proofs.
- Pub/Sub — async work like member creation and balance updates.
- Cloud Tasks — rate-limited outbound work such as order placement.
- Cloud Scheduler — recurring jobs: approval reminders, digest emails, expiry alerts.
- BigQuery — analytics and the bulk-swag supplier catalog.
- Firebase Auth — identity, with custom claims binding a user to an organization and role.
Search
Two search engines power browsing, each scoped by a secured per-user key:
- Algolia indexes product variants for the member marketplace and gift search.
- Typesense indexes the bulk-swag supplier catalog and mockup templates.
Source: proto/ · CLAUDE.md · functions/AGENTS.md · backend/AGENTS.md · apps/frontend/src/services/{algolia,typesense}.ts