Features
Everything the app does today.
Below is a faithful summary of behavior in the codebase — not a roadmap slide.
Instant JSON endpoints
Each form exposes POST /api/s/{slug} with application/json only. No API key is required for public submission (ownership is enforced on reads and exports).
Spam scoring
Submissions are scored from field names and values: honeypot-style field names, URL density in text, suspicious keywords, and repeated-character patterns. When the score reaches the configured threshold, the request is rejected as spam (HTTP 202) and counted on the form without creating a submission row.
Form lifecycle
Forms have a unique slug, optional description, and an ACTIVE or PAUSED status. Paused forms return FORM_PAUSED and do not accept new payloads.
Dashboard analytics
Per-form dashboard shows volume stats, a time-range chart for accepted submissions, and the exact endpoint URL using your configured APP_URL (or localhost in development).
Pause and resume
Toggle acceptance from the form dashboard without deleting the form or losing history.
Export submissions
Signed-in owners can download filtered submissions as JSON or CSV via GET /api/forms/{id}/submissions/export with optional status and date range query parameters.
Email alerts (planned)
The schema includes submissionEmailAlerts on forms for future owner notifications; delivery is not wired in the application yet.
Payload rules (public API)
Bodies must be a single JSON object whose values are only strings, numbers, booleans, or null — no nested objects or arrays. Maximum encoded size is 5KB. Wrong content types, malformed JSON, or invalid shapes return structured error payloads with HTTP 4xx codes.