← Blog

How AI coding agents can add signup forms in seconds

Teach your AI coding agent to add a signup form in seconds — public pub_ id, HTML or fetch, domain allow-list, no API keys in front-end.

Coding agents are great at scaffolding UI. They are also great at pasting a pretty <form> whose action points nowhere useful — a relative PHP path, a same-origin API you never built, or a placeholder comment. You still need an HTTPS endpoint that stores the email, rejects junk, and stays out of the browser's secret stash. That is a thin intake problem, not a reason to invent a backend on every project.

The fastest safe way for AI coding agents to add signup forms is to give them a real public endpoint, a public campaign id, and explicit instructions never to put secrets in browser code.

Why agents leave dead forms

Most training data still assumes a server next to the HTML: PHP mail(), a framework route, or "wire this later." Static and lightly dynamic sites do not have that server. An agent that only "makes the form look right" will ship markup that submits into a 404 — unless you give it a real public endpoint and hard rules about what must never land in front-end code.

What the agent actually needs

  • Subscribe URLPOST /api/subscribe on the Simple Signups origin (or path-style POST /c/:publicCampaignId).
  • Public campaign idpub_… only. Safe in HTML and client JS.
  • Domain allow-list — every hostname that will embed the form, added in the dashboard before you trust a browser test.
  • Honeypot — field hp, hidden, empty for real humans.
  • Hard ban on secrets — never ss_live_… API keys in the browser, shipped env, or agent chat you will commit.

Field reference stays in the docs quickstart; this post is the agent-shaped workflow.

Prompt pattern (copy this)

Paste the block into Cursor, Copilot Chat, Claude Code, or any agent that edits your repo. Pick your public pub_… id in the prompt example below — that choice is baked into the copy-ready prompt and stays locked to the outcome snippets on this page. Keep the guardrails — agents follow explicit constraints more reliably than vague "add a newsletter form" asks.

Prompt for your coding agent
Add a newsletter signup form that POSTs to Simple Signups.
Requirements (do not skip):
- Endpoint: https://simple-signups.com/api/subscribe
(or path-style POST https://simple-signups.com/c/pub_your_campaign_id)
- Public campaign id only: pub_your_campaign_id (replace with my real pub_… id; never invent one).
- NEVER put ss_live_ API keys, secrets, or dashboard tokens in front-end code,
HTML, env exposed to the browser, or commit messages.
- Include email (required) and honeypot field name="hp" (hidden, empty for humans).
- Optional tags: JSON array of short slugs, or HTML hidden name="tags" (comma-separated).
- Do NOT disable, remove, or bypass honeypot, Turnstile, rate limits, or domain checks.
- After wiring: remind me to allow-list every browser hostname that embeds the form
(exact match; apex ≠ www; no wildcards).
Prefer plain HTML form first. Optional: small fetch() JSON submit with the same fields.
Leave success/error UX minimal unless I ask.

Snippets the agent should produce

Happy path is plain HTML: no SDK, no same-origin API route. Public campaign id in a hidden field; honeypot present and empty. These examples stay free of another picker — they reuse the campaign id chosen above.

HTML form (agent-ready)
<form action="https://simple-signups.com/api/subscribe" method="POST">
<input type="hidden" name="campaignId" value="pub_your_campaign_id" />
<input type="email" name="email" required placeholder="you@example.com" />
<input type="text" name="hp" tabindex="-1" autocomplete="off"
style="position:absolute;left:-9999px" aria-hidden="true" />
<button type="submit">Subscribe</button>
</form>

If the page already uses client JS, the same fields work as JSON. Still no API key header on browser submits — domain allow-list + public id are the gate.

fetch (JSON)
await fetch('https://simple-signups.com/api/subscribe', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
campaignId: 'pub_your_campaign_id',
email: 'jane@example.com',
tags: ['newsletter'], // optional category slugs
// hp must be empty or omitted — filled honeypots are dropped
}),
});

Verify before you trust it

  1. Allow-list the embed host. Matching is exact after normalization — www.example.com does not cover example.com; there is no *.pages.dev wildcard. Add local preview hosts only if you test from them.
  2. Submit once from the real origin (or a listed preview). Confirm the row in the dashboard subscriber list for that campaign.
  3. Negative check: from an unlisted origin you should see DOMAIN_NOT_ALLOWED (403). That means the gate works — fix the allow-list, do not ask the agent to "bypass domain checks."

Optional Turnstile stays server-enforced when you enable it. Do not prompt the agent to strip bot checks to "make local dev easier." Details: anti-abuse & domains.

Guardrails worth repeating to the agent

  • Browser forms and fetch use public pub_… ids only.
  • ss_live_… keys are for server-side or trusted automation — never HTML, SPA bundles, or client env.
  • Keep honeypot hp; do not remove rate limits or Turnstile "for DX."
  • Simple Signups is intake (store, gate, export) — not a CRM, ESP, or general app backend.

Related