---
title: "Tally Forms Alternative: Custom Forms With a Submission API"
description: "A Tally Forms alternative for developers. Build the form in your own frontend, run the logic in your own code, and POST the result to a submission API."
intro: "Forminit is a Tally Forms alternative for teams who want to build the form themselves. You own the markup, the styling and the logic; Forminit gives you an endpoint to POST to, validation, storage, notifications and an inbox."
updated: 2026-09-17
verified: 2026-09-17
---
**The difference is one request.** Tally gives you a form. Forminit gives you an endpoint.

If you want a form on a page and you do not want to write code, Tally is a good product and it is free for a lot of what people need. This page is not for that case. It is for the one where you have already built the interface — the multi-step flow, the configurator, the three emoji buttons under an article, the onboarding wizard inside your app — and now you need somewhere for the data to go.

## What a form builder can't give you

Every form builder, Tally included, owns the last mile. The form is rendered by the builder, on the builder's page or inside an embed, and your job is to configure it. Custom CSS and custom domains change how that rendering looks. They do not change who does the rendering.

That creates a ceiling. You cannot put a builder's form inside a native mobile app without wrapping it in a webview. You cannot make a single tap on a reaction button into a submission, because the unit of interaction is a form, not a tap. You cannot run a pricing rule against your own database mid-flow. And you cannot send data the user is not allowed to change: Tally's hidden fields arrive as URL parameters, which means the person filling the form can read and edit them.

Most importantly, you cannot submit from your own code. Tally's public API is a management API. It lists and creates forms, reads and deletes submissions, returns analytics and manages webhooks and workspaces. There is no documented endpoint that creates a submission. Responses have to come in through a page Tally renders.

The [why Forminit](/docs/why-forminit/) page walks through the rest of that ceiling in detail.

## The programmatic approach

Forminit inverts the relationship. There is no form to render, no embed to style and no builder to fight. There is an endpoint:

```
POST https://forminit.com/f/{formId}
```

It accepts `application/json`, `multipart/form-data` for file uploads, and `application/x-www-form-urlencoded` for a plain HTML form post. The body is a list of typed blocks:

```js
await fetch(`https://forminit.com/f/${FORM_ID}`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    blocks: [
      { type: 'sender', properties: { email, firstName, lastName } },
      { type: 'select', name: 'plan',    value: selectedPlan },
      { type: 'rating', name: 'urgency', value: 4 },
      { type: 'text',   name: 'message', value: message },
    ],
  }),
});
```

That is the whole integration. Forminit validates each block server-side against its type, stores the submission, sends the notifications, runs spam protection and drops the result into an inbox. What produced those values — a React form, a canvas, a keyboard shortcut, a Swift view, a cron job — is entirely your business.

Because the submission is just a request, it can also come from your server, and that is where it stops being a nicer form endpoint and starts being something a builder cannot do at all:

```js
// app/api/onboarding/route.js
export async function POST(req) {
  const session = await auth();                    // you decide who they are
  const { goal, teamSize } = await req.json();     // they decide the rest

  await fetch(`https://forminit.com/f/${FORM_ID}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-KEY': process.env.FORMINIT_API_KEY,   // Protected mode
    },
    body: JSON.stringify({
      blocks: [
        { type: 'sender', properties: { email: session.user.email } },
        { type: 'text',   name: 'user_id',   value: session.user.id },
        { type: 'text',   name: 'org_id',    value: session.org.id },
        { type: 'select', name: 'plan',      value: session.org.plan },
        { type: 'select', name: 'goal',      value: goal },
        { type: 'number', name: 'team_size', value: teamSize },
      ],
    }),
  });

  return Response.json({ ok: true });
}
```

The user filled in two fields. The submission carries six, and the four they did not fill in came from your session rather than from a URL parameter they could have edited. In [Protected mode](/docs/submit-form-api/) the form only accepts requests carrying your API key, so the browser cannot bypass the route and post whatever it likes.

## Ten things you can build this way

Each of these is a normal afternoon's work against a submission endpoint, and none of them is a form in the sense a builder means it.

- **Onboarding that already knows the user.** Ask only what you do not have. Skip the company-size question for anyone who came in through a seat invite, branch on plan or permission, and attach a verified user ID and organization ID to the completed response. Support sees the answers next to the account they belong to.

- **One-tap emoji feedback.** Three reaction buttons under a doc page or a feature. A tap posts a `radio` block and the page ID, then swaps itself for a thank-you. No modal, no iframe, no second page load — and it still shows up as counts and percentages in analytics because the block is typed.

- **Feedback on individual AI responses.** Thumbs up and down on each message, with `conversation_id`, `message_id`, the model and the product area attached. You get a reviewable stream of exactly which answers went wrong, which is not something you can express as a form at all.

- **In-app support with real context.** Your own support panel, opened in place, carrying an authorized order ID, the subscription plan, the app version, the current route and a screenshot. The customer writes one sentence instead of answering six triage questions.

- **Native mobile feedback.** A SwiftUI or Jetpack Compose sheet using native controls, posted through your backend with the release version and screen name. No webview, no browser chrome, no styling that looks borrowed.

- **Quote requests with server-verified numbers.** A configurator or visual selector where your backend re-checks availability and recalculates the price before the request is accepted, then submits a quote ID that matches what your system actually offered. A client-side calculation in a builder is a suggestion; this is a commitment.

- **Checkout and post-purchase surveys triggered by events.** Nobody fills in a form. Your order webhook fires, your server posts the order value, SKU mix and fulfilment time, and the follow-up survey response lands in the same inbox as the rest.

- **In-app NPS at the right moment.** Not on a schedule and not in an email — after the third successful export, or the first invite sent. Your code decides when the prompt appears and what account data rides along with the score.

- **Multi-step and conversational flows that save as they go.** Auto-save each step to local state, restore on return, and post once at the end, or post progressively. The pacing, animation and back-button behaviour are yours because the markup is yours.

- **Client sites that match the design exactly.** For agencies and freelancers: the form is built from the same components as the rest of the site, in whatever stack the project uses — HTML, React, Next.js, Vue, Astro, Webflow, WordPress — or generated wholesale by Lovable, v0 or Bolt and pointed at an endpoint. No branding to remove, no builder DOM to override, no plan upgrade to unlock custom CSS.

## Contact forms, without the compromise

The plain case still works plainly. Keep the markup you have, add the [HTML endpoint](/docs/html/) or the [framework-agnostic SDK](/docs/sdk/), and you are done — no key, no server, [Public mode](/docs/submit-form-api/).

The difference shows up as the form grows. A contact form that opens with selectable service cards, reveals project-specific questions, checks your calendar for availability before accepting an inquiry and refuses a date you cannot staff is still a contact form. In a builder it is a feature request. Here it is code you already know how to write, ending in one POST.

## Where the two products actually differ

| | Forminit | Tally Forms |
|---|---|---|
| **Who renders the form** | You do | Tally, on its page or in an embed |
| **Submission entry point** | `POST /f/{formId}` from any client | Tally-hosted page or embed |
| **Documented API for creating submissions** | Yes | No — the API manages forms and reads submissions |
| **Where logic runs** | Your frontend and backend | Builder conditions and calculations |
| **Attaching application data** | Any supported block, from client or server | Hidden fields via URL parameters |
| **Can the user tamper with that data** | No, in Protected mode | Yes — the values are in the URL |
| **Native mobile** | Direct API call | Webview around a hosted form |
| **Sub-form interactions (one tap, one emoji)** | Yes | No |
| **Server-triggered submissions** | Yes | No |

Tally is better at the thing it is for. If you want a survey live in ten minutes, a payment or signature collected without code, forty-five languages out of the box, or a form built by someone who does not write any, use Tally — it does that well and its free tier is genuinely generous. Forminit is the answer to a different question: *my frontend already exists, where do I POST?*

## Analytics for a form you designed yourself

**A custom interface can still produce structured response analytics.** Map your service cards, dropdowns or emoji buttons to `select`, `radio` or `checkbox` blocks and Forminit reports option **counts and percentages** in submission details and on the Analytics page. Three emoji buttons feed a radio block perfectly well even though nothing about them looks like a form.

Attribution comes along too: submissions broken down by **source, medium and campaign**, with UTM parameters and click IDs such as `gclid`, `fbclid` and `msclkid` captured automatically by the [browser SDK](/docs/sdk/#automatic-tracking) when present. Form Analytics starts on **Pro**.

<figure class="comparison-proof comparison-proof-wide">
  <img src="/blog/changelog/2026-02-24/analytics-dashboard-ss.png" alt="Forminit Analytics showing submission trends, sources, click IDs and a subject breakdown with counts and percentages" width="3840" height="2880" loading="lazy" decoding="async" />
  <figcaption>Response breakdowns and lead attribution for forms you built yourself.</figcaption>
</figure>

## What happens after the POST

The endpoint is the interesting part, but it is not the whole product. Submissions land in a [real inbox](/docs/why-forminit/#6-submissions-are-stored-in-a-real-inbox) you can search full-text, filter, star, mark read or unread, annotate with notes and move through **To do, In progress, Done and Closed**.

[Workspaces](/docs/workspace/) separate clients and teams with **Owner, Admin, Editor and Guest** roles, their own API keys and their own SMTP. [Notification emails](/docs/email-notifications/) are composed in a visual editor with **To, CC, BCC and Reply-to**; Business adds [autoresponders](/docs/autoresponder/) and [custom SMTP](/docs/custom-sender-email-smtp/). [Webhooks](/docs/webhooks/) on Pro are signed with HMAC-SHA256, with delivery attempts and errors visible in **Form Settings → Logs** and [alerts when they fail](/blog/webhook-failure-alerts/).

Files are handled for you, up to 25 MB per submission. Data is hosted on AWS in Ireland; submissions stay until you delete them, with [optional per-form retention](/docs/submission-retention/) on Pro and a separate 30-day expiry for spam.

## Moving over from Tally

Be clear about what this migration is: you are not repointing an embed, you are building a frontend. Budget for that first.

1. **Build the interface** in your own design system and decide which Form Block each value maps to. This is the real work, and it is also the reason you are switching.
2. **Move the logic into code.** Conditions, calculations and skips become ordinary application code. Pick Public mode for a browser-direct form, or Protected mode wherever a value has to be trusted.
3. **Connect and test.** Wire up the [HTML endpoint](/docs/html/) or [SDK](/docs/sdk/), recreate notifications, spam protection and integrations, then walk every conditional path and every error state before you switch traffic.

Export your existing Tally responses and download any attachments you want to keep. Historical submissions, form layouts and logic do not transfer.

[See the submission API →](/docs/submit-form-api/)

## FAQ

### Can I POST submissions to Tally from my own frontend?

Not with the documented public API. Tally's API creates and manages forms, and reads or deletes submissions, but there is no documented endpoint for creating a submission. Responses enter through a Tally-hosted page or embed. Forminit exposes `POST https://forminit.com/f/{formId}`, so any frontend, backend or mobile app can submit.

### Can I attach a user ID or other application data to a submission?

Yes, and you can make it trustworthy. Send `user_id`, `organization_id`, `plan`, `order_id`, `conversation_id` or any other supported Form Block with the response. In Protected mode your backend resolves those values from the session after checking permissions, so the submitter never sees or controls them.

### Do I have to rebuild my form to switch from Tally?

Yes, and that is the point. Forminit does not render a form. You build the interface in your own components and connect it to the endpoint. Swapping an iframe URL will not turn a Tally form into your own UI, so plan the frontend work before you migrate.

### Can I use Forminit without writing a backend?

Yes. Public mode accepts submissions straight from the browser with no API key, which covers most contact and signup forms. Protected mode is for when a value has to be trusted, such as a user ID or a verified price.

## About this comparison

Published by Forminit. Tally's capabilities were checked against its official developer documentation and help centre on **17 September 2026**. Products change; if something here is out of date, [tell us](/contact/).