Inbound Webhooks
Accept form submissions from any external source â custom HTML forms on your website, Google Ads lead forms, Zapier, Make, or any system that can send an HTTP POST. No embed script needed. Just point your form's action URL at your webhook endpoint.
How It Works
- Create a webhook in your site's Integrations tab
- Copy the webhook URL and secret key
- Point your external form or integration at that URL
- ProForms receives the data, auto-creates a form (if needed), and stores the submission
- Email notifications, lead qualification, and outbound webhooks all fire as normal
Endpoint
POST https://proforms.io/api/v1/incoming/{webhookId}?key={secretKey}You can also pass the key as a header instead of a query parameter:
X-Webhook-Key: your_secret_keyCustom HTML Forms
The most common use case. Drop a plain HTML form on any website and have submissions flow into ProForms â with email notifications, spam protection, and lead management.
Basic Example
<form action="https://proforms.io/api/v1/incoming/YOUR_WEBHOOK_ID?key=YOUR_SECRET_KEY" method="POST">
<input type="text" name="name" placeholder="Your name" required />
<input type="email" name="email" placeholder="Email address" required />
<input type="tel" name="phone" placeholder="Phone number" />
<textarea name="message" placeholder="How can we help?"></textarea>
<!-- Honeypot spam protection (hidden from real users) -->
<input type="text" name="_hp" style="display:none" tabindex="-1" autocomplete="off" />
<button type="submit">Send</button>
</form>That's it. ProForms will:
- Auto-detect field types (email, phone, text) from the data
- Create a form in your site with matching fields
- Store the submission and send email notifications
- Score for spam (honeypot, timing, rate limiting, bot detection)
With Redirect
Add a _redirect hidden field to send users to a thank-you page after submission:
<form action="https://proforms.io/api/v1/incoming/YOUR_WEBHOOK_ID?key=YOUR_SECRET_KEY" method="POST">
<input type="text" name="name" placeholder="Full name" required />
<input type="email" name="email" placeholder="Email" required />
<input type="text" name="company" placeholder="Company" />
<select name="service">
<option value="">Select a service</option>
<option value="SEO">SEO</option>
<option value="Web Design">Web Design</option>
<option value="Google Ads">Google Ads</option>
</select>
<!-- Redirect after submission -->
<input type="hidden" name="_redirect" value="https://yoursite.com/thank-you" />
<!-- Name the auto-created form -->
<input type="hidden" name="_form" value="Service Inquiry" />
<!-- Honeypot -->
<input type="text" name="_hp" style="display:none" tabindex="-1" autocomplete="off" />
<button type="submit">Request Quote</button>
</form>Special Fields
Fields prefixed with _are control fields â they're not stored in the submission data.
| Field | Type | Description |
|---|---|---|
| _redirect | URL | Redirect the user to this URL after successful submission (303 redirect). A ?ref= param is appended with the reference ID. |
| _form | string | Name for the auto-created form. If omitted, uses the webhook name. |
| _hp | hidden | Honeypot field. Must be empty â if filled (by bots), submission is silently marked as spam. |
Google Ads Lead Form Extensions
Connect Google Ads lead form extensions directly to ProForms. When someone fills out a lead form on your ad, the data flows straight into ProForms for tracking and notifications.
Setup Steps
- Create a webhookin your site's Integrations tab. Set the source to
Google Adsif available, or use the default. - In Google Ads, go to your campaign â Assets â Lead Form Extension â Edit.
- Under "Lead delivery", select
Webhook. - Webhook URL:URL
https://proforms.io/api/v1/incoming/YOUR_WEBHOOK_ID - Key:Paste your webhook secret key into the Google Ads "Key" field. Google sends this as
google_keyin the payload body. - Send test leadâ use Google's "Send test data" button to verify the connection.
Google Ads Payload Format
Google Ads sends lead data in a specific format. ProForms auto-detects and parses it:
{
"lead_id": "TeSter-9999999999-0000000000-0",
"campaign_id": 12345678901,
"form_id": 12345678901,
"gcl_id": "CjwKCAjw...",
"google_key": "YOUR_SECRET_KEY",
"user_column_data": [
{ "column_id": "FULL_NAME", "string_value": "Jane Smith" },
{ "column_id": "EMAIL", "string_value": "jane@example.com" },
{ "column_id": "PHONE_NUMBER", "string_value": "+14155551234" },
{ "column_id": "COMPANY_NAME", "string_value": "Smith Co" },
{ "column_id": "POSTAL_CODE", "string_value": "84401" }
],
"api_version": "1.0",
"is_test": true
}Supported Google Ads Fields
| Column ID | Maps To |
|---|---|
| FULL_NAME | Name field |
| FIRST_NAME | Name / Text field |
| LAST_NAME | Name / Text field |
| Email field | |
| PHONE_NUMBER | Phone field |
| COMPANY_NAME | Company / Text field |
| STREET_ADDRESS | Address / Text field |
| CITY | Text field |
| STATE | Text field |
| POSTAL_CODE | Text field |
| COUNTRY | Text field |
lead_id, gcl_id, campaign_id, and form_id are stored in submission metadata â not as form fields. They appear in the submission detail view and API responses.JSON API (Zapier, Make, Custom Code)
Send a JSON POST from any system â Zapier, Make (Integromat), n8n, or your own backend.
curl -X POST "https://proforms.io/api/v1/incoming/YOUR_WEBHOOK_ID?key=YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"phone": "801-555-1234",
"service": "SEO",
"message": "Interested in monthly SEO services"
}'Or pass the key as a header:
curl -X POST "https://proforms.io/api/v1/incoming/YOUR_WEBHOOK_ID" \
-H "Content-Type: application/json" \
-H "X-Webhook-Key: YOUR_SECRET_KEY" \
-d '{ "name": "Jane Smith", "email": "jane@example.com" }'Nested Data
If your payload has a data object, ProForms will use it as the submission fields:
{
"data": {
"name": "John Doe",
"email": "john@example.com",
"phone": "801-555-0000"
}
}Response
Success (JSON)
{
"success": true,
"submissionId": "sub_abc123...",
"referenceId": "PF-42",
"fieldsMapped": ["name", "email", "phone", "message"]
}Success (HTML Form with _redirect)
When a _redirect field is present, returns a 303 See Other redirect to the specified URL with ?ref=PF-42 appended.
Errors
| Status | Meaning |
|---|---|
| 400 | Invalid body or no data fields found |
| 401 | Missing or invalid secret key |
| 403 | Webhook disabled or site inactive |
| 404 | Webhook not found |
Spam Protection
Inbound webhooks have built-in spam scoring. Submissions are scored 0â100 and anything above 70 is marked as spam.
| Signal | Score |
|---|---|
| Honeypot filled (_hp field has value) | +100 (instant spam) |
| Submission under 2 seconds | +40 |
| Missing Referer header | +30 |
| Bot user agent | +30 |
| Duplicate content (same IP, same data) | +25 |
| Rate limit exceeded (5/min or 20/hr per IP) | +20 |
| Excessive URLs in text fields | +20 |
| Spam phrases detected | +10 each (cap 30) |
Auto-Created Forms
When a webhook receives data and no form is assigned, ProForms automatically:
- Creates a form with fields inferred from the incoming data (email detection, phone detection, etc.)
- Names it using the
_formfield value, or falls back to the webhook name - Links the webhook to that form for all future submissions
- Evolves the schema â if a later submission includes new fields, they're added to the form automatically
You can also pre-assign a form to a webhook in the Integrations settings. In that case, incoming data is mapped to existing form fields by key name, label, or field type.
Verification Endpoint
Some services ping webhooks with a GET request to verify they're reachable:
GET https://proforms.io/api/v1/incoming/YOUR_WEBHOOK_ID{
"success": true,
"webhook": "Contact Form",
"status": "active"
}WordPress Example
Add this to any WordPress page or post using a Custom HTML block:
<form action="https://proforms.io/api/v1/incoming/YOUR_WEBHOOK_ID?key=YOUR_SECRET_KEY" method="POST" style="max-width: 500px;">
<p>
<label>Name</label><br/>
<input type="text" name="name" required style="width: 100%; padding: 8px;" />
</p>
<p>
<label>Email</label><br/>
<input type="email" name="email" required style="width: 100%; padding: 8px;" />
</p>
<p>
<label>Phone</label><br/>
<input type="tel" name="phone" style="width: 100%; padding: 8px;" />
</p>
<p>
<label>Message</label><br/>
<textarea name="message" rows="4" style="width: 100%; padding: 8px;"></textarea>
</p>
<!-- Spam protection -->
<input type="text" name="_hp" style="display:none" tabindex="-1" autocomplete="off" />
<!-- Redirect to thank you page -->
<input type="hidden" name="_redirect" value="https://yoursite.com/thank-you/" />
<input type="hidden" name="_form" value="Contact Form" />
<p><button type="submit" style="padding: 10px 24px; background: #10B981; color: white; border: none; border-radius: 6px; cursor: pointer;">Submit</button></p>
</form>Webhook Lifecycle
Webhooks can be archived and restored. An archived webhook is disabled and returns a 410 Gone response when it receives submissions. This lets you deactivate a webhook without losing its configuration or submission history.
- Active â Accepts submissions normally
- Disabled â Returns
403 Forbidden(webhook exists but is turned off) - Archived â Returns
410 Gone(soft-deleted, restorable) - Deleted â Returns
404 Not Found(permanently removed)
To manage webhook lifecycle (create, archive, restore, delete), see the Manage Webhooks API.
Accepted Content Types
| Content-Type | Use Case |
|---|---|
| application/json | API calls, Zapier, Make, custom code |
| application/x-www-form-urlencoded | HTML <form> submissions (default browser behavior) |
| multipart/form-data | HTML forms with file fields (files are not stored â text fields only) |