Changed terminology from "API" to "Routes" to better reflect server-rendered HTML approach: - Renamed submission_api.md → submission_routes.md - Renamed dashboard_api.md → dashboard_routes.md - Renamed admin_api.md → admin_routes.md - Updated headers to clarify "Response Type: Server-rendered HTML (no JavaScript required)" - Updated references in plan.md and quickstart.md This clarifies that the application uses traditional web routes with form submissions and HTML responses, not REST API endpoints with JSON. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
5.3 KiB
Submission Routes Contract
Scope: Anonymous feedback submission web routes (User Story P1) Authentication: None (anonymous access) Response Type: Server-rendered HTML (no JavaScript required)
GET /submit/{product_slug}
Display the feedback submission form for a specific product.
Request
Path Parameters:
product_slug(string, required): Product's URL-safe identifier
Headers: None required
Query Parameters: None
Response
Success (200 OK):
Content-Type: text/html
<!DOCTYPE html>
<html>
<head><title>Submit Feedback - {Product Name}</title></head>
<body>
<h1>Submit Feedback for {Product Name}</h1>
<form method="POST" action="/submit/{product_slug}" enctype="multipart/form-data">
<textarea name="feedback_text" maxlength="10000"></textarea>
<input type="file" name="attachments" multiple accept=".pdf,.docx,.txt,.jpg,.png,.gif,.webp">
<button type="submit">Submit Feedback</button>
</form>
</body>
</html>
Error (404 Not Found): Product does not exist or is archived
Content-Type: text/html
<!DOCTYPE html>
<html>
<body>
<h1>Product Not Found</h1>
<p>The product you're looking for does not exist or is no longer accepting feedback.</p>
</body>
</html>
Functional Requirements Covered
- FR-001: Public, unauthenticated submission form
- FR-002: Text input up to 10,000 characters
- FR-004: Up to 3 file attachments
POST /submit/{product_slug}
Submit anonymous feedback for a specific product.
Request
Path Parameters:
product_slug(string, required): Product's URL-safe identifier
Headers:
Content-Type: multipart/form-data
Form Data:
feedback_text(string, optional): Feedback text (0-10,000 characters)attachments(file[], optional): Up to 3 files, max 10MB each
Example:
POST /submit/acme-app HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="feedback_text"
The app crashes when uploading files over 5MB.
------WebKitFormBoundary
Content-Disposition: form-data; name="attachments"; filename="screenshot.png"
Content-Type: image/png
[binary data]
------WebKitFormBoundary--
Response
Success (200 OK):
Content-Type: text/html
<!DOCTYPE html>
<html>
<body>
<h1>Thank You!</h1>
<p>Your feedback has been submitted successfully.</p>
<p>Your feedback ID: {feedback_id}</p>
</body>
</html>
Error (400 Bad Request): Validation failure
Content-Type: text/html
<!DOCTYPE html>
<html>
<body>
<h1>Submission Error</h1>
<ul>
<li>Feedback must contain text or at least one attachment</li>
<li>Maximum 3 attachments allowed</li>
<li>Text cannot exceed 10,000 characters</li>
<li>File size cannot exceed 10MB per file</li>
<li>Unsupported file type: {filename}</li>
</ul>
</body>
</html>
Error (413 Payload Too Large): File size exceeds limit
Content-Type: text/html
HTTP/1.1 413 Payload Too Large
<!DOCTYPE html>
<html>
<body>
<h1>File Too Large</h1>
<p>One or more files exceed the 10MB limit.</p>
</body>
</html>
Error (429 Too Many Requests): Rate limit exceeded
Content-Type: text/html
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1697456789
<!DOCTYPE html>
<html>
<body>
<h1>Too Many Submissions</h1>
<p>You have exceeded the submission limit of 10 per hour. Please try again later.</p>
</body>
</html>
Error (451 Unavailable For Legal Reasons): Malware detected
Content-Type: text/html
<!DOCTYPE html>
<html>
<body>
<h1>Security Error</h1>
<p>One or more files failed security scanning. Please ensure your files are safe and try again.</p>
</body>
</html>
Validation Rules
-
Text Validation (FR-002, FR-003):
- Length: 0-10,000 characters
- Encoding: UTF-8, any language accepted
- Empty allowed if attachments present
-
Attachment Validation (FR-004, FR-005, FR-006):
- Count: 0-3 files
- Size: Max 10MB per file
- Types: PDF, DOCX, TXT, JPG, PNG, GIF, WebP
- MIME type validation (server-side)
-
Submission Validation (FR-011, FR-012):
- Must have text OR attachments (not both empty)
-
Security (FR-059, FR-060):
- ClamAV virus scan before storage
- Filename sanitization (remove path traversal)
- MIME type validation
-
Rate Limiting (FR-061):
- 10 submissions per hour per IP address
Functional Requirements Covered
- FR-001 to FR-012: Complete submission flow
- FR-055: No IP/session tracking stored
- FR-059, FR-060: File security
- FR-061: Rate limiting
Side Effects
-
File System:
- Creates
data/products/{product_id}/feedback/{feedback_id}/ - Writes
metadata.yaml,content.txt - Writes
attachments/{filename}if files uploaded
- Creates
-
Async Processing:
- Triggers AI analysis background job
- Updates feedback status:
submitted→analyzing
POST /submit/{product_slug}/status/{feedback_id}
Note: This endpoint is OUT OF SCOPE for MVP. Feedback submission is fire-and-forget. Users cannot track submission status anonymously.
Future consideration: Anonymous status check via feedback ID (requires balancing anonymity with user experience).