Outbound OS
Browse documentation

Website forms and lead webhooks

Updated 24 October 2026 · For version 0.3.0 · Edit on GitHub

A live enquiry from a website form, automation or external system enters through a lead source: a web address with its own key and field map. Outbound OS creates or merges the record, then evaluates it for the first-contact flow you enabled. Outreach depends on consent, lead state, sender readiness, templates and account limits. Historical imports are separate and do not send messages.

1. Create a source

Open Integrations → Webhooks. Use Quick add from presets for a known sender (website form, Facebook Lead Ads, marketplace, TradeIndia, JustDial, Typeform, Tally, Google Forms), or Add webhook source for anything else. You get an address and a key:

Prefer a server-side form handler or a builder’s server-side webhook to keep source keys private. A key embedded in a public HTML action is visible to every visitor and may be used to create leads and trigger eligible outreach. For direct HTML forms, use a dedicated source, review its automation rules, monitor abuse and rotate exposed keys.

Address
https://your-host/api/webhooks/inbound/<source>

The key is shown in full only once. Send it in an x-api-key header, or — for senders that cannot set headers, such as a plain HTML form or marketplace — as ?apiKey=<key> on the address.

2a. From an HTML form

contact.html
<form action="https://your-host/api/webhooks/inbound/<source>?apiKey=<key>" method="post">
  <input name="name" required>
  <input name="email" type="email">
  <input name="phone">
  <textarea name="message"></textarea>
  <label><input type="checkbox" name="email_consent" value="yes"> Email me updates</label>
  <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">
  <input type="hidden" name="_next" value="https://your-site.com/thanks">
  <button>Send</button>
</form>
  • _gotcha is a spam trap. People never see it; a submission that fills it is answered as a success and dropped.
  • _next sends the visitor to your own thank-you page. It is only followed back to the site the form was posted from. Without it, visitors see a short thank-you page.
  • A ticked email_consent records marketing consent with its time and source. An unticked box is not consent.

2b. From Zapier, Make, n8n or code

Terminal
curl -X POST https://your-host/api/webhooks/inbound/<source> \
  -H "Content-Type: application/json" \
  -H "x-api-key: <key>" \
  -d '{"name":"Jane Doe","phone":"+44 7700 900123","email":"jane@example.com","product":"500 units"}'

The response says what happened:

Response
{ "ok": true, "leadId": 1042, "created": true, "outreach": { … } }

created is false when the submission merged into an existing lead. A lead needs at least a phone number or an email; without either the response is 400.

Field names that are recognised

You rarely need a field map. These names are recognised without one (names are case-sensitive):

Lead fieldAccepted names (examples)
namename, full_name, contact_name, buyerName, sender_name
mobilemobile, phone, phone_number, whatsapp, contact_mobile
emailemail, email_address, contact_email
companycompany, company_name, org_name
productproduct, subject, enquiry, requirement, message
countrycountry
consentemail_consent, marketing_consent, newsletter, subscribe, opt_in

For anything else, edit the source’s field map. Paths can reach into nested JSON with dots — data.lead.buyerMobile or RESPONSE.SENDER_NAME.

Webflow: custom form action

  1. Create a Website Contact Form source in Outbound OS and copy its URL and key.
  2. In Webflow Designer, select the form and open its settings. Under Send to, remove Webflow and Email notifications, then add Custom action. Set the URL to the webhook URL with ?apiKey=<key>, choose POST, and save.
  3. Set the input field names to name, email, phone and message. The field name, not its visible label, is what the server receives. Require an email or phone number.
  4. Publish the site and submit from the published page. Custom actions bypass Webflow’s built-in submission storage and notification flow; Outbound OS returns its thank-you page.
  5. Check the new lead’s contact details and source in Outbound OS. An identical second submission should update the same lead.

For your own thank-you page, add a hidden _next input inside the form through an HTML Embed. Its URL must belong to the same host as the submitted page. A plain form cannot set an x-api-key header; use the query parameter here.

