Skip to content
ContactForms

[ For AI agents and builders ]

Your agent can set up a working form in one prompt

No signup step for the agent. It gets a key, wires the form, sends one test lead to your own inbox and hands you a claim link. Anything that emails a customer, changes a rule or deletes data waits for your click.

Read llms.txt

The prompt says <your form key>; a key looks like f_8Kq2xLm9. An agent with only your email address can make its own: the key starts weak and waits for you to claim it (how).

› claude  “add a contact form that emails me”
● contactforms.create_form(email: “mani@northwind.studio”)
  ✓ form f_8Kq2xLm9 created · key returned once · unclaimed · bounded real intake, held until claimed
● contactforms.get_snippet(framework: “nextjs”)
  ✓ wrote app/contact/page.tsx
● contactforms.send_test(form: “f_8Kq2xLm9”)  · to your inbox only
  ✓ test lead stored · delivery pending (email sending is not live yet)
  → owner: open the claim link while signed in with that address

Illustrative example, not a live session.

[ 01 · Three ways in ]

Pick what your agent speaks

MCP server

Tools: create_form, get_snippet, send_test, list_submissions, get_submission. Setup for Claude Code, Cursor, Codex and VS Code is below.

llms.txt

One plain-text page with every endpoint, tool and error code, generated from the code and written for models.

REST API

POST /api/forms with an email returns a working key and a claim link. No account needed. Then read forms and submissions with the key.

[ 02 · Ready prompts ]

Ready prompts for the builders people use

Each prompt tells the builder to show success only when the answer says success: true and to show the error and its fix otherwise. A prompt is a draft until it has been run in its own tool; the label above each one says whether that has happened.

Prompt for Lovable

Not yet run in Lovable. This prompt is a draft until it has been run there.

Connect the contact form on this site to ContactForms, a form backend.

Form address: https://contactforms.dev/f/<your form key>
Replace <your form key> with the form key you were given. It looks like f_8Kq2xLm9. If you were given none, stop and ask me for it; do not invent one.

Do this:
1. Send the form with fetch: POST to that address with the headers "Content-Type: application/json" and "Accept: application/json", and a JSON body with the fields name, email, phone and message (these exact names).
2. Read the JSON answer. Show the success message ONLY when the answer has "success": true. Never show success because the request merely finished.
3. When "success" is false, do not show success. Show the answer's "message" to the visitor, and log its "fix" text so the developer can repair it.
4. The form key in the address is public and is meant to be in the page. Do not put any API key (cf_live_ or cf_agent_) in the page, a file or a commit.
5. After building it, send one real test submission from the page and tell me exactly what the answer said.

[ 03 · Connect over MCP ]

Setup for your coding tool

The formats follow each tool's documentation (checked 20 Oct 2026). Use an agent key with the scope you want it to have; see the MCP docs for the tools and limits.

claude mcp add --transport http contactforms https://contactforms.dev/api/mcp \
  --header "Authorization: Bearer ${CF_AGENT_KEY}"

[ 04 · Errors agents can fix ]

Every error says how to fix it

So the agent repairs its own code instead of guessing — and the form your customer sees is the one that works.

{
  "success": false,
  "error": "origin_not_allowed",
  "fix": "Add brightsmile.in to allowed domains, or call from that site.",
  "docs_url": "https://contactforms.dev/docs/domains"
}

[ 05 · Safe by default ]

Unclaimed forms are capped

Bounded real intake, held and never delivered. After 72 hours the key sleeps: leads are still stored but held until you claim it, for up to 90 days.

Agents cannot send for you

Any send, change or delete needs your click in the dashboard.

Submitted text is data

Form content can never give instructions to an AI step.