Skip to main content
Every submission lands here, whether it comes from the capture script embedded on your site or from a request you build yourself, and the token in the URL is the only thing that authenticates it.
Where
Web FormsInstall
Your role needs
Read access to Web form capture endpoint read-web-forms. Admin, Member and Viewer have it by default.
To create
create-web-forms on top of the permission above.

Endpoint

They live at https://api.pipelime.ai. {token} is the one credential they check. There’s no API key and no Authorization header, so treat the URL itself as the secret. The capture script uses both paths. A direct integration (your own code, a Zapier zap, a platform’s native webhook) only ever needs the submissions route. See Install methods: JavaScript snippet or direct endpoint for the script tag itself and the platform-specific guides.

Request body

A checkbox group or a multi-select has to arrive as one value, not an array: join the selected options into a single string before you send them. A fields value that’s an array or an object instead of a scalar, a field name that’s too long, or a value that’s too long all fail validation the same way; see Responses. A form_key is matched on the key alone. Nothing else about the definition is looked at. Send one that doesn’t match anything, or leave it out entirely, and the submission is queued exactly the same way; what decides whether it becomes a lead automatically, or waits in the product for you to map it, is on Field mapping and the submissions inbox. Every context key is an optional string, and only the keys below are read. Anything else you put in context is accepted and then ignored:

The domain check

A request that carries an Origin or Referer header is checked against the form’s configured domain: Origin when both are there. A request with neither is treated as server-to-server and let through on the strength of the token alone. Only hostnames are compared. The host is read out of the header value (so the scheme and any port are dropped), lowercased, and a leading www. removed. The form’s configured domain is normalised the same way: scheme, leading www. and any trailing slash come off first. A form saved as https://www.acme.test/ therefore matches a page served from https://acme.test/contact.
The OPTIONS preflight always answers 204, even for a token that doesn’t resolve to a form. So an unknown token doesn’t get blocked at this stage; the actual POST still goes through and comes back with the real 404 your JavaScript can read. A domain mismatch is different: the preflight for a token it does recognize carries that form’s own domain in its CORS header, not the calling page’s, so the browser refuses to send the POST at all. You’ll see a CORS failure in the console, never a 403: only a server-to-server call, which skips preflight entirely, gets that body directly.
An unknown token and an inactive form’s token fail identically, with the same 404. There’s no way to tell “wrong token” from “this form is turned off” from the response alone.

Responses

Every response from the submissions route: The checks run in that order: token, then domain, then body size, then validation. The first one that fails is the response you get.
Two different problems both come back as {"error": "Invalid payload"}. One is Laravel’s own validator failing. That one also carries an errors object naming the field. The other is a fields value that isn’t a plain scalar, or a name or value that’s too long, checked separately after the validator passes. It never gets an errors object. Check whether errors is present before assuming which one happened.

Honeypot and deduplication

Two things stop a submission before it is ever queued for processing, and they stop it in different ways:
  • _hp is a honeypot: real visitors leave it empty. Any value in it that isn’t blank marks the submission spam. The submission is still recorded, with that status, but nothing is queued.
  • The exact same fields sent again from the same IP address, to the same form, shortly after the first attempt doesn’t create a second submission. Nothing new is stored at all. Only fields is compared, so a different context on the retry (a different page, different UTM tags) doesn’t change that.
Both cases return the same 200 {"status": "accepted"} as a genuine success, so neither a spam script nor a retried request can tell it was caught.
Because a caught duplicate creates nothing, retrying a request you’re not sure landed is safe, but only for a little while after the original attempt. Wait too long before retrying and the second call creates a real, separate submission instead of being caught.

Form discovery

The capture script also reports the forms it finds on a page to /api/public/web-forms/{token}/forms: their fields, labels and how each one submits. We use that to build the list you map fields against in the product. A successful report answers 200 with {"status": "ok"}. A direct integration doesn’t need to call this route at all, unless it’s replicating what the script does. The token check, the domain check and the size limit are the same as on the submissions route. Posting a form’s key again refreshes what’s known about its fields, but never touches whether you’re tracking it or how its fields are mapped. Rediscovery can’t downgrade a form you’ve already set up.

Rate limit

Requests to this endpoint are throttled on their own, keyed by IP address and form token together: a different address posting to the same form, or the same address posting to a different form, each has its own budget. The submissions route and the discovery route share that budget: discovery calls from one address eat into what’s left for submissions from that same address. Go over it and further calls return 429 until the window clears.

Install methods: JavaScript snippet or direct endpoint

The exact script tag, the platform-specific guides, and the context fields each install method accepts.

Connect the forms on your website

Scan your site, map the fields, and turn on the form this token belongs to.

API overview

Where this endpoint sits next to the versioned lead-intake API.