Quick Start Guide
Get up and running with Submeto in under 2 minutes.
1. Create your account
Sign up for free and create your first form endpoint from the dashboard. You'll receive a unique Form ID and a Secret Token for API access.
2. Add the form to your site
Set your HTML form's action attribute
to your Submeto endpoint. No JavaScript required.
Authenticate the form with a hidden _token field
carrying your form's Secret Token. HTML forms can't send custom headers, so the token
must travel in the body — for JSON/API requests use the
X-Form-Token header instead.
<form action="https://api.submeto.com/f/YOUR_FORM_ID" method="POST">
<input type="hidden" name="_token" value="YOUR_FORM_TOKEN" />
<input type="text" name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message"></textarea>
<button type="submit">Send</button>
</form> 3. File uploads
Submeto supports two upload modes. Pick the one that matches how you submit:
- Sync (multipart) — for HTML forms. Files are uploaded together with the submission in a single request.
- Async (two-step) — for JSON / SPA / server-to-server flows. Files are uploaded first, then referenced by ID in the JSON submit payload.
3a. Sync uploads (HTML forms)
Add enctype="multipart/form-data" to
your form. For multi-file fields, use name="field[]" with
multiple.
The file is persisted only if the submission as a whole validates.
<!-- Single and multiple file fields. Use `name[]` for multi-file inputs. -->
<form
action="https://api.submeto.com/f/YOUR_FORM_ID"
method="POST"
enctype="multipart/form-data"
>
<input type="hidden" name="_token" value="YOUR_FORM_TOKEN" />
<input type="text" name="name" required />
<input type="file" name="resume" />
<input type="file" name="screenshots[]" multiple />
<button type="submit">Upload</button>
</form> 3b. Async uploads (JSON / SPA)
Use this flow when the submission body is JSON. First upload each file via
POST /api/v1/forms/{id}/files as
multipart/form-data with
two parts:
a field_name matching the
form's file field, and the binary file.
The response returns a file ID with an expiry timestamp (12 hours by default).
// Step 1 — upload the file. Returns a file ID with a 12-hour TTL.
const uploadBody = new FormData();
uploadBody.append("field_name", "resume");
uploadBody.append("file", fileInput.files[0]);
const uploadRes = await fetch(
"https://api.submeto.com/api/v1/forms/YOUR_FORM_ID/files",
{
method: "POST",
headers: {
Accept: "application/json",
"X-Form-Token": "YOUR_FORM_TOKEN",
},
body: uploadBody,
}
);
const { data: uploaded } = await uploadRes.json();
// uploaded.id => "01HW2..."
// uploaded.expires_at => "2026-04-29T22:00:00Z"
Then submit the form. Pass the returned IDs through the _files object,
keyed by field name. Other fields go alongside as usual.
// Step 2 — submit the form, referencing uploaded file IDs in `_files`.
await fetch("https://api.submeto.com/api/v1/forms/YOUR_FORM_ID/submit", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"X-Form-Token": "YOUR_FORM_TOKEN",
},
body: JSON.stringify({
name: "Jane Doe",
email: "jane@example.com",
_files: {
resume: [uploaded.id],
},
}),
}); On a successful submit, the file is atomically attached to the new submission and its TTL is cleared. If the submit fails validation, the file remains uploaded with a short grace period so you can retry.
4. Programmatic API access
For JavaScript apps or server-to-server integrations, use the REST API endpoint.
Pass your form's secret token in the
X-Form-Token header.
const response = await fetch(
"https://api.submeto.com/api/v1/forms/YOUR_FORM_ID/submit",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Form-Token": "YOUR_FORM_TOKEN",
},
body: JSON.stringify({
name: "Jane Doe",
email: "jane@example.com",
message: "Hello!",
}),
}
);
const data = await response.json();
console.log(data); // { data: { id: "...", ... } } Or with cURL:
curl -X POST https://api.submeto.com/api/v1/forms/YOUR_FORM_ID/submit \
-H "Content-Type: application/json" \
-H "X-Form-Token: YOUR_FORM_TOKEN" \
-d '{"name": "Jane", "email": "jane@example.com"}' 5. Custom redirect
Add a hidden _redirect field
to send users to a custom thank-you page after submission.
<!-- Redirect after submission -->
<form action="https://api.submeto.com/f/YOUR_FORM_ID" method="POST">
<input type="hidden" name="_token" value="YOUR_FORM_TOKEN" />
<input type="hidden" name="_redirect" value="https://yoursite.com/thank-you" />
<input type="text" name="name" required />
<button type="submit">Send</button>
</form>
Without _redirect,
browser submissions land on Submeto's default success page, and any error (bad token, rate limit,
validation, etc.) shows a friendly error page instead of raw JSON. Programmatic requests to
/api/v1/forms/{id}/submit always
receive JSON.
How files are managed
Storage & lifecycle
- Uploaded files are stored on Cloudflare R2 (S3-compatible object storage).
- Sync uploads are persisted only if the entire submission validates; otherwise the file is rolled back so storage is never charged for failed submits.
- Async uploads start as orphan files with a 12-hour TTL. On submit they atomically transition to attached and the TTL is cleared. Orphans that are never attached are auto-deleted hourly.
- When a submission is deleted, its attached files are deleted too (via soft-delete cascade, then permanently purged after the retention window).
Download URLs
Submeto never exposes raw storage paths. File downloads use short-lived presigned URLs:
- Submission API responses: 15 minutes
- Webhook payloads: 60 minutes
- Email notifications and Google Sheets cells link via the dashboard for stable, auth-checked access.
Plan limits
| Plan | Per-file size | Allowed MIME types |
|---|---|---|
| Free | 3 MB | JPEG, PNG, GIF, WebP, PDF, TXT, CSV |
| Starter | 10 MB | Free tier + ZIP, GZIP, TAR, JSON |
| Pro | 10 MB | Starter tier + DOCX, XLSX, PPTX |
Each file field can further restrict allowed_mime,
cap max_files, and toggle
multiple. Field-level rules
must be a subset of your plan's allowlist.
Async orphan caps
To prevent abuse of the two-step flow, each user can hold at most
100 orphan files totalling 200 MB at any time.
Hit either cap and /files returns
429 orphan_quota_exhausted
until existing orphans are attached or expire.
Endpoint Reference
| Endpoint | Body | Auth | Use Case |
|---|---|---|---|
| POST /f/{id} | multipart / urlencoded | _token field | HTML form submissions (sync, supports files) |
| POST /api/v1/forms/{id}/submit | JSON | X-Form-Token | Programmatic submit (use _files for async file IDs) |
| POST /api/v1/forms/{id}/files | multipart | X-Form-Token | Async file upload (returns ID + TTL) |
Supported Content Types
- application/x-www-form-urlencoded — Standard HTML forms
- multipart/form-data — Forms with file uploads
- application/json — API/JavaScript submissions
Using an AI coding assistant?
Share this page's URL with your AI tool (e.g. Claude Code, Cursor, Copilot) and it can read the documentation to integrate Submeto into your project automatically.
https://submeto.com/docs Need help?
If you run into any issues, reach out to us at support@submeto.com.