ZyanDocs

Post leads from your own form

The public capture endpoint — send a lead to Zyan from a form you already built, with a capture token you generate and rotate yourself.

If you already have a contact form you like, you do not have to replace it. The capture API takes a JSON POST and creates a lead in exactly the same place a Zyan form would.

Before you start

Generating a capture token is owner-gated. You will find it under LeadsLead ManagerLead settingsIntegration. A member with leads edit and settings edit can read that panel's status, but only an owner can mint, rotate or disable the token.

Two credentials, not one

This is the part that catches people, so it is worth settling before the snippet. Every call carries both of these:

CredentialWhere it goesWhat it is
Workspace publishable keyThe apikey and Authorization: Bearer headersYour workspace's public API key. Safe to put in a page a visitor can read
Capture tokenThe capture_token field in the JSON bodyThe secret that decides which workspace the lead lands in. Never ship this to a browser

Miss the headers and the request is refused at the gateway before the token is read at all — which looks exactly like a bad token and is not one. Both values are already filled in on the snippet the Integration panel gives you.

Get a capture token

Open the Integration panel

LeadsLead ManagerLead settingsIntegration. The status pill reads Configured, Not configured, Checking… or Status unavailable.

Generate the token

It looks like <workspace id>.<secret>.

Copy it immediately

The secret half is shown exactly once. If you lose it, rotate to get a new one — you cannot read the old one back.

A capture token is a credential

Anyone holding it can create leads in your workspace. Keep it server-side. If your form posts from the browser, put a small endpoint of your own in front so the token never ships to a visitor.

Send a lead

curl -X POST 'https://<your functions host>/functions/v1/public-lead-capture' \
  -H 'Content-Type: application/json' \
  -H 'apikey: <workspace publishable key>' \
  -H 'Authorization: Bearer <workspace publishable key>' \
  -d '{
    "capture_token": "<workspace id>.<secret>",
    "name": "Dana Reyes",
    "email": "dana@example.com",
    "subject": "Website redesign",
    "message": "We are replacing a 2019 Squarespace site. Budget is flexible."
  }'

The exact endpoint URL, your publishable key and a ready-made snippet are all on the Integration panel — copy them from there rather than assembling the host by hand.

Request

Four content fields are required, plus the token. The rest are optional and worth sending — a detail you leave out here is a detail somebody has to ring up and ask for.

FieldRequiredNotes
capture_tokenYesThe full <workspace id>.<secret> string
nameYes
emailYesLower-cased on the way in, so casing never splits a match
subjectYes
messageYes
phoneNo
companyNo
websiteNo
form_sourceNoYour own label for the page that captured it. Defaults to website_contact
attributionNoAn object for click ids and campaign details. A campaign is honoured only when it belongs to your workspace

Response

{ "success": true, "lead_id": "…", "message": "…" }
StatusMeaning
200The lead was created
400A required field is missing, or the message tripped the spam filter
404Anything token-shaped went wrong
413The body is over 32 KB
429The workspace is over 100 captured leads in the last hour

A 404 never tells you which problem it is

No token minted, a wrong token, a rotated token, a disabled endpoint, an inactive workspace — every one of them returns the same 404 with the same body. That is deliberate: nobody probing the endpoint should be able to learn whether a workspace exists. Diagnose it from the Integration panel, not from the response.

Caps

LimitValue
Captured leads100 per hour, per workspace
Request body32 KB

Testing from one machine will not trip the hourly cap quickly, but a broken loop on your own site will. The cap is counted per workspace, not per form.

Rotating and disabling

Both live on the same Integration panel.

  • Rotate issues a new token and kills the old one instantly — update every caller first, or you will drop leads between the two steps.
  • Disable turns the endpoint off entirely. Existing leads are untouched.

What these leads look like afterwards

Leads from this endpoint are labelled Custom integration in the operator notification, and their Attribution tab carries a source but no Form — the endpoint has no way to know which of your pages the visitor was on.

Form-scoped automations will never match them

Only hosted, embed and provider submissions carry a form id. If you scope an automation trigger to a specific form, capture-API leads slide straight past it. Use an unscoped lead created trigger for this route.

Troubleshooting

SymptomCauseFix
400 on every requestA required field is missingname, email, subject and message are all required — subject is the one people forget
Requests stopped working suddenlyThe token was rotatedCopy the new token from Integration
A steady 404 and the panel reads Not configuredNo token has been minted, or the endpoint was disabledGenerate a token, or re-enable it on the Integration panel
The token is not visibleIt is only shown once, at generationRotate to mint a fresh one
Requests fail before the token is even checkedThe publishable key headers are missingSend it as both apikey and Authorization: Bearer
429 Too many requestsOver 100 captured leads in the hourWait for the hour to roll over, and check nothing is looping
413 on a large requestThe body is over 32 KBTrim the payload
A 400 on a request that looks completeThe message tripped the spam keyword filterReword it, or capture through a hosted form
A form-scoped automation never firesThese leads carry no form idUse an unscoped lead created trigger