Students pick an assignment, upload a file, submit. Teachers get a dashboard showing who turned in what.
Updated March 2026
Collecting homework by email is a mess. Who submitted what? Did they attach the right file? Good luck finding it later. This gives students a form: pick the assignment week, upload a file, add optional notes. Teachers get a dashboard with submission counts per assignment, a timeline, and a table with download links. Takes about 15 minutes to set up. An agent can set this up end-to-end: create the form with file upload fields, build the teacher dashboard with bar charts grouped by assignment.
Architecture
| Tool | Role | Cost |
|---|---|---|
| Cloudflare Pages | Static site hosting | Free |
| Sutrena | File upload, submission storage, dashboard | $9/mo (Builder) |
Total cost: $9/mo
Builder starts at $9/month for 50 projects. Pro at $29/month covers 200 projects and all your other work too. Build five blueprints on Pro and it is still $29/month. That is where the value is — at scale.
A single HTML page on Cloudflare Pages. Students fill in their name, email, select the assignment week, upload their file, and submit. Files go through presigned URLs, so they go straight to storage without touching your server.
The teacher dashboard has a bar chart of submissions per assignment, a running total, and a full table with names, emails, assignments, and file links. Teachers can export everything as CSV.
Form Definition
Student name, email, assignment week (select dropdown so it groups cleanly in the dashboard), file upload (PDF, DOC, DOCX, or ZIP up to 10 MB), and optional notes.
{
"name": "Homework Submissions",
"fields": [
{
"name": "student_name",
"label": "Student Name",
"type": "text",
"required": true
},
{
"name": "student_email",
"label": "Student Email",
"type": "email",
"required": true
},
{
"name": "assignment",
"label": "Assignment",
"type": "select",
"options": [
"Week 1",
"Week 2",
"Week 3",
"Week 4"
],
"required": true
},
{
"name": "file",
"label": "Upload File",
"type": "file",
"accept": ".pdf,.doc,.docx,.zip",
"maxFileSize": 10485760,
"required": true
},
{
"name": "notes",
"label": "Notes (optional)",
"type": "textarea"
}
]
}Dashboard Definition
Total count, bar chart by assignment week, daily timeline, and a table of everything sorted newest first. Student names, emails, assignment weeks, and notes all visible.
{
"version": 1,
"widgets": [
{
"type": "metric_card",
"title": "Total Submissions",
"value": "count(*)"
},
{
"type": "bar_chart",
"title": "Submissions per Assignment",
"groupBy": "assignment"
},
{
"type": "line_chart",
"title": "Submissions Over Time",
"groupBy": "$submitted_at:day"
},
{
"type": "data_table",
"title": "All Submissions",
"columns": [
"student_name",
"student_email",
"assignment",
"notes"
],
"limit": 100,
"sortBy": "$submitted_at",
"sortOrder": "desc"
}
]
}Frontend Integration
An HTML form that handles file upload via presigned URL, then submits the rest of the data. Three steps in one submit handler: get upload URL, upload file, submit form. Replace frm_YOUR_FORM_ID with yours.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Homework Submission</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 500px; margin: 2rem auto; }
label { display: block; margin-top: 1rem; font-weight: 600; }
input, select, textarea { width: 100%; padding: 0.5rem; margin-top: 0.25rem; }
button { margin-top: 1.5rem; padding: 0.75rem 2rem; cursor: pointer; }
</style>
</head>
<body>
<h1>Submit Homework</h1>
<form id="hw-form">
<label>Student Name <input name="student_name" required /></label>
<label>Student Email <input name="student_email" type="email" required /></label>
<label>Assignment
<select name="assignment" required>
<option value="">Select week</option>
<option>Week 1</option>
<option>Week 2</option>
<option>Week 3</option>
<option>Week 4</option>
</select>
</label>
<label>Upload File <input name="file" type="file" accept=".pdf,.doc,.docx,.zip" required /></label>
<label>Notes <textarea name="notes" rows="3"></textarea></label>
<button type="submit">Submit</button>
</form>
<p id="status"></p>
<script>
const FORM_ID = "frm_YOUR_FORM_ID";
document.getElementById("hw-form").addEventListener("submit", async (e) => {
e.preventDefault();
const status = document.getElementById("status");
status.textContent = "Uploading...";
const fd = new FormData(e.target);
const file = fd.get("file");
// 1. Get presigned upload URL
const uploadRes = await fetch(
`https://sutrena.com/api/forms/${FORM_ID}/upload`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
field: "file",
filename: file.name,
contentType: file.type,
}),
}
);
const { uploadUrl, fileRef } = await uploadRes.json();
// 2. Upload file
await fetch(uploadUrl, {
method: "PUT",
headers: { "Content-Type": file.type },
body: file,
});
// 3. Submit form with file reference
const res = await fetch(
`https://sutrena.com/api/forms/${FORM_ID}/submit`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
student_name: fd.get("student_name"),
student_email: fd.get("student_email"),
assignment: fd.get("assignment"),
file: fileRef,
notes: fd.get("notes"),
}),
}
);
status.textContent = res.ok
? "Submitted successfully!"
: "Something went wrong. Please try again.";
});
</script>
</body>
</html>PDF, DOC, DOCX, and ZIP up to 10 MB. Change accept and maxFileSize when creating the form if you need different types or sizes.
By default, yes. To prevent that, add uniqueBy: ["student_email", "assignment"] to the form config. Whether you want that depends on your class.
File references include download URLs accessible via the API with your API key. Teachers can also export as CSV and grab the file links from there.
Yes. PATCH the form to add more options to the assignment field. Existing submissions stay as they are.
Sutrena is the web runtime for AI agents. Three primitives — pages, forms, and dashboards — accessible through one API. Your agent creates web artifacts, humans interact with them, and your agent gets the data back. Framework-agnostic. Works from any MCP client or HTTP client.
1. Get a trial key (no auth, no signup)
curl -X POST https://sutrena.com/api/trial2. Create a form + dashboard from a template
curl -X POST https://sutrena.com/api/forms \
-H "Authorization: Bearer st_trial_xxx" \
-H "Content-Type: application/json" \
-d '{"templateId": "waitlist", "createDashboard": true}'Get a trial API key instantly with no signup, or create an account for the full experience.