WordPress: Contact Form 7 and CF7 to Webhook

This route uses Contact Form 7 and CF7 to Webhook. The add-on sends JSON from WordPress to your public HTTPS webhook endpoint.

  1. Create a Website Contact Form source in Outbound OS and copy its URL and key.
  2. In WordPress, open Plugins → Add Plugin, then install and activate Contact Form 7 and CF7 to Webhook.
  3. Open Contact → Contact Forms and edit or create a form. In its Form tab, use the tags below. The webhook: option maps your CF7 field names to the names Outbound OS expects.
  4. Open the form’s Webhook tab. Check Send to Webhook and paste the source URL into Webhook URL. Under Method, keep POST.
  5. Expand Headers and enter x-api-key: <your-source-key>, with a colon separating the name and value. Leave the custom Body empty to send the mapped fields as JSON.
  6. Choose whether Send Mail should also send CF7’s normal email notification. Leave it unchecked for a webhook-only form, then save.
  7. Copy the generated shortcode into a page and publish it. Submit, check the lead’s contact details and message in Outbound OS, and submit again to confirm the same lead is updated.
Contact Form 7 → Form
<label>Name [text* your-name webhook:name]</label>
<label>Email [email* your-email webhook:email]</label>
<label>Phone [tel your-phone webhook:phone]</label>
<label>Message [textarea your-message webhook:message]</label>
[submit "Send enquiry"]

Email is required in this example; phone is optional. The add-on blocks private and loopback destinations by default, so use your public HTTPS URL. For a controlled private deployment, follow the add-on’s explicit host allowlist instructions; keep its default protection for other destinations.

WordPress alternative: WPForms webhook addon

This route needs WPForms’ Webhooks addon, with webhook access enabled for your license. Check the provider’s current plan requirements before choosing this route.

  1. Create a Website Contact Form source in Outbound OS.
  2. Activate the Webhooks addon in WPForms. Edit the form, open Settings → Webhooks and enable webhooks.
  3. Set Request URL to the copied source URL, Request Method to POST and Request Format to JSON.
  4. Add a request header named x-api-key with the copied source key.
  5. In Request Body, add keys name, email, phone and message. Map each value to its form field using WPForms’ field picker. Require email or phone.
  6. Save the form, embed it in a published page and submit. Check the lead and its source in Outbound OS, then repeat the submission to confirm deduplication.

WPForms handles its own visitor confirmation. Never put the webhook key in a visible form input.

Tally: submission webhook

  1. Create a Tally source from Outbound OS’s presets.
  2. Use question labels Name, Email, Phone and Message; Company and Country are optional. Require at least Email or Phone.
  3. Publish the form. In Tally, open Integrations → Webhooks and add the copied source URL.
  4. Add an HTTP header named x-api-key with the source key, then connect the webhook. Confirm webhook access and limits for your current Tally plan.
  5. Submit the published form. Check Tally’s webhook delivery and the new lead in Outbound OS. Replay or repeat the submission and verify that no second lead appears.

Tally sends data.fields as an array. The preset resolves labels without depending on question order. For renamed questions, map stable keys such as data.fields.question_abc.value. A checkbox’s option IDs are not consent; map email_consent only to a field that delivers an explicit yes/true value. See Tally’s webhook guide.

Sanitized Tally payload shape
{
  "eventType": "FORM_RESPONSE",
  "data": { "fields": [
    { "key": "question_name", "label": "Name", "type": "INPUT_TEXT", "value": "Test Buyer" },
    { "key": "question_email", "label": "Email", "type": "INPUT_EMAIL", "value": "buyer@example.test" },
    { "key": "question_message", "label": "Message", "type": "TEXTAREA", "value": "Need 500 units" }
  ] }
}

