1 · Create a lead-capture key
1
Open Connections → Lead capture
In the app, choose Connections, then Lead capture.
2
Create the key
The key is shown once. Copy it into your server’s secret store straight
away. We keep only a fingerprint, so we cannot show it again; if you lose
it, create a new one and revoke the old one.
- It carries the
leads.writescope and can call onlyleads.capture. It is refused on every other action. - It is tied to one workspace, the one you created it in. Leads always land there; you never name a workspace in the request.
- An ordinary full-access key cannot call
leads.capture. Use a lead-capture key.
2 · Connect GoHighLevel
Leads are sent on to your workspace’s GoHighLevel. Connect it under Connections (it is an OAuth sign-in) so contacts flow there. You can send leads before you connect. Each one is stored and its contact is created in Goosy Bear as normal; its CRM status readsconnection_required
and nothing is lost. When you connect GoHighLevel, those leads are pushed on
the next sweep, in the order they arrived.
3 · The request
Headers
Body fields
A top-level field we do not know is dropped, the lead is still stored, and the
response lists the field in
ignored_fields. To keep a field of your own, put
it inside fields.
The consent field
consent records what the person agreed to, as you send it. We store it
with the lead; we do not check it or assume it. If you collect consent, send it.
You are responsible for collecting consent on your form and for sending it
truthfully. Leaving
consent out does not mark anyone as consenting.
4 · The response
A stored lead answers200:
lead_idis the stored lead.contact_idis the workspace contact the submitter became. It can benullwhen the email names no one we can make a contact for.submission_idnames this submission: yourIdempotency-Keyorsubmission_id, or one we made when you sent neither. Send it on a retry so the lead is not stored twice.replayedistruewhen this was a retry of a submission already stored (see below). Nothing new was written.ignored_fieldslists every field we dropped: unknown top-level fields by name, unknown campaign keys asutm.<key>. Empty when nothing was dropped.workspaceis the slug of the workspace the lead went to.
5 · Refusals
Refusals follow the usual shape:ok: false, a
code and a message written to be read. Branch on ok, not on the status
code. A refused lead was not stored, except where noted.
Refused by the action
These answer200 with "ok": false.
Refused at the door
6 · Idempotency and retries
A network can fail after we stored the lead but before you heard back. Name each submission withIdempotency-Key (or submission_id), and send the
same value when you retry.
The key is optional. Without one, each call is stored as a new submission and
the response’s submission_id names it; send that value on any later retry.
A call whose response you never received cannot be matched that way, so if
your server retries on network errors, send your own key.
- A retry with the same key returns the first answer, with
"replayed": true, and stores nothing again. No second lead, no second automation, no second CRM push. - Choose one stable id per form submission, such as the id of the row you saved
for it, and reuse it only for retries of that same submission. A different
submission under a key you already used is refused with
idempotency_key_reused. - Keys are honoured for at least 24 hours.
- Retry on a network error, a
429(afterRetry-After) or5xx, and onleads.capture_unavailable,idempotency_in_progressandidempotency_unavailable. Do not retryinvalid_argumentsand the other errors that need a change from you.
7 · Limits
There is no CAPTCHA on this door. Because your server holds the key, spam
protection on your form (a CAPTCHA, a honeypot, your own rate limit) belongs on
your side before you call us. If a key leaks, revoke it and create another.
8 · What happens after a lead arrives
1
The lead is stored
It is saved to your workspace with the form name, fields, campaign data and
consent as you sent them.
2
The submitter becomes a contact
We find the contact with that email or create one. The response carries its
contact_id.3
Automations start
Automations whose trigger is Lead captured run for the new lead. If you
sent
form, an automation can be set to start only on that form.4
The contact goes to GoHighLevel
With GoHighLevel connected, the contact is pushed to it. With none
connected, the lead waits as
connection_required and is pushed after you
connect. A push that fails for another reason is retried.An automation may email or text the person, and the contact’s details reach
your CRM. A lead sent cannot be unsent from the API, so test with an address
you control (below).
9 · Copy-paste examples
Run these on your server. Keep the key in an environment variable.Node (fetch)
cURL
10 · Test it
1
Send one lead to an address you control
Use the cURL call above with your own email and a fresh
Idempotency-Key.
You should get "ok": true and a lead_id.2
Check it arrived
Open the contact in your workspace. If you have a Lead captured
automation, confirm it ran.
3
Send the same call again
Same
Idempotency-Key. The answer is the same ids with
"replayed": true, and no second lead appears.Troubleshooting
401 tenant_api.unauthenticated
401 tenant_api.unauthenticated
The key is missing, mistyped, revoked or expired. Check for a stray space or
newline in your secret, and look at the key under Connections.
tenant_api_key_scope_denied
tenant_api_key_scope_denied
You sent a full-access key to
leads.capture, or a lead-capture key to
another action. Use a key from Connections → Lead capture for this call.leads.capture_invalid_email
leads.capture_invalid_email
The
email is not a valid address. Check what the person typed, and that
your form sends the email field and not another one.invalid_arguments
invalid_arguments
Read the
issues list in the reply. The usual causes are a missing
email, or a value that is too long or not a string.A field I sent is in ignored_fields
A field I sent is in ignored_fields
We do not know that top-level field, so it was dropped and the lead was
stored without it. Move it inside
fields to keep it.The lead is there but not in GoHighLevel
The lead is there but not in GoHighLevel
GoHighLevel is probably not connected yet. Connect it under Connections
and the waiting leads are pushed on the next sweep.
No automation ran
No automation ran
The automation must start on Lead captured. If it is limited to one
form, the
form you send must match.429 tenant_mcp.rate_limited
429 tenant_mcp.rate_limited
You passed 60 calls a minute on this key. Wait for
Retry-After, then
retry with the same Idempotency-Key.x-request-id from the response.