LANDWORKPRO

For agencies and integrators

Send leads into an operator's pipeline

One endpoint, one key. Post a lead and it becomes a customer and a new deal in that operator's account, with the campaign that produced it attached. Everything below is what the endpoint actually does today.

The endpoint

POST https://landworkpro.com/api/public/leads
Content-Type: application/json
Authorization: Bearer YOUR_KEY

Either spelling works. /api/public/leads and the same path with a trailing slash both answer terminally, so a redirect can never be counted as a failed delivery by your workflow.

Your key

Send it either way. Both are accepted, because some workflow builders cannot set an Authorization header at all.

Authorization: Bearer YOUR_KEY
X-API-Key: YOUR_KEY

The key identifies the operator. A payload cannot name its own destination: the account a lead lands in is resolved from the key and never from the body, so a key can only ever write into the one account it belongs to. Keys are stored as a hash, never as plaintext.

The body

Send a phone or an email. Everything else is optional, and each field accepts a few spellings so you can usually map straight through from your form.

FieldAlso accepted as
namefull_name, fullName
phonephone_number, phoneNumber
emailemail_address
addressaddress1, full_address
notesmessage, comments

Attribution, which is the part worth wiring

Send the campaign data with the lead and the operator sees which campaign produced the job, not just the lead. Each of these also accepts an attribution_ prefix, so attribution_utm_source works as well as utm_source.

utm_source   utm_medium   utm_campaign   utm_content   utm_term
gclid        fbc          fbp

Mapping from GoHighLevel: these line up with its own attributionSource object. One gap worth knowing: fbclid is not accepted today. GoHighLevel carries it natively, so if you map its attribution object straight through, that one field is dropped. Send fbc where you have it.

Retries, and not creating the lead twice

Send an Idempotency-Key header, or an event_id in the body, that is stable across retries of the same lead. The header wins if you send both. This is required, and it is what makes a retry safe.

It matters more than it looks. GoHighLevel counts any non 2xx response as a failed delivery and retries up to twelve times, so a single lead can arrive thirteen times. With a stable key, those thirteen deliveries create one customer and one deal. Without one, the request is refused outright rather than risking thirteen.

When it refuses

Every refusal returns a fixed, machine readable reason beside a sentence for a human. Branch your workflow on the reason, never on the message.

{ "ok": false, "reason": "invalid_api_key", "message": "..." }
reasonStatusWhat to do
missing_api_key401No key was sent. Check the header name and that your workflow is not stripping it.
invalid_api_key401The key is wrong or has been revoked. Ask the operator for a current one.
org_not_eligible403The destination account is closed to inbound leads. This is not something you can fix from your side. Tell the operator.
missing_idempotency_key400Send an Idempotency-Key header, or an event_id in the body, that stays the same across retries of the same lead.
no_contact_method422Send a phone or an email. A lead with neither cannot be contacted, so it is refused.
malformed_payload400The body was not readable JSON, or a field was the wrong shape. Fix and resend.
rate_limited429More than 120 leads in a minute for this account. Back off and retry.
lead_unresolved503We could not record it and we are not pretending we did. Transient by construction: retry with the same idempotency key.

Nothing here answers 2xx or 3xx. A refusal is always a real error status, so your workflow's own retry logic sees it as one.

Rate limit

120 leads per account per minute, then rate_limited with a 429. It is deliberately generous because GoHighLevel bursts on retry, and a limit that trips during a legitimate retry storm turns a recoverable delivery into a lost lead.

What it can and cannot do

A complete request

curl -X POST https://landworkpro.com/api/public/leads \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ghl-contact-7f3a91" \
  -d '{
    "name": "Dale Whitfield",
    "phone": "615 555 0142",
    "email": "dale@example.com",
    "address": "1200 County Line Rd, Franklin TN",
    "notes": "12 acres, wants it cleared before spring",
    "utm_source": "google",
    "utm_campaign": "land-clearing-middle-tn",
    "gclid": "Cj0KCQjw..."
  }'

Getting a key

Keys are issued by us, one per operator account. There is no self serve screen: ask the operator to request one, or contact us directly and we will issue it and wire the first delivery with you.

Book a call and get a keycarson@landworkpro.com

That is deliberate while this is new. We would rather talk to every agency sending leads than hand out credentials and find out later how they were used.

Something here not matching what you are seeing? Tell us. This page is generated from the endpoint's own contract, so if it is wrong we want to know before you do.