api reference
Screen a lead in one request.
LeadFilter analyzes an inbound message and returns a spam-likelihood score, a verdict, and a plain-English reason. Send lead text from any form, CRM webhook, automation, or internal tool.
/v1/leads/analyzeOverview
The LeadFilter API is a screening layer for inbound leads. You send the raw message text (plus any context you have), and LeadFilter responds with a score, a verdict, a short reason, and matched categories.
LeadFilter is conservative by design. It estimates how spam-like a message is — it does not decide whether a lead is valuable, and it never deletes anything. You stay in control of what happens to each result.
- One JSON endpoint, one predictable response shape.
- Every result includes a
request_idfor auditing, support, and debugging. - Works with contact forms, CRM webhooks, automations, and internal tools.
Quick start
Create an account, add your business context, generate an API key, then send your first lead. Here's a complete request and response:
// 200 OK
{
"request_id": "lfreq_...",
"score": 94,
"verdict": "likely_spam",
"confidence": 0.92,
"reason": "This message appears to be an SEO outreach pitch.",
"categories": ["seo_pitch", "link_building"],
"processed_at": "2026-05-30T20:00:00.000Z"
}Authentication
Authenticate every request with an API key in the x-api-key header. Create and manage keys from your dashboard — when you generate a key, the full secret is shown once, so copy it then.
x-api-key: lfai_live_••••••••••••••••Analyze a lead
https://api.leadfilter.zsetup.com/v1/leads/analyzeSubmit an inbound message for screening. The only required field is lead.text; everything else is optional context that can sharpen the result. A successful call returns 200 OK with the screening result.
Request fields
| Field | Type | Requirement | Description |
|---|---|---|---|
lead.text | string | Required | The raw inbound message to screen. |
lead.email | string | Optional | Sender's email address, if you have it. |
lead.company | string | Optional | Company name associated with the lead. |
lead.source | string | Optional | Where the lead came from, e.g. website_form. |
lead.country | string | Optional | Two-letter country code, e.g. US. |
external_id | string | Optional | Your own reference, such as a CRM record ID. |
metadata | object | Optional | Arbitrary key/values stored alongside the check. |
Response fields
| Field | Type | Description |
|---|---|---|
request_id | string | Unique ID for this check. Cite it for support and debugging. |
score | integer | Spam likelihood from 0–100. Higher means more spam-like. |
verdict | string | One of likely_legit, needs_review, likely_spam. |
confidence | number | Confidence in the normalized decision, from 0 to 1 (not raw provider certainty). |
reason | string | Short, human-readable explanation of the result. |
categories | string[] | Matched pattern categories, e.g. seo_pitch. |
processed_at | string | ISO-8601 timestamp of when the check ran. |
Understanding verdicts
Every result maps to one of three verdicts. They describe how spam-like a message is — not whether it's a good fit for your business.
Reads like a genuine inquiry. Safe to route straight into your pipeline.
Ambiguous or low-confidence. Flagged for a human rather than dropped — intentionally. Send these to a review queue.
Matches well-known outreach and spam patterns. Hold, tag, or archive on your terms — it's never deleted automatically.
needs_review is a feature, not a fallback. When LeadFilter isn't confident, it says so — so a real opportunity isn't thrown away by a machine.Understanding the score
The score is a spam-likelihood estimate from 0 to 100. A 100 means very likely spam, fake, irrelevant, or non-actionable; lower scores look more like genuine inquiries.
Thresholds for each verdict are configurable per account, so you decide where needs_review begins and ends. A low-confidence result can intentionally become needs_review even when the score sits mid-range.
Error responses
Errors return a non-2xx status and a consistent JSON shape. The request_id is always included so you can trace any failure. A service_unavailable response means screening is temporarily down — it never implies the submitted lead is legitimate, so hold and retry rather than letting it through. For Analyze, provider failures currently fall back to a conservative deterministic classifier and return a valid 200; provider_unavailable is reserved.
// 400 Bad Request
{
"error": {
"code": "validation_failed",
"message": "Lead text is required.",
"request_id": "lfreq_..."
}
}| Code | Status | When it happens |
|---|---|---|
validation_failed | 400 | The request body was invalid or a required field was missing. |
unauthorized | 401 | The API key is missing or invalid. |
subscription_inactive | 402 | No active subscription or trial on the account. |
api_key_revoked | 401 | This API key has been revoked from the dashboard. |
forbidden | 403 | The key is valid but not permitted to perform this action. |
not_found | 404 | The requested resource does not exist. |
conflict | 409 | The request conflicts with the current state of a resource. |
quota_exceeded | 402 | The monthly lead-check allowance has been reached. |
rate_limited | 429 | Reserved; not enforced at the app layer in MVP. |
provider_unavailable | 503 | Reserved for Analyze; provider failures currently fall back and return a valid 200 response. |
service_unavailable | 503 | Screening is temporarily unavailable. Retry shortly. |
internal_error | 500 | An unexpected error occurred on our side. |
This is the stable public error-code vocabulary. Reserved codes remain part of that vocabulary even when the Analyze endpoint currently handles the condition through a successful fallback response.
Integration guidance
Call LeadFilter wherever a lead enters your world. A common pattern is to screen on form submission or CRM webhook, then branch on the verdict.
- Route
likely_legitstraight into your pipeline. - Send
needs_reviewto a queue for a human to confirm. - Hold or tag
likely_spam— keep the audit trail either way.
Store the request_id with your lead record so any result can be looked up later in your dashboard logs.