AI-powered receipt scanning and reimbursement tracking for clubs/teams. Upload a receipt photo → Amazon Nova AI extracts details → Review & submit → Appended to Airtable.
Formerly/
├── client/ # React frontend (Vite + Tailwind)
│ └── src/
│ ├── components/
│ │ ├── AdminDashboard.jsx # Admin-only: spreadsheet view of club receipts
│ │ ├── Header.jsx
│ │ ├── SuccessScreen.jsx
│ │ └── ...
│ ├── utils/api.js # Axios wrapper + all API calls
│ └── App.jsx # Screen-based routing (member vs admin flows)
├── server/ # Express backend (Node.js)
│ └── src/
│ ├── routes/
│ │ ├── users.js # Google OAuth + user profile
│ │ ├── admin.js # Admin-only receipt CRUD
│ │ └── receipts.js # Upload, extract, submit
│ ├── services/
│ │ ├── airtableService.js # Airtable read/write/delete
│ │ ├── userService.js # DynamoDB-backed user store
│ │ └── novaService.js # Amazon Bedrock Nova AI
│ └── config/index.js
└── README.md
- Node.js 18+
- AWS account with Bedrock access (Nova model enabled) and DynamoDB
- Airtable account with a base set up (one table per club)
- Google Cloud project with OAuth 2.0 credentials
cd server && npm install
cd ../client && npm installCreate server/.env:
PORT=3001
NODE_ENV=development
# Google OAuth
GOOGLE_CLIENT_ID=your_google_client_id
# AWS (Bedrock + DynamoDB)
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your_key_id
AWS_SECRET_ACCESS_KEY=your_secret_key
NOVA_MODEL_ID=amazon.nova-lite-v1:0
# Airtable
AIRTABLE_API_TOKEN=your_airtable_pat
AIRTABLE_BASE_ID=your_base_id
# File uploads
UPLOAD_DIR=./uploads
# Admins — comma-separated Google emails that receive the admin role
ADMIN_EMAILS=[email protected],[email protected]
# DynamoDB table name (auto-created on startup)
DYNAMODB_TABLE=formerly-users- Create an Airtable base
- Create one table per club (names must match the
tableNamevalues inserver/src/config/index.js) - Each table needs columns:
Timestamp,SubmittedBy,Part,Merchant,Date,Total,Tax,Currency,PaymentMethod,ReceiptID,Notes,Status - Generate a Personal Access Token with
data.records:readanddata.records:writescopes - Add the token and base ID to
server/.env
- Bedrock — enable
amazon.nova-lite-v1:0in AWS Console → Bedrock → Model access - DynamoDB — the
formerly-userstable is created automatically on first server startup (requiresAmazonDynamoDBFullAccesson your IAM user) - Google OAuth — create an OAuth 2.0 client in Google Cloud Console, add your domain to authorized origins
# Terminal 1 — Backend
cd server && npm run dev
# Terminal 2 — Frontend
cd client && npm run devFrontend: http://localhost:5173 Backend: http://localhost:3001
| Role | How assigned | Experience |
|---|---|---|
| Admin | Email listed in ADMIN_EMAILS env var |
Lands on Admin Dashboard — spreadsheet view of all submitted receipts per club, with delete and submit-own-receipt capabilities |
| Member | Everyone else | Existing receipt upload flow — pick club, photograph receipt, AI extracts data, review and submit |
Roles are checked on every sign-in so you can promote/demote users by updating ADMIN_EMAILS and restarting the server.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/users/google-auth |
Verify Google JWT, find/create user |
| GET | /api/users/:id |
Get user profile + role |
| PUT | /api/users/:id/parts |
Update club memberships |
| POST | /api/receipts |
Upload receipt image |
| POST | /api/receipts/:id/extract |
Run Nova AI extraction |
| GET | /api/receipts/:id |
Get receipt status/data |
| POST | /api/receipts/:id/submit |
Submit to Airtable |
| GET | /api/admin/clubs/:clubId/receipts |
(admin) List all receipts for a club |
| DELETE | /api/admin/clubs/:clubId/receipts/:recordId |
(admin) Delete an Airtable record |
Admin endpoints require an x-user-id header containing the authenticated user's ID. The server verifies the user has role: "admin" and that the club is in their adminClubs list.
- Sign in with Google
- Select the clubs you belong to (first sign-in only)
- Pick which club this receipt is for
- Upload receipt photo
- Nova AI extracts merchant, date, total, tax, currency, payment method
- Review and edit extracted data
- Confirm — row appended to Airtable
- Sign in with Google (email must be in
ADMIN_EMAILS) - Land on Admin Dashboard automatically
- Switch between club tabs to view their submitted receipts
- Delete records directly from the dashboard
- Optionally click "Submit a Receipt" to use the member upload flow
- After submission, "Back to Dashboard" returns to the spreadsheet view