Back to Blog
๐Ÿ“
/5 min read/By SMTPfast Team

Add a signup form to your website in 5 minutes

Drop a form on your site, collect contacts straight into SMTPfast, with double opt-in and webhook events out of the box.

formstutorialcontacts
Share:๐•in

If you have a marketing site or a landing page, you probably want a way for visitors to leave their email so you can talk to them later. SMTPfast now ships embeddable signup forms that drop into any HTML page and post submissions straight into your contacts.

This post walks through the whole flow: building a form in the dashboard, embedding it on your site, customizing the look, and handling double opt-in confirmations.

What you get

  • A form builder in the dashboard at /forms: name it, pick which fields show, customize the button text and color.
  • Two embed snippets per form: a plain HTML <form> (no JavaScript required) and a styled JS widget that submits inline without a page reload.
  • Double opt-in baked in. Turn it on (recommended) and SMTPfast emails the visitor a confirmation link before adding them as a contact. Confirmation links are HMAC-signed and expire after 7 days.
  • Honeypot bot protection in both snippets, silently swallowing the obvious automated traffic.
  • A contact.subscribed webhook event that fires the moment a contact is confirmed, so your downstream automations (welcome email, CRM sync, Slack ping) can hook in.

Step 1: Create the form

In the dashboard, open Forms in the sidebar and click New form. Give it a name like "Newsletter signup", then you'll land on the editor.

The editor has three panes:

  • Settings: name, fields, button text + color, success message, redirect URL, double-opt-in toggle, active/paused.
  • Live preview: what visitors see when the snippet loads on your site.
  • Embed: two copy-paste snippets ready to drop on your site.

Hit Save changes when you're happy. Edits propagate to the live JS widget within five minutes (the embed script is cached for that long).

Step 2: Embed it

Pick the snippet that fits your site. Both submit to the same endpoint and behave identically from a data perspective. The difference is purely UX.

Option A: Plain HTML form

Drop this into any page:

<form action="https://smtpfa.st/api/forms/YOUR_FORM_ID/submit" method="POST">
  <input type="email" name="email" placeholder="[email protected]" required />
  <input type="text" name="first_name" placeholder="First name" />
  <input type="text" name="_hp" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px"/>
  <button type="submit">Subscribe</button>
</form>

When a visitor submits, the browser navigates away to a thanks page (or your redirect_url if you set one). The hidden _hp field is the honeypot. Bots tend to fill every input they see, and tripped submissions are silently dropped.

This option is bulletproof: it works without JavaScript, on any browser, with any CSP that allows form posts.

Option B: JS widget (recommended)

<div data-smtpfast-form="YOUR_FORM_ID"></div>
<script src="https://smtpfa.st/api/forms/YOUR_FORM_ID/embed.js" async></script>

The widget renders the form into the placeholder div, posts via fetch, and shows your success message inline without reloading the page. Because the script reads form config at runtime, dashboard edits show up automatically. No need to re-paste the snippet when you change the button color.

The widget has zero dependencies and weighs about 3KB.

Step 3: Decide on double opt-in

Double opt-in is on by default. Leave it on unless you have a good reason not to.

When DOI is on:

  1. Visitor submits the form.
  2. SMTPfast emails them a confirmation link.
  3. They click the link โ†’ contact appears in your account, contact.subscribed webhook fires.

When DOI is off:

  1. Visitor submits the form.
  2. Contact appears immediately, contact.subscribed webhook fires immediately.

DOI adds a tiny bit of friction up front, but it protects your sender reputation, complies with GDPR, blocks list-bombing, and ensures every contact actually owns the address they typed.

Step 4: Hook up a webhook (optional)

If you want a Slack ping or a CRM sync the moment someone subscribes, head to Webhooks in the dashboard and create one with the contact.subscribed event checked. The payload looks like:

{
  "type": "contact.subscribed",
  "data": {
    "contact_id": "clx_contact123",
    "email": "[email protected]",
    "first_name": "Steve",
    "last_name": null,
    "form_id": "clx_form123",
    "double_opt_in": true
  },
  "timestamp": "2026-05-01T12:00:00.000Z"
}

Every webhook delivery is signed with HMAC-SHA256. See the webhooks docs for verification examples in Node, Python, and Go.

What happens to bad submissions?

A few things you don't have to worry about:

  • Suppressed addresses: visitors who previously bounced or complained on your account never make it into your contacts list, even if they re-submit.
  • Already-subscribed contacts: re-submissions short-circuit; we don't email a duplicate confirmation.
  • Rate limit abuse: 10 submissions per minute per (form, IP). Anything over that gets a 429.
  • Tampered confirmation links: the HMAC signature is verified before any DB lookup, so bots probing random tokens can't even cause load on your database.

Try it now

  1. Create a form in your dashboard.
  2. Copy either embed snippet.
  3. Drop it on your site.
  4. Submit a test address from your phone or another browser to see the flow.

Full reference is at /docs/forms. If you hit anything unexpected, the public OpenAPI spec covers every form endpoint with request/response shapes.

Happy collecting!

Ready to get started?

Start sending transactional email today. Free to start, no credit card required.

Get Started for Free