Connecting a Website Form

Create a form endpoint, post your own website form to it with a few lines of script, map the fields to contact fields and go live — with allowed origins, rate limits and reCAPTCHA covered.

Connecting a Website Form

A form endpoint turns a form you already have on your website into a lead source. You create the endpoint, add a short script to your page that posts the form's fields to it, send one test, map the fields, and submissions start creating and updating contacts.

What you'll need

Step 1 — Create the endpoint

  1. Go to CRM → Setup → Forms. The page is titled Form Endpoints.
  2. Click + Create Form Endpoint.
  3. Fill in Form Endpoint Details:
    • Form Name — how it appears in lists and on the contact's timeline ("Contact Us", "Pilot Request").
    • Email Recipients — comma-separated addresses that get an email for every live submission. Leave blank for none.
    • Email Subject — the subject of that notification.
    • Allowed Origins (CORS) — the sites allowed to post to this endpoint, as full origins (https://www.example.com), comma-separated. Leave * while you test; narrow it before you go live.
  4. Click Create Form Endpoint.
  5. The Form Endpoint Created! dialog shows your API key and Endpoint URL. Copy the key now — it is shown once. If you lose it, Regenerate API Key on the edit page issues a new one and retires the old.
  6. Click Configure Form to open the endpoint's edit page.

Step 2 — Post your form to it

On the edit page, the Integration Guide card has three tabs — JavaScript, HTML Form and cURL — with your endpoint URL already filled in. The HTML Form tab is the usual starting point:

<form id="contactForm">
    <input type="email" name="email" required>
    <input type="text" name="name">
    <textarea name="message"></textarea>
    <button type="submit">Submit</button>
</form>

<script>
document.getElementById('contactForm')
  .addEventListener('submit', async (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const data = Object.fromEntries(formData);
    const response = await fetch('https://omni.karmaflow.ai/api/forms/<your-workspace>/<form>', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-API-Key': 'YOUR_API_KEY'
        },
        body: JSON.stringify(data)
    });
    const result = await response.json();
    if (result.success) alert('Submitted!');
});
</script>

Keep your own fields and styling; what matters is that the page sends the fields as JSON (or URL-encoded) to the endpoint URL with the X-API-Key header. Field names are yours to choose — you map them in the next step.

Three things to know before you paste:

Step 3 — Send a test

Submit your form once from the page (or run the cURL tab's command). Until fields are mapped the endpoint treats every submission as a test: it records the field names and values and creates no contact. The response says so:

{ "success": true, "isTest": true,
  "message": "Test submission received. Please configure field mappings in the admin panel." }

Back on the edit page, Detected Fields switches from Awaiting First Submission to N fields detected and lists each field with the value you sent. Wrong field set? Clear & Re-detect and submit again. On the Forms list the endpoint's status moves from Awaiting Test to Needs Mapping.

Step 4 — Map the fields

In Field Mapping, choose a destination for each detected field:

Choose To
-- Skip this field -- Keep the value in the submission record but write it nowhere on the contact
Full Name → First Name + Last Name Split one name field into the two contact fields
A standard field — First Name, Last Name, Email, Phone, Company, LinkedIn ID, Years Experience Write it to that contact field (phone numbers are normalised with your workspace's default country)
Notes (adds as a note) Add the value as a note on the contact, prefixed with the form's name
A custom field Write it to one of your contact custom fields
+ Create New Custom Field Make a new custom field for it, right here

One field must map to Email — Save Field Mappings refuses otherwise, because email is how a submission finds its contact.

Click Save Field Mappings. The endpoint is now Active, and the next submission is live. Remap clears the map if you need to start over.

Step 5 — Lock it down

Back in Form Details on the edit page:

  1. Replace * in Allowed Origins (CORS) with your site's origin(s). A post from any other origin is refused with 403 Origin not allowed.
  2. Set Rate Limit (per minute) — the default is 60 submissions a minute per endpoint; above it the endpoint answers 429 with a Retry-After header.
  3. Click Save Changes.

For bot protection, open the reCAPTCHA v3 card, tick Enable reCAPTCHA v3, paste your reCAPTCHA secret key, set the Score Threshold (Google suggests 0.5; higher is stricter) and Save reCAPTCHA Settings. Your page must then include the token Google issues as a captchaToken field (and the action name as captchaAction); a missing token is refused with 400, a failing score with 403.

AI spam scoring is on for every endpoint by default: each live submission is scored against the last ten, and anything over the threshold is stored as Spam and creates no contact — while the response still reads as a success, so a bot cannot tell.

What a live submission does

  1. Matches the contact by email. An existing contact is updated with the mapped fields; a new email becomes a contact in the lead lifecycle stage. A submitter whose address is on one of your own domains (Internal Email Domains) is stored with the outcome skipped — internal domain and no contact is created.
  2. Writes Submitted web form "Form Name" (contact created) on the contact's Activity Timeline, with a View submission link.
  3. Fires the Form Endpoint Submission workflow trigger for this endpoint — build an orchestration on it and pick the form in the trigger's Form Endpoint select. Only that form's submissions start it.
  4. Emails your Email Recipients a table of the submitted values.
  5. If the Karmaflow.ai tracker is on the page and your form posts to the endpoint URL (or carries data-kf-attribution), the visitor's click id, campaign tags and GA4 session are lifted off the submission onto the contact, and a generate_lead event is sent when the GA4 integration is on.

Reading submissions

CRM → Setup → Forms → the eye icon (or View Submissions) opens Form Submissions for the endpoint. Filter by status — All · Test · Received · Processed · Failed · Spam — and click View on a row for Submission Details: when it arrived, its status and CRM Outcome (created contact, updated contact, skipped — internal domain), the IP, user agent and referring page, the spam score, the Submitted Data as sent, the Mapped Contact Data as written, and an Open link to the contact.

The same submission is reachable from the contact's timeline row.

Email Intake (optional)

Open the Email Intake card and click Enable Email Intake. The endpoint gets its own address, form-<name>.<id>@sys.karmaflow.ai. Mail sent or forwarded there is read by AI, the fields are extracted according to your field map and the result is processed as a normal submission. AI Processing Instructions lets you give hints ("the subject line holds the person's name"). Use it when a partner or an older system emails you leads instead of posting them.

Troubleshooting

401 API key required or Invalid API key. The header is missing or the key is wrong. Check the header is literally X-API-Key, and that the key is the one shown at creation (or after the last Regenerate API Key). A native form post without script always lands here.

403 Origin not allowed. The page's origin is not in Allowed Origins (CORS). Add the exact origin including scheme and any www..

429 Rate limit exceeded. More than Rate Limit (per minute) submissions in a minute. Raise it on the edit page, or wait for Retry-After.

The status stays "Awaiting Test". No submission has reached the endpoint yet. Run the cURL tab's command to prove the endpoint works, then look at your page's network tab.

Submissions arrive but no contact is created. Open the submission: a Test status means the fields are not mapped yet (Step 4); Spam means the AI score crossed the threshold; skipped — internal domain means the submitter's domain is one of yours.

My workflow runs for other forms. Open the orchestration and check the trigger's Form Endpoint select names this form. A trigger with no form selected runs for every endpoint.

Related