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 theX-Form-Token header instead.

contact.html
<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[]" withmultiple. The file is persisted only if the submission as a whole validates.

upload.html
<!-- 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 viaPOST /api/v1/forms/{id}/files asmultipart/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).

upload-step-1.js
// 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.

upload-step-2.js
// 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: "[email protected]",
    _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 theX-Form-Token header.

submit.js
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: "[email protected]",
      message: "Hello!",
    }),
  }
);

const data = await response.json();
console.log(data); // { data: { id: "...", ... } }

Or with cURL:

terminal
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": "[email protected]"}'

5. Custom redirect

Add a hidden _redirect field to send users to a custom thank-you page after submission. Its origin must also be listed in the Form'sallowed_origins setting.

form.html
<!-- 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, or when its origin is not owner-allowed, 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 / HTTP Request payloads and email notifications: 60 minutes
  • Google Sheets cells link to the authenticated submission detail in the dashboard.

Account limits

AccessPer-file sizeActive submissions per accountStorage per accountPer-form hourly rateAllowed MIME types
Free3 MB1,500100 MB30 / hourJPEG, PNG, GIF, WebP, PDF, TXT, CSV
Paid10 MB100,00010 GB1,000 / hourFree types + ZIP, GZIP, TAR, JSON, DOCX, XLSX, PPTX

Each file field can further restrict allowed_mime, cap max_files, and togglemultiple. Field-level rules must be a subset of your plan's allowlist. A file field accepts at most 20 filesper submission; fields without multipleaccept exactly one file. Active submission and storage totals are shared across the account; hourly rates remain per form, and separate per-IP anti-abuse rate limits also apply.

Async orphan caps

To prevent abuse of the two-step flow, each user can hold at most100 orphan files totalling 200 MB at any time. Hit either cap and /files returns429 orphan_quota_exhausteduntil existing orphans are attached or expire.

Endpoint Reference

EndpointBodyAuthUse Case
POST /f/{id}multipart / urlencoded_token fieldHTML form submissions (sync, supports files)
POST /api/v1/forms/{id}/submitJSONX-Form-TokenProgrammatic submit (use _files for async file IDs)
POST /api/v1/forms/{id}/filesmultipartX-Form-TokenAsync 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[email protected].