Typeform: response webhook

  1. Create a Typeform source from the presets.
  2. Use Name, Company and Message as question titles; use the Email and Phone Number question types for contact details. Require at least one contact field.
  3. In Typeform’s form connection settings, add a webhook with the source URL and ?apiKey=<key>. Typeform does not support a custom x-api-key delivery header.
  4. Enable the webhook and submit a published form response. Check the mapped lead in Outbound OS and repeat the delivery to confirm deduplication.

Answers are matched by stable field ref or by question title from form_response.definition.fields. Email and Phone Number can also be matched by their question type. Change the preset’s paths if your titles differ. A named ref works as form_response.answers.name.text; avoid numeric answer indexes, which change when questions are reordered. See the Typeform payload reference.

Google Forms: Apps Script submit trigger

  1. Create a Google Forms source from the presets.
  2. Create questions titled Name, Email, Phone and Message, with optional Company and Country. Require Email or Phone. For optional email marketing consent, use a Yes/No question titled Email_consent.
  3. From the form’s menu, open Script editor and paste the script below. This must be attached to the form, not its response spreadsheet.
  4. Open Project Settings → Script properties. Set OUTBOUNDOS_WEBHOOK_URL to the source URL and OUTBOUNDOS_API_KEY to its key.
  5. In Triggers, add a trigger for onFormSubmit, event source From form, event type On form submit. Authorize the script when Google prompts.
  6. Submit through the published form. Check Apps Script’s Executions for failures and Outbound OS for the lead. Do not press Run in the editor: it has no form-submit event.
Code.gs
function onFormSubmit(e) {
  if (!e || !e.response) {
    throw new Error('Use an installable trigger: From form → On form submit.');
  }
  const settings = PropertiesService.getScriptProperties();
  const url = settings.getProperty('OUTBOUNDOS_WEBHOOK_URL');
  const key = settings.getProperty('OUTBOUNDOS_API_KEY');
  if (!url || !key) throw new Error('Set the webhook URL and API key in Script properties.');

  const answers = {};
  e.response.getItemResponses().forEach(function (item) {
    const title = item.getItem().getTitle().trim().toLowerCase();
    const value = item.getResponse();
    answers[title] = Array.isArray(value) ? value.join(', ') : value;
  });
  const payload = {
    name: answers.name || '',
    email: answers.email || e.response.getRespondentEmail() || '',
    phone: answers.phone || '',
    company: answers.company || '',
    message: answers.message || '',
    country: answers.country || '',
    email_consent: answers.email_consent === 'Yes',
    externalId: e.response.getId(),
    submittedAt: e.response.getTimestamp().toISOString()
  };
  const response = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: { 'x-api-key': key },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });
  if (response.getResponseCode() >= 300) {
    throw new Error('Outbound OS rejected the submission: HTTP ' + response.getResponseCode());
  }
}

Google Forms does not have a native direct-webhook switch. Keep the key in Script properties, not a form question. The script reports non-success HTTP responses; check trigger failure notifications because it does not implement automatic retries.

Verify your form connection

Provider payload mapping, source authentication and duplicate delivery are covered by Outbound OS’s automated tests. Contact Form 7 was also exercised through a published form in an isolated WordPress installation. Webflow, WPForms, Tally, Typeform and Google Forms still need a submission from your published form: check the provider’s delivery log, the lead’s saved fields and a repeated delivery before turning on outreach. Builder screens and plans can change.

Sanitized verification records and the remaining provider checks are tracked in the repository’s validation notes.

Test without sending anything

Add "test": true to a JSON body. The response shows the lead that would be created, with every mapped field, and nothing is stored or sent.

Terminal
curl -X POST https://your-host/api/webhooks/inbound/<source> \
  -H "Content-Type: application/json" -H "x-api-key: <key>" \
  -d '{"test": true, "name": "Test Lead", "email": "test@example.com"}'

Duplicates

A submission with a phone number or email that already belongs to a lead updates that lead instead of creating another, filling in fields that were empty. It is not contacted a second time.

Source-specific guides