Contact forms and lead capture, no backend needed.

coss.com is a backend for contact and lead forms. Post plain HTML to an endpoint, and it screens spam, emails your team and adds leads to your contacts.

Read the docs

The problem

A contact form is a few inputs on a page, and it still needs a server, a spam filter and somewhere for the messages to go.

Static sites have nowhere to post a form. Adding a function for it means parsing the post, validating fields, keeping bots out, emailing the right person and getting the lead into your CRM or mailing list, and every public form attracts spam the day it goes up.

coss.com does this on one key: Forms gives each form an endpoint, Risk screens every submission, email tells your team, and contacts keep the leads you can write to later.

How it works

Create a form, point your HTML at its endpoint, and accepted submissions come to you.

const { :  } = await ..({
  : "Contact us",
  : ["https://www.example.com"],
  : [
    { : "email", : "email", : true },
    { : "first_name", : "text" },
    { : "company", : "text", : 100 },
    { : "message", : "textarea", : 5000 },
  ],
  // Your team gets an email, from your domain, and replies go to the submitter.
  : { : ["sales@example.com"], : "forms@example.com" },
  // The submitter becomes a contact in your leads audience.
  : {
    : true,
    : [],
    : "first_name",
    : { : "company" },
  },
});
// form?.endpoint: https://…/f/form_01k…
<form action="https://…/f/form_01k…" method="post">
  <input type="email" name="email" required>
  <input type="text" name="first_name">
  <input type="text" name="company">
  <textarea name="message"></textarea>
  <input type="hidden" name="_next" value="https://www.example.com/thanks">
  <!-- The honeypot: people leave it empty, bots fill it in. -->
  <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">
  <button>Send</button>
</form>

That's a working form with no JavaScript. To submit without a page load and show errors per field, add attachForm, which also sends a device token so Risk recognizes the browser:

const  = .<>("#contact");
if () {
  (, {
    () {
      if (.) .("Thanks!");
      else if (.) for (const  of .) .(., .);
    },
  });
}

To send leads to your CRM, handle the webhook:

const  = await (await .(), ., );
if (. === "form.submission.received") {
  // Submitted values are untrusted input.
  await (.., ..);
}

Read more in the Forms guide, HTML forms and destinations.

What you get

Collect, screen and deliver submissions, from plain HTML, a script or a page coss.com hosts.

Any HTML form

The endpoint takes the URL-encoded and multipart posts browsers send, and JSON. Define fields to validate types and required values, or leave them out and take any field. See HTML forms.

Spam stays out

A honeypot, then a Risk assessment with your own rules and lists, a content check, and an optional Turnstile or hCaptcha. Spam is answered like a success, so bots learn nothing. See spam filtering.

False positives come back

What Risk wants reviewed waits as held. Release it or anything marked spam, and it's delivered as if it had been accepted, and Risk learns from it. If Risk can't be reached, submissions are delivered rather than lost.

Notifications your team can answer

Up to 5 recipients get an email per accepted submission, with every field and a link to it in the dashboard, and a reply goes to the submitter. They come from your verified domain, or from coss.com as "form name via coss.com".

Leads in your contacts

Submitters become contacts, with their audiences, topics and properties, ready for broadcasts. Double opt-in can make new contacts confirm first. See double opt-in.

Webhooks for everything else

form.submission.received and form.submission.flagged reach your webhooks, signed, with what was submitted. Each destination retries on its own if it fails.

Hosted pages

Turn on hosted and the endpoint also serves the form as a page rendered from its fields, to link to or embed, with no JavaScript needed. See hosted pages.

Confirmed addresses and attachments

submitter.verify holds a submission until the submitter clicks the link emailed to them. File uploads take attachments, served only as downloads. They aren't scanned for malware, so treat them as untrusted.

Specifications

The limits a submission is checked against. The docs list each one in context.

Encodings
URL-encoded, multipart, JSON
Fields per submission
100, 10,000 characters a value
Text per submission
64 KB
Notification recipients
up to 5
Allowed origins
up to 10, https
Submissions per form
10 a second, bursts of 50
Submissions per client IP
20 a minute
Files
3 by default, up to 10, 10 MB each
Accepted submissions kept
90 days
Spam and unconfirmed kept
14 days

Pricing

You pay per accepted live submission, and for the files it carries. Spam, held and unconfirmed submissions cost nothing unless you release them or the submitter confirms.

The Free plan includes 1,000 form submissions a month and 1,000 MB of form uploads a month. Notification emails sent from your own domain are billed like any email; notifications sent "via coss.com" are not. Sandbox forms are free. See pricing for the plans.

Questions

How do I add a contact form to a static site?
Create a form with coss.forms.create() or in the dashboard, then set your HTML form's action to its endpoint. coss.com stores each submission, screens it for spam and sends accepted ones to your team's inbox, your webhooks and your contacts. You write no server code.
How does coss.com keep spam out of a contact form?
A hidden honeypot field catches simple bots at once. Every other submission is a Risk assessment of the submitter's email, IP address, browser and the text they wrote, with your own rules and lists, plus a content check for spam, phishing, malware and fraud and an optional Turnstile or hCaptcha. Spam is answered exactly like a success, so bots learn nothing.
What if a real message is marked as spam?
Find it on the form's Spam or Held tab and release it, in the dashboard or with coss.forms.submissions.release(). It is delivered as if it had been accepted, and Risk records the assessment as legitimate, so it learns from the mistake.
Will I get an email for each submission?
Yes. Up to 5 recipients get an email per accepted submission with every field, and a reply goes straight to the submitter. In live environments, recipients must be members of your organization or addresses on its verified domains.
How do I send leads to my CRM?
Subscribe a webhook endpoint to form.submission.received. Each one is signed and carries the submitted fields, so your handler can create the lead. Treat the values as untrusted input.
Can submitters join my mailing list?
Yes. With contacts turned on, the submitter becomes a contact of the environment, added to the audiences and topics you choose, with fields mapped to contact properties. Double opt-in can make new contacts confirm their address first.
Do I have to write the HTML?
No. Turn on a hosted page and coss.com renders the form from its field definitions, at a link you can share or embed in an iframe. Hosted pages are on coss.com's forms domain, since custom domains for them aren't available.
Is there a drag-and-drop form builder?
No. You define a form's fields in code or write your own HTML. Drag-and-drop building and conditional or multi-page forms aren't available.

The products

One key, one SDK, one log for all of them.

Build it this afternoon.

Hand your coding agent this prompt, then pick the parts you need:

Add coss.com to this project: read https://coss.com/llms.txt, get a development key with `npx coss-cli init --agents`, and build and test with it.

Pricing