Krishna KumarandClaude Fable 5 e73b4f7e34 Add optional message field to /submit for iOS review intercept
- /submit now requires email OR message (email-only calls unchanged)
- message trimmed + capped at 10k chars; stored via guarded ALTER TABLE migration
- /export CSV gains message column with proper quoting/escaping
- test suite (node --test): 9 tests covering compat, validation, truncation, CSV escaping

Ticket: T2 of review-flow-10x epic (chatai-ios#136)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WPnLnXS4YRz9vVt1BEdeR
2026-07-22 12:22:30 -05:00

Form Capture

Lightweight form capture microservice. ~100 lines, SQLite storage, zero config.

Deploy to Railway

Deploy on Railway

  1. Create new project on Railway
  2. Connect this repo
  3. Add environment variables (optional):
    • API_KEY - Protect admin endpoints (recommended)
    • ALLOWED_ORIGINS - Comma-separated origins (default: https://chatlabsai.com)
  4. Add a volume mounted at /app/data for persistent storage
  5. Deploy

Environment Variables

Variable Default Description
PORT 3000 Server port
API_KEY none Protect /emails, /export, /stats
ALLOWED_ORIGINS https://chatlabsai.com CORS origins (comma-separated)
DATABASE_PATH ./data/forms.db SQLite database path

API Endpoints

Submit Form

POST /submit
Content-Type: application/json

{
  "email": "user@example.com",  # optional if message present
  "form_name": "newsletter",    # optional, default: "default"
  "source": "homepage",         # optional
  "message": "free-text..."     # optional, max 10k chars; required if email absent
}

Either email or message is required. Message-only submissions (e.g. form_name: "review_intercept" from the iOS negative-review intercept) store an empty email.

List Submissions (protected)

GET /emails?form_name=newsletter&limit=100&offset=0
Authorization: Bearer YOUR_API_KEY

Export CSV (protected)

GET /export?form_name=newsletter
Authorization: Bearer YOUR_API_KEY

Stats (protected)

GET /stats
Authorization: Bearer YOUR_API_KEY

Health Check

GET /health

Frontend Usage

// Submit form
const response = await fetch('https://your-app.railway.app/submit', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'user@example.com',
    form_name: 'android-waitlist',
    source: window.location.pathname
  })
});

if (response.ok) {
  // Show success message
}

Local Development

npm install
npm start

Test submission:

curl -X POST http://localhost:3000/submit \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com", "form_name": "test"}'
S
Description
Lightweight form capture microservice
Readme
48 KiB
Languages
JavaScript 100%