Migrate · Basin
Migrate from Basin to Formward
Basin has the same shape as Formward: an HTML form posts to https://usebasin.com/f/ plus a form ID, and every named field is captured. Basin's default honeypot is _gotcha, which Formward reads under the same name.
Basin sets the redirect in its dashboard and uses a field named email for the reply-to address and the autoresponse. If you use BasinJS for in-page success messages, a small fetch() handler replaces it.
Last checked 7 October 2026. What this guide says about Basin comes from Basin's own documentation, listed under Sources. Check their current docs if something looks different in your account. The Formward side is the reference in the migration docs.
The change
Swap the action URL. Add _redirect where Basin handled it from its dashboard. The email field keeps setting Reply-To, so there is nothing to rename for it.
Before: Basin, HTML
<form action="https://usebasin.com/f/YOUR_FORM_ID" method="POST">
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<input type="hidden" name="_gotcha">
<button type="submit">Send</button>
</form>After: Formward, HTML
<form action="https://forms.formward.eu/f/<formId>" method="POST">
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<input type="hidden" name="_redirect" value="/thanks">
<input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">
<button type="submit">Send</button>
</form>Before: Basin, BasinJS
<script src="https://js.usebasin.com/v2.11.1.min.js" defer></script>
<form action="https://usebasin.com/f/YOUR_FORM_ID" method="POST"
data-basin-form data-basin-success-action="render">After: Formward, fetch()
const res = await fetch("https://forms.formward.eu/f/<formId>", {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});
const data = await res.json();
if (!data.ok) throw new Error(data.error);Field and feature mapping
| Basin | Formward | Notes |
|---|---|---|
| https://usebasin.com/f/{id} | https://forms.formward.eu/f/<formId> | The only required change to the form tag. |
| _gotcha | _gotcha | Same name. If you set a custom honeypot name in Basin, switch back to _gotcha. |
| email (Reply-To, autoresponse) | email (same) | A field named email sets Reply-To on Formward too; _replyto overrides it. The autoresponder is set up on the form. |
| Redirect URL (dashboard) | _redirect or the form's redirect | Formward redirects to the URL as written; there are no placeholders. |
| CC and BCC (dashboard) | Extra notification recipients | From Personal. |
| data-basin-form (BasinJS) | fetch() | Show your own success message after { ok: true }. |
| reCAPTCHA, hCaptcha or Turnstile | cf-turnstile-response | Formward supports Cloudflare Turnstile, optional per form. |
| attachments[] with multiple | file input, multipart | Uploads are on Professional and up (5 files of up to 10 MiB each on Professional). |
What changes, what stays
Changes
- The action URL.
- The redirect moves from Basin's dashboard to a _redirect field or the Formward form.
- BasinJS is replaced by a fetch() handler.
Stays the same
- The _gotcha honeypot.
- The email field, which still sets Reply-To.
- Your other field names; every named field is stored as sent.
- multipart/form-data for forms with files, and Accept: application/json for JSON responses.
Differences to test before you switch
Specific to Basin
- Basin accepts uploads on every plan and files up to 100 MB each. Formward starts uploads on Professional with a 10 MiB per-file limit there, so check forms that take large files.
- Basin's redirect URL can use Handlebars placeholders such as {{name}}. Formward redirects to the URL as written.
- Basin uses the email field for both the reply-to address and the autoresponse. On Formward the email field sets Reply-To the same way, with _replyto as an override; the autoresponder is set up on the form.
How Formward behaves
- JSON responses. Send Accept: application/json and a success is a 200 with { ok: true, id }. An error is { ok: false, error } with a status code. Without that header, a successful post redirects or shows Formward's thanks page. Code that only checks res.ok keeps working.
- Status codes. 402 when the plan's monthly submission limit is reached (the body has upgrade: true), 422 when a field fails a validation rule set on the form or required consent is missing, 429 when one address posts too often (with a Retry-After header), and 403 when the origin is not allowed, the Turnstile check fails or files are sent on a plan without uploads.
- File uploads. Uploads need enctype="multipart/form-data" and a plan that includes them: Professional and up (5 files of up to 10 MiB each on Professional). On Free and Personal a post that carries files is refused with 403, so test an upload before you switch a form that takes attachments.
- Redirects. A hidden _redirect is followed only if it is a relative path or a URL on one of the form's allowed origins; otherwise it is ignored and the redirect set on the form applies, then Formward's thanks page. JSON (AJAX) requests are never redirected.
- Spam. If the _gotcha honeypot is filled in, the sender gets a normal success response, but the submission is stored as spam, sends no notification and does not count toward your quota. Rate limits apply on every plan; Cloudflare Turnstile and disposable-email blocking are optional per form.
- Allowed origins. With the list empty, any site can post to the endpoint. Once you add an origin, requests whose Origin header does not match are refused with 403, including server-side requests that send no Origin header.
- Reply-To. The Reply-To of the notification email is taken from _replyto, then _reply_to, then a field named email. So a plain email field sets Reply-To with no rename, and _replyto only overrides it, for example when the email field has another name or the form has several. An invalid address is dropped, and the email then has no Reply-To.
Every status code and body is listed in responses and redirects.
History, integrations and settings
- Basin's docs describe downloading submissions as CSV or XLSX; choose CSV for Formward. Formward's CSV importer (Dashboard, Import & migrate) takes any CSV with a header row, up to 5,000 rows and 10 MB per file. Imported rows send no email and do not count toward your quota. Each row keeps its original date when the file has a column named Submitted At, created_at or createdAt; otherwise it is dated at import time. Attached files are not imported, only cell values.
- Forms on the Free plan keep submissions for 30 days, and the nightly retention sweep deletes imported rows older than a form's retention window. To keep a longer history, import into a form on a paid plan with a long enough window.
- The form importer on the same page reads your original Basin HTML form and creates an inactive draft with the fields mapped. Notification addresses, webhooks, autoresponders and spam settings are listed for you to review and are never switched on by the import.
- Webhooks, Zapier, Google Sheets and Slack connections, the autoresponse and CC recipients belong to your Basin account and do not move with the endpoint. Recreate the ones you need: Formward webhooks (including Slack, Discord, Teams and Mattermost), Google Sheets and Baserow sync start on Professional, the autoresponder on Professional, and extra notification recipients on Personal.
Pricing and data location
- Basin's plan comparison lists Free (50 submissions), Starter (250), Growth (1,000), Pro (5,000) and Agency (25,000) tiers; prices are on its pricing page.
- Basin's GDPR page names DigitalOcean in Canada for hosting and the database, with US providers for file storage and transactional email.
- Formward is priced in euros: Free with 50 submissions a month, then Personal at EUR 15, Professional at EUR 30 and Business at EUR 90 a month (EUR 10, 20 and 60 billed yearly).
- Formward stores and processes submissions at Hostup AB in Sweden and sends notification email through Mailjet in the EU. The one optional non-EU sub-processor is Cloudflare Turnstile, on forms where you switch it on.
The full side-by-side, with sources, is in Formward vs Basin. Formward's prices are on the plans page, and where each part of Formward runs is on EU data residency.
Step-by-step checklist
- Create the form. In the Formward dashboard, create a form, set the notification email and copy the endpoint.
- Change the action. Replace the usebasin.com/f/ URL in the form's action with the Formward endpoint.
- Set the redirect. Add a hidden _redirect field or set the redirect on the form. The visitor's email field needs no change; it still sets Reply-To.
- Replace BasinJS. If the form uses data-basin-form, remove the script and handle the submit with fetch().
- Restrict origins. Add your site's origin, for example https://www.example.com, to the form's allowed origins. Leave the list empty if you also post from a server.
- Test before you switch. Point a copy of the form at Formward and send a few entries: a normal one, one with the honeypot filled, and one with a file if the form takes uploads. Check the dashboard and the notification email.
- Recreate integrations. Set up webhooks, Sheets sync, extra recipients and the autoresponder on the Formward form.
- Bring the history. If you want past submissions, export them as CSV from your old provider and import them under Dashboard, Import & migrate.
- Retire the Basin form. Switch the live form, watch the first real submissions arrive, then disable the form at Basin so nothing is split between two inboxes.
Sources
Last checked 7 October 2026
Move one form first
The free plan takes 50 submissions a month with no card. Point a copy of one form at Formward, send a few test entries, and switch the live form once the path works end to end.