Lead capture from your own website
Put a form on the website you already have, and have it post straight into your CRESUPER instance. There is nothing to install and no code of ours running on your site - your form talks to your instance's public lead API, and the lead lands in your pipeline.
1. The three endpoints
Replace <your-slug> with your instance's slug.
POST https://<your-slug>.cresuper88.com/api/public/leads/buy POST https://<your-slug>.cresuper88.com/api/public/leads/sell POST https://<your-slug>.cresuper88.com/api/public/contact
Use /api/public/leads/buy for buyer and tenant inquiries,
/api/public/leads/sell for owners who want to sell or lease out a property, and
/api/public/contact for a general "get in touch" form.
2. The fields
All three
| Field | Rule |
|---|---|
contact_name | Required, non-empty, 200 characters or fewer. |
company_website | The honeypot. See section 4 - leave it in. |
/api/public/leads/buy
| Field | Rule |
|---|---|
contact_phone | Required. Normalized to exactly 10 US digits: punctuation is stripped and a leading country-code 1 is dropped, so (505) 555-0134 and +1 505 555 0134 are both fine. Anything that does not reduce to 10 digits is rejected. |
contact_email | Optional, but shape-checked when present. |
notes | Optional, 5000 characters or fewer. |
budget | Optional, 50 characters or fewer. |
timeline | Optional, 100 characters or fewer. |
property_types[], areas[] | Optional arrays, at most 20 elements each, each element 80 characters or fewer. |
/api/public/leads/sell
| Field | Rule |
|---|---|
contact_phone | Required, same 10 US digits rule as buy. |
address | Required - the property being sold or leased out. 300 characters or fewer. |
contact_email | Optional, shape-checked when present. |
notes | Optional, 5000 characters or fewer. |
size | Optional, 50 characters or fewer. |
timeline | Optional, 100 characters or fewer. |
reason | Optional, 500 characters or fewer. |
/api/public/contact
| Field | Rule |
|---|---|
contact_email | Required here, and it must be a valid address. This is the one endpoint where the email is required and the phone is not. |
message | Optional, 5000 characters or fewer. |
3. What comes back
| Case | Status | Body |
|---|---|---|
| Success | 200 | {"ok":true} - buy additionally returns a chat_url you may show the visitor. |
| Validation failure | 400 | {"error":"Please check the form and try again."} |
| Contact, bad or missing email | 400 | {"error":"Please enter a valid email."} |
Branch on the status code, and show the error string as-is: it is written for the
visitor, not for you.
4. The honeypot, and why you must not remove it
Include a hidden input named company_website in your form, positioned off-screen
or with display: none, with tabindex="-1" and
autocomplete="off" so a real visitor can never focus it.
If that field arrives with any value, the request is accepted and silently
dropped: you get 200 with {"ok":true} and no lead is written.
That is deliberate - telling a bot it failed teaches it to try again. Two consequences for your
developer: do not "fix" the field away because it looks pointless, and do not treat a 200 as a
delivery receipt. If you need certainty that a lead landed, look in the CRM.
5. Snippet A - server-side relay (recommended)
Your own backend receives the form the ordinary way and posts JSON onward. No CORS is involved, because the request comes from your server rather than from a browser. This is the only shape that works from a marketing site on a different hostname.
// your site's own handler, any framework
const r = await fetch('https://<your-slug>.cresuper88.com/api/public/leads/buy', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contact_name: form.name,
contact_phone: form.phone,
contact_email: form.email,
notes: form.message,
company_website: form.company_website, // pass the honeypot through untouched
}),
});
const out = await r.json();
// r.ok -> out.ok === true ; otherwise show out.error to the visitor
6. Snippet B - browser fetch, same origin only
If the page is served from the instance's own origin
(https://<your-slug>.cresuper88.com), the browser can post directly:
const res = await fetch('/api/public/leads/sell', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contact_name: name.value,
contact_phone: phone.value,
address: address.value,
company_website: hp.value,
}),
});
const out = await res.json();
CORS: in production your instance allows exactly one browser origin - its own
public URL. A fetch() from any other origin, including your marketing site, is
refused at the preflight and never reaches the API. That is why snippet A exists.
7. The constraint that catches everyone
The API parses JSON only. Your instance mounts a JSON body parser and no
urlencoded parser at all, so a plain <form method="post"> - which sends
application/x-www-form-urlencoded - arrives with an unparsed body and fails
validation on fields you did send. Always send a JSON body with
Content-Type: application/json, from your server (snippet A) or from a same-origin
page (snippet B).
Your instance is the authority on all of the above. If a limit here and your instance disagree, your instance is right - tell us and we will fix this page.