HTML forms
Use an access key to post any custom form to Pathline. You keep full control of the HTML and CSS on your site.
Setup
-
Create an access key
Sign in to Pathline and open Forms → Access keys. Click the + next to the tab to create a key.
Give the key a name and enter the domain where the form will live, such as
example.com. You can add more domains later. -
Copy your key
Copy the access key UUID, or use Copy instructions for AI on the Access keys page.
-
Add the form to your site
Point the form at Pathline’s submit endpoint and include your access key. Field names become labels in Messages (
first_nameshows as “First Name”). -
Test a submission
Load the page on an allowed domain and submit. The entry should appear in Messages. If you get a 403, add the page hostname to your access key’s allowed domains (e.g.
example.com).
Code examples
Build and post from HTML, JavaScript, Python, or Go. Replace YOUR-ACCESS-KEY with your access key UUID.
<style>
.contact-form {
display: grid;
gap: 12px;
padding: 24px;
border-radius: 12px;
}
</style>
<form class="contact-form"
action="https://api.pathline.io/submit"
method="POST">
<input type="hidden" name="access_key"
value="YOUR-ACCESS-KEY">
<input type="text" name="name" required>
<input type="email" name="email" required>
<textarea name="message"></textarea>
<button type="submit">Send</button>
</form>
const form = document.querySelector('#contact-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const res = await fetch('https://api.pathline.io/submit', {
method: 'POST',
body: new FormData(form),
});
const json = await res.json();
if (json.success) {
window.location.href = '/thank-you';
}
});
import requests
payload = {
"access_key": "YOUR-ACCESS-KEY",
"name": "Jane Doe",
"email": "jane@company.com",
"message": "Hello from Python!",
}
response = requests.post(
"https://api.pathline.io/submit",
data=payload,
timeout=10,
)
response.raise_for_status()
package main
import (
"net/http"
"net/url"
)
func submitForm() error {
data := url.Values{}
data.Set("access_key", "YOUR-ACCESS-KEY")
data.Set("name", "Jane Doe")
data.Set("email", "jane@company.com")
data.Set("message", "Hello from Go!")
_, err := http.PostForm(
"https://api.pathline.io/submit",
data,
)
return err
}
https://api.pathline.io/submit.
HTML and JavaScript run in the browser on an allowed domain. Python and Go samples show the same payload shape. Server-side posts without a browser Origin are rejected for access-key forms.
Optional fields
| Field | Purpose |
|---|---|
subject |
Custom email subject line for notification emails. |
redirect |
URL to send the visitor to after a successful submit. |
replyto |
Reply-to address for notification emails. |
Advanced forms
Pathline accepts any standard HTML form field. The name on each input becomes the label in Messages. Style the form however you want. Pathline only receives the field names and values.
Field types that work
| HTML | Example name |
In Messages |
|---|---|---|
<input type="text"> |
name |
Name |
<input type="email"> / tel |
email, phone |
Email, Phone |
<textarea> |
message |
Message |
<input type="radio"> |
project_type |
Project Type |
<select> |
service |
Service |
<input type="checkbox"> |
newsletter |
Newsletter |
Radio buttons
Use the same name on each radio in a group. Only the selected value is submitted.
<fieldset> <legend>What kind of project?</legend> <label><input type="radio" name="project_type" value="Construction" required> Construction</label> <label><input type="radio" name="project_type" value="Real estate"> Real Estate</label> <label><input type="radio" name="project_type" value="Brand / Corporate"> Brand / Corporate</label> </fieldset> <fieldset> <legend>Timeline</legend> <label><input type="radio" name="timeline" value="Within 30 days"> Within 30 days</label> <label><input type="radio" name="timeline" value="1-3 months"> 1–3 months</label> </fieldset>
Dropdowns
A <select> works the same way: one name, one submitted value.
<label for="service">Service needed</label> <select id="service" name="service" required> <option value="" disabled selected>Choose one…</option> <option value="Video production">Video production</option> <option value="Photography">Photography</option> <option value="Both">Both</option> </select>
Follow-up text field
Pair a radio group with an optional text input when “Other” or a custom answer is needed. Use a different name for the text field.
<label><input type="radio" name="budget" value="Under $2,500"> Under $2,500</label> <label><input type="radio" name="budget" value="$5K-$10K"> $5K–$10K</label> <label for="budget_custom">Or type your budget:</label> <input type="text" id="budget_custom" name="budget_custom" placeholder="e.g. $3,500">
Full quote-request example
A multi-step quote form with radio groups, tracking fields, contact inputs, Pathline hidden fields, and JavaScript submit.
<form id="quote-form" novalidate>
<input type="hidden" name="access_key" value="YOUR-ACCESS-KEY-HERE">
<input type="hidden" name="subject" value="New quote request">
<input type="hidden" name="redirect" value="https://example.com/thank-you">
<input type="checkbox" name="botcheck" style="display:none">
<!-- Optional: UTM / ad tracking -->
<input type="hidden" name="utm_source" id="utm_source">
<input type="hidden" name="utm_medium" id="utm_medium">
<input type="hidden" name="gclid" id="gclid">
<!-- Client-side honeypot (hide with CSS) -->
<div class="hp-field" aria-hidden="true">
<input type="text" name="website_url" tabindex="-1" autocomplete="off">
</div>
<input type="hidden" name="form_load_time" id="form_load_time">
<!-- Questions -->
<p>What kind of project?</p>
<label><input type="radio" name="project_type" value="Construction" required> Construction</label>
<label><input type="radio" name="project_type" value="Real estate"> Real estate</label>
<p>Timeline</p>
<label><input type="radio" name="timeline" value="Within 30 days"> Within 30 days</label>
<label><input type="radio" name="timeline" value="1-3 months"> 1–3 months</label>
<label for="service">Service</label>
<select id="service" name="service">
<option value="Video">Video</option>
<option value="Photo">Photo</option>
</select>
<!-- Contact -->
<input type="text" name="name" placeholder="Full name" required>
<input type="tel" name="phone" placeholder="Phone">
<input type="email" name="email" placeholder="Email" required>
<textarea name="message" placeholder="Project details" rows="4"></textarea>
<button type="submit">Send</button>
</form>
<script>
// Capture UTM params on load
const params = new URLSearchParams(window.location.search);
['utm_source', 'utm_medium', 'gclid'].forEach(function (key) {
const el = document.getElementById(key);
if (el) el.value = params.get(key) || '';
});
document.getElementById('form_load_time').value = String(Date.now());
document.getElementById('quote-form').addEventListener('submit', async function (e) {
e.preventDefault();
const form = e.target;
// Client honeypot
if (form.website_url && form.website_url.value) return;
// Reject instant bot submits (< 3s)
const loadTime = parseInt(form.form_load_time.value, 10);
if (Date.now() - loadTime < 3000) return;
const res = await fetch('https://api.pathline.io/submit', {
method: 'POST',
body: new FormData(form),
});
const json = await res.json();
if (json.success) {
window.location.href = form.redirect.value || 'https://example.com/thank-you';
} else {
alert(json.body?.message || 'Something went wrong');
}
});
</script>
Tips for complex forms
- One question per
name: radio groups share a name; text fields get their own. - Use snake_case names:
project_typereads cleaner in Messages thanprojectType. - Empty fields are skipped: optional inputs left blank won’t clutter the submission.
- Always include
botcheck: add client-side honeypots and time checks on top if you want. - Style freely: grid layouts, custom radio labels, and multi-page wizards all work as long as fields stay inside the
<form>(or you buildFormDatamanually beforefetch).
Spam protection
Built-in honeypot (botcheck)
Include a hidden checkbox named botcheck in every form. Real visitors never see it. Bots often fill every field.
If botcheck is filled in, Pathline quietly accepts the request but does not create a Message or send email.
<input type="checkbox" name="botcheck" style="display:none">
Use CSS to hide it, not type="hidden".
Extra client-side checks
You can add your own JavaScript checks before calling fetch (for example, block submits within 2 seconds of page load). Only botcheck is enforced on the server.
Allowed domains
HTML access-key forms only accept submissions from domains on your allow list. If the page hostname is not allowed, Pathline returns a 403 and the submission is rejected. Pathline checks the browser’s Origin or Referer header, not the value in your form HTML.
What to enter
- Enter a hostname, such as
example.com, not a full URL path. - If you paste a URL like
https://www.example.com/contact, Pathline stores onlywww.example.com. - Add every site where the form is embedded. You can attach multiple domains to one access key.
How Pathline matches domains
| You add | Also allowed | Notes |
|---|---|---|
example.com |
www.example.com |
Pathline adds the www variant automatically for normal production domains. |
www.example.com |
example.com |
Works both ways. You only need to add one. |
staging.example.com |
None | Subdomains are separate. Add each subdomain explicitly. |
localhost:3000 |
None | For local dev, include the port if your browser sends it in Origin. |
Browser-only submissions
- Submissions must come from a real browser on an allowed hostname.
curl, server-side scripts, and backend proxies are rejected because they do not send a matching browserOrigin.- JavaScript
fetchfrom an allowed page sends a CORS preflight; the hostname must still be on your access key.
Managing domains
Add or remove domains anytime on Forms → Access keys. In the dashboard, example.com and www.example.com are shown as a single entry.
Deliverability
Pathline filters noisy and abusive submissions so your Messages inbox and notification emails stay usable. These checks apply to HTML access-key forms and Pathline-hosted forms.
Access-key forms are also restricted by your allowed domain list. Only browsers on those hostnames can post, even if someone copies your form HTML elsewhere.
Pathline Spam Agent
When the Pathline Spam Agent is enabled, submissions it flags as spam are marked as spam and appear in your Spam inbox in Messages, not in the main inbox. The visitor still receives a success response.
Non-Latin message filtering
Most form spam Pathline sees uses non-Latin scripts (Cyrillic, Greek, Arabic, CJK, Hebrew, Armenian, and similar). If any answer field contains those characters, Pathline:
- Returns a normal success response to the visitor (same as a real submission).
- Does not create a Message in your inbox.
- Does not send a notification email.
Legitimate messages written in Latin characters (English, Spanish, French, German, etc.) are unaffected. If you expect real inquiries in non-Latin scripts, contact Pathline support. This filter is designed for typical English-language business sites.
Honeypot spam (botcheck)
When the hidden botcheck field is filled, Pathline also accepts the request quietly without creating a Message. See Spam protection for setup.