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.

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[]" with multiple. 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 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).

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: "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.

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: "jane@example.com",
      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": "jane@example.com"}'

5. Custom redirect

Add a hidden _redirect field to send users to a custom thank-you page after submission.

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, 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.