• Help · Your site

Forms on your website

A form on your site lets visitors sign up, ask a question or book. Each one that is sent lands in your people list. A working form needs four things, all checked when your AI puts the site online: the forms address, co1-app.json, the bot check and a thank-you page.

The easy way: build the page from page.json with the build-a-landing-page skill. It does all four for you. The rest of this page is for forms written by hand.

When your AI puts the site online, a hand-written form is refused if:

  • co1-app.json doesn't ask for forms.submit,
  • co1-app.json declares no form with that name,
  • an input in the form isn't one of the form's fields,
  • the person check is missing.

Each refusal says exactly what to fix, so the site never goes online with a form that can't work.

The forms address

A form posts to the site's own forms address, as a path on the site, never a full web address:

<form method="post" action="/api/v1/forms/join">

Here join is the form's name. Every form that lands in your people list posts to /api/v1/forms/<form>. On public pages, the form's code can sit in the page or in a .js file, and a form from another service (a newsletter tool, say) may post to that service. On members pages, code goes in .js files, and a form may post to another site only if that site is listed in co1-app.json under allowedHosts.form (see "Your website").

The co1-app.json file

Each site with a form has a file called co1-app.json at its root. It says what the site may do, and which forms and fields it has. Without it, every visitor's form is refused, so the site is not put online until it's there.

{
  "v": 1,
  "id": "home",
  "kind": "static",
  "permissions": ["forms.submit"],
  "forms": {
    "join": {
      "thanks": "/thanks/",
      "fields": {
        "name": { "type": "text" },
        "email": { "type": "email", "required": true }
      }
    }
  }
}
  • id is the site's name, the same as deploy_site's site.
  • permissions must include forms.submit for any form. Selling adds checkout.create.
  • How many forms a site can have, and how many fields a form can have, comes with your plan. If a site goes over, the answer says your plan's number. Need more? Email hello@companyofone.eu and we'll lift it.
  • A form's name uses lowercase letters, digits and -.
  • Each form has at least 1 field.

Fields

  • Every input in the form must be a field declared in co1-app.json, with the same name. Field names use lowercase letters, digits and _.
  • Each form needs one email-type field, so the person can be answered.
  • Field types: text, long_text, email, phone, number, choice (with its options), consent and date.
  • A field can be required, and can set its max length (up to 5,000). Without one, text holds up to 300 characters and long_text up to 2,000.
  • You may also send utm_source, utm_medium, utm_campaign, utm_content and source without declaring them, to see where people came from.

The bot check

Forms, sign-ups and bookings check that a real person sent them: that is the person check. A form without the check is refused when the site goes online. Put the widget inside the form, with your site's real key in data-sitekey, and load the person check's script once on the page. deploy_site with read: true gives the key as turnstile_site_key, and a refusal for a missing check gives it too:

<form method="post" action="/api/v1/forms/join">
  <input name="name">
  <input name="email" type="email" required>
  <div class="cf-turnstile" data-sitekey="your site's real key"></div>
  <button type="submit">Join</button>
</form>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

Only if you really don't want the check, set "turnstile": false in co1-app.json.

The thank-you page

Give every form a "thanks" page in co1-app.json, like /thanks/, and make that page. After a visitor sends the form, they land there. If something goes wrong, they see a plain page that says what happened, with a link back. Never raw code.

Fields a site never asks for

  • No password, card, bank or one-time-code fields. Members sign in with an emailed code, and payments go through the checkout.
  • A newsletter field is a visible checkbox, unticked, that the visitor ticks themselves. Code never ticks it. A yes then gets an email to confirm it, and only after that click are they on the list.

Testing a form, and what you see

  • On a preview, nothing a visitor sends is kept. The page says "Preview · nothing you send here is saved". Test the form once the site is live.
  • Each sent form lands in your people list. Its answers, like a date, are a note on the person's card. You can't sort people by them yet.
  • Everyone in your people list needs an email address. A phone number is optional.
  • People, Needs you shows who still waits for your answer.