The regulated-finance BFF.
A TypeScript Express 5 service that owns provider secrets, identity bridging, money movement, invoicing, statements and reconciliation. Clients stay thin; the backend stays in charge.
At a glance
Inside the BFF
Express boot → middleware → routers → controllers → providers + Postgres.
HTTP surface
Every namespace mounted in src/app.ts, with endpoint counts from route extraction.
| Base path | Purpose | Endpoints |
|---|---|---|
| /api/users | Signup, login, Cognito flows, profile, notifications, SumSub token / status, account deletion | 27 |
| /api/if | Integrated Finance clients, accounts, cards, beneficiaries, transfers, transactions, graphs, open banking, webhooks | 41 |
| /api/sumsub | SumSub webhook + SSE status stream | 2 |
| /api/phyllo | Phyllo users, SDK tokens, work platforms, accounts, profiles, social / income data, webhooks | 36 |
| /api/monite/common | Counterparts, addresses, bank accounts, contacts, VAT IDs | 27 |
| /api/monite/entity | Entities, roles, entity users, bank accounts, VAT IDs, settings | 19 |
| /api/monite/payables | Payables and credit notes | 6 |
| /api/monite/receivables | Units, products, VAT rates, payment terms, invoices, recurrences | 20 |
| /api/statements | Account, confirmation, balance, fee and email statement generation | 5 |
| /api/support | Intercom tickets, tickets metadata, tags, replies | 18 |
| /api/support/kb | Intercom knowledge base facade | 5 |
| /api-docs | Swagger UI · OpenAPI | — |
| /webhook | Generic placeholder webhook logger | — |
Tech stack
Every layer of the runtime, with the file or surface that proves it.
Core flows
The chains the backend actually orchestrates day to day.
Signup, verification, login
Email + SMS OTP staged onboarding, Cognito user creation, admin-confirm bypass for dev, refresh-token rotation persisted in PostgreSQL.
KYC / KYB → financial provisioning
SumSub access token issuance, webhook upserts SumSubStatus, then Integrated Finance external user + bank account, then Monite entity / role / entity user.
Accounts, cards, transfers
Wraps Integrated Finance: client + account + card lifecycle, beneficiary requirements, transfer creation, card transaction authentication approval/reject, IF webhook reconciliation.
Statements & documents
Handlebars HTML → Puppeteer PDF → S3 → presigned URL. Account, confirmation, balance summary, statement of fees and password-protected statement by email.
Phyllo creator data
Create Phyllo user linked to cognitoId, SDK tokens, work platforms, profiles, audience, content, income transactions, payouts and balances. Webhook payloads stored in PhylloData.
Monite invoicing
Entities, roles, entity users, counterparts, products, VAT rates, payment terms, payables, receivables, recurring invoices and PDF generation.
Support & knowledge base
Intercom-backed: ticket lifecycle, replies, tagging, KB collections / sections / articles / search — all brokered server-side.
Notifications
OneSignal push and SMTP email with persisted Notification rows, send status and read/unread state per user.
Signup → login sequence
KYC approved → provider provisioning
Webhook fan-out
Provider events become Lobster-owned state — and an audit trail.
Environment contract
The names of every secret the runtime expects. Values stay in the secret manager.
- NODE_ENV
- PORT
- DATABASE_URL
- FRONTEND_URL
- APP_PATH
- IF_API_BASE_URL
- IF_CLIENT_ID
- IF_INSTANCE_ID
- IF_OIDC_ISSUER
- IF_TOKEN_ENDPOINT
- IF_PRIVATE_KEY_PATH
- SUMSUB_BASE_URL
- SUMSUB_APP_TOKEN
- SUMSUB_SECRET_KEY
- SUMSUB_WEBHOOK_SECRET
- PHYLLO_BASE_URL
- PHYLLO_CLIENT_ID
- PHYLLO_CLIENT_SECRET
- AWS_REGION
- COGNITO_USER_POOL_ID
- COGNITO_CLIENT_ID
- COGNITO_CLIENT_SECRET
- AWS_S3_BUCKET
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- SMTP_HOST
- SMTP_PORT
- SMTP_SECURE
- SMTP_USER
- SMTP_PASS
- MONITE_CLIENT_ID
- MONITE_SECRET
- ONESIGNAL_APP_ID
- ONESIGNAL_REST_API_KEY
- SMS_BYPASS_ENABLED
- HARDCODED_SMS_OTP
- EMAIL_BYPASS_ENABLED
- HARDCODED_EMAIL_OTP
Deployment & operations
Security & production readiness
Findings from the system breakdown, surfaced honestly so they get fixed.
Webhook signature verification
HighSumsub verification is commented out; IF webhook does not verify signatures. Raw-body HMAC must run before any DB write.
Unauthenticated finance routes
HighIF token / client routes, several IF account / beneficiary routes, Monite payables and common routes, and a user admin reset path lack auth.
Rate limiter target path
HighLimiter checks /api/users/signup but real routes are /signup/initiate, /login, /resend/*. Update path matching and broaden coverage.
Sensitive logs
HighSMTP options, Phyllo Basic auth headers, Cognito secret hash, raw webhook payloads logged in clear. Adopt redacted structured logging.
Secret material in repo
High.env.prod and secret-keys/private-key.pem exist locally. .gitignore ignores .env* but not secret-keys/. Rotate and move to a secret manager.
Hardcoded webhook secret fallback
HighSumsub handler has a literal fallback. Require SUMSUB_WEBHOOK_SECRET and fail closed on missing env.
Cognito JWT verification depth
MediumVerifies signature + sub; should also check issuer, audience / client id and token use.
Express 5 with @types/express 4
MediumType drift between runtime and type packages.
Build / deploy config drift
MediumPostgreSQL app paired with Mongo docker-compose; build copies .env but snapshot ships .env.prod.
Stale test suite
MediumOnly test hits GET /api/users which is not in the current route list.
Missing DTO / schema layer
MediumZod installed but not enforced at most route boundaries.
Client ↔ backend route drift
Medium/if/transfers/company vs /organization, /users/sumsub/regenerate/:userId vs /sumsub/regenerate, etc.