Leads
Receive contact form submissions from your sites via the API. Leads appear in the CMS dashboard and trigger notifications via Discord, email, and webhooks.
How Leads Work
When a visitor submits a form on your site, you send the data to the leads API. The CMS creates a lead record and triggers notifications to workspace owners and admins.
At least one of email or phone is required. You can also include a source to identify which form or page the lead came from, and pass any custom data in the data field.
Submitting Leads
Security: Always proxy leads through your own API route. Never expose your CMS API key in client-side code.
// app/api/contact/route.ts
export async function POST(request: Request) {
const body = await request.json();
const res = await fetch(
`${process.env.CMS_URL}/api/public/leads`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.CMS_API_KEY!,
},
body: JSON.stringify({
email: body.email,
firstName: body.firstName,
lastName: body.lastName,
source: "contact-form",
message: body.message,
}),
}
);
if (!res.ok) {
return Response.json({ error: "Failed to submit" }, { status: 400 });
}
return Response.json({ success: true });
}// app/api/contact/route.ts
export async function POST(request: Request) {
const body = await request.json();
const res = await fetch(
`${process.env.CMS_URL}/api/public/leads`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.CMS_API_KEY!,
},
body: JSON.stringify({
email: body.email,
firstName: body.firstName,
lastName: body.lastName,
source: "contact-form",
message: body.message,
}),
}
);
if (!res.ok) {
return Response.json({ error: "Failed to submit" }, { status: 400 });
}
return Response.json({ success: true });
}async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const form = new FormData(e.currentTarget);
const res = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: form.get("email"),
firstName: form.get("firstName"),
message: form.get("message"),
}),
});
if (res.ok) {
// Show success message
}
}async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const form = new FormData(e.currentTarget);
const res = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: form.get("email"),
firstName: form.get("firstName"),
message: form.get("message"),
}),
});
if (res.ok) {
// Show success message
}
}Anti-Spam
The leads API supports a honeypot field. Add a hidden input to your form — if a bot fills it in, the request succeeds silently but no lead is created:
<form onSubmit={handleSubmit}>
{/* Visible fields */}
<input name="email" type="email" required />
<textarea name="message" required />
{/* Hidden honeypot - bots will fill this in */}
<input
name="honeypot"
type="text"
style={{ display: "none" }}
tabIndex={-1}
autoComplete="off"
/>
<button type="submit">Send</button>
</form><form onSubmit={handleSubmit}>
{/* Visible fields */}
<input name="email" type="email" required />
<textarea name="message" required />
{/* Hidden honeypot - bots will fill this in */}
<input
name="honeypot"
type="text"
style={{ display: "none" }}
tabIndex={-1}
autoComplete="off"
/>
<button type="submit">Send</button>
</form>Notifications
When a lead is created, the CMS automatically sends notifications:
- Discord — notification to your workspace's Discord webhook
- Email — to workspace owners and admins
- Webhook — POST to your configured webhook URL