Technical Architecture
Runtime architecture, data model, processing pipeline, and operational boundaries behind Steve's HTTP APIs.
Runtime topology
Monorepo layout
| Path | Purpose |
|---|---|
apps/admin | Admin web application |
apps/mobile | Mobile application |
convex | Backend schema, functions, HTTP routes, cron jobs |
packages/shared-types | Shared types and schema helpers |
packages/tsconfig | Shared TypeScript configuration |
packages/eslint-config | Shared lint configuration |
landing | Static marketing site |
Core data model
Identity
Users are stored in users, with profile separation by role:
adminUserscustomers
Legacy role and isActive fields still exist on users for compatibility with older data, but the profile tables are the authoritative authorization model.
Workflow model
Important tables:
workflows: metadata, active flag, latest version pointer, Open Loyalty schema IDworkflowVersions: immutable six-stage config snapshotssubmissions: current state, extracted data, fraud state, review state, sync statesubmissionFiles: original and enhanced R2 URLs, pHash, EXIF metadatasubmissionEvents: audit trailsubmissionFraudMatches: structured fraud findings and reviewer resolution
Supporting tables include:
fraudConfigimageModerationConfigaiModelConfigproviderAvailabilitytokenUsagenotificationsnotificationReadStatusapiKeysapiSessionsrateLimitBuckets
Processing pipeline
The main orchestration lives in convex/engine/process.ts.
Stage flow
- Load submission, files, workflow version, and workflow metadata.
- Move status to
processingand logprocessing_started. - If global image moderation is enabled, run an explicit/disturbing-image pre-check before workflow processing continues.
- Compute and persist perceptual hashes early to reduce fraud race conditions.
- Optionally enhance uploaded images and write enhanced URLs back to
submissionFiles. - Resolve the AI model:
- workflow-specific stage override if present
- otherwise fall back to global AI config
- Run AI analysis through
engine/analyze.ts. - Record extracted data, confidence, certainty, summary, and token usage.
- Run fraud checks if enabled.
- Decide the terminal state for the processing pass:
- auto-approve
- send to review
- fail
- If approved and sync is enabled, call
engine/sync.ts.
If moderation blocks a submission, the pipeline exits immediately, marks the submission failed, records moderation-specific events, and skips enhancement, AI extraction, fraud checks, and sync.
Workflow stage configuration
Each workflow version stores six stage configs:
| Stage | Purpose |
|---|---|
| Upload | File count, labels, formats, size, instructions |
| Enhancement | Image preprocessing before AI |
| AI analysis | Prompt, system prompt, output schema, reference images, model overrides |
| Fraud detection | Visual duplicate and data duplicate controls |
| Review | Editable fields, instructions, auto-approve threshold |
| Sync | Open Loyalty event name, field mapping, static fields |
Image moderation is intentionally not a workflow-version stage. It is a global system configuration, managed through imageModerationConfig, so every workflow can be protected by the same pre-step and prompt policy.
AI architecture
Providers
The provider registry is code-defined:
openrouter
Model selection
The analysis pipeline supports:
- workflow-level provider and model overrides
- optional fallback provider and model
- global defaults in
aiModelConfig
Image moderation uses its own global AI pipeline ID, image_moderation. Its default provider is OpenRouter, and its prompt comes from imageModerationConfig rather than from the workflow version.
Token usage
Moderation requests are tracked separately under sourceType: image_moderation, so the admin usage dashboard can break out moderation traffic from OCR or other workflow analysis calls.
Today the active provider set contains only OpenRouter. The provider and fallback selectors remain in place so additional providers can be reintroduced later without redesigning the workflow configuration UI.
Today the active provider set contains only OpenRouter. The provider and fallback selectors remain in place so additional providers can be reintroduced later without redesigning the workflow configuration UI.
Test runs
Super admins can upload ad hoc images in the workflow builder and run unsaved prompt/schema configs through engine/testRun.ts. Those runs also record token usage.
Fraud detection
Fraud checks combine:
- visual duplicate detection via perceptual hashing
- data duplicate detection against extracted values
Findings are stored in submissionFraudMatches, not just embedded in a single submission document. Approval is blocked while unresolved fraud matches remain.
Storage model
Files are stored in Cloudflare R2, not Convex file storage.
Patterns:
- clients request presigned upload URLs
- clients upload directly to R2
- backend stores public R2 URLs on submission records
- enhancement and test-run flows also upload server-side to R2
Key helpers live in:
convex/r2Storage.tsconvex/lib/r2.ts
Daily orphan cleanup acts as a safety net for abandoned uploads.
Auth and authorization
Authentication
- Convex Auth with password provider
- admin web stores auth state through Convex React
- mobile stores auth tokens in Expo SecureStore
Authorization
Role checks are centralized in convex/lib/roles.ts:
super_adminadminreviewer- customer users via
customers
Notable rules:
- self-registration is intentionally locked down for non-super-admin accounts
- only invited customers get valid customer profiles
- only super admins can use the workflow agent and some workflow-admin actions
Notifications and auditability
- Submission notifications are stored in
notifications - Per-user read state is stored in
notificationReadStatus - Submission audit history is stored in
submissionEvents - Review detail pages surface both timeline events and fraud-comparison context
Open Loyalty integration
There are two integration paths:
1. Schema push
workflowSchemaSync.ts creates or updates custom-event schemas in Open Loyalty based on the saved workflow sync config and output schema.
2. Submission sync
engine/sync.ts sends approved submissions as custom events. Sync uses the workflow's latest sync-stage config rather than the submission's pinned version, so integration mapping changes apply immediately.
HTTP API surface
Implemented in convex/http.ts:
| Route | Audience | Notes |
|---|---|---|
POST /api/agent/chat | Internal admin tooling | Super admin only, streaming |
GET /api/invitation/validate | Invite onboarding | Public |
POST /api/invitation/complete | Invite onboarding | Public |
GET /api/v1/companies | External API | API key auth |
GET /api/v1/jobs/{sessionId} | External API | API key auth |
POST /api/v1/webhooks/test | External API | API key auth |
The larger session-based upload API described in the repository is still planned rather than mounted.
Scheduled jobs
Cron jobs currently handle:
- orphaned R2 object cleanup
- expired API session cleanup
- expired rate-limit bucket cleanup
- expired draft submission cleanup
- expired trash cleanup
- deleted notification cleanup
Delivery and current gaps
Current automated coverage is still thin:
- admin smoke test
- Convex API-auth tests
There is no broad end-to-end or CI coverage yet for:
- workflow processing
- HTTP routes
- webhook delivery
- full monorepo lint/typecheck/test gates