Skip to content

Embeddable signup form widget (drop-in script tag) #75

Description

@JuansesDev

Problem

Today there is no built-in way for a SendDock user to capture subscribers from their own site. They have to either:

  • Build a custom HTML form and wire it to POST /api/v1/projects/{id}/subscribers (requires backend or proxying API keys, plus their own anti-bot, plus confirmation flow)
  • Use a third-party form tool (Typeform, Tally, Google Forms) and Zapier — adds cost and brittleness

EmailOctopus, Mailchimp and Beehiiv all ship an embeddable JS widget specifically because the form is the funnel mouth. Without it, the path from "potential subscriber on a marketing site" to "row in the database" is harder than it should be.

Proposal

A drop-in embeddable signup form distributed as a single script tag.

Embed

<script src="https://app.senddock.dev/widget.js"
        data-project="proj_abc123"
        data-form="default"
        async></script>
<div id="senddock-form"></div>

The script bootstraps a styled form inline at the target element. Fields are configurable per form via a builder in the SendDock UI (more on that below).

Form builder UI

New section Project → Forms with CRUD:

  • Fields: email is mandatory; optional inclusion of name and any project custom field (from Custom fields for subscribers (typed attributes beyond email/name) #72). Per-field: label, placeholder, required toggle, helper text.
  • Style: pick a theme (light / dark / system) and accent color. Optionally provide custom CSS via a textarea (escape-hatch for full control).
  • Behavior:
    • Double opt-in toggle (sends confirmation email — uses Templates for the confirmation copy)
    • Success: inline message OR redirect to URL
    • Add subscriber to tags (multi-select, from Subscriber tags and segmentation #40)
    • Set custom field defaults (e.g. signup_source: "homepage")
  • Anti-abuse: honeypot field + Cloudflare Turnstile / hCaptcha toggle (configurable site key per project)

API additions

  • POST /api/v1/projects/{id}/forms create form definition
  • GET /api/v1/projects/{id}/forms list
  • GET /widget.js?project=...&form=... returns the rendered widget JS (cached at the edge)
  • POST /api/v1/projects/{id}/forms/{formId}/submit public endpoint the widget hits with CORS open to the form's allowed origins (configurable per form, default *)
  • Rate-limited per IP to prevent abuse

Confirmation flow (double opt-in)

When double opt-in is enabled, submission creates the subscriber with status = pending and sends the configured confirmation template. The confirmation link hits GET /api/v1/projects/{id}/confirm/{token} which flips status to active. The existing unsubscribe page pattern is reused for the confirmation success page.

Acceptance criteria

  • Form CRUD UI in project Settings
  • Configurable fields (email + name + any custom field from Custom fields for subscribers (typed attributes beyond email/name) #72)
  • Theme presets + custom CSS override
  • Embed via single <script> tag with data-project and data-form attributes
  • Double opt-in flow with confirmation email rendered from a template
  • Honeypot + optional Turnstile / hCaptcha
  • Tags + custom field defaults on signup
  • Per-form allowed-origins for CORS
  • Per-form submission analytics (views, submissions, conversion rate) on the form's detail page

Tier

Core — table stakes for any subscription tool. Without this, the funnel from prospect to subscriber is broken.

Blocked by

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions