API Reference
Overview
LeadFlags scores inbound leads with predefined quality criteria and returns a numeric score, quality level, flags, and a recommended action.
Content-Type: application/json. No form-encoded or XML.
Use LeadFlags before CRM ingestion to filter low-quality leads, reduce wasted calls, and focus your team on leads that convert.
Base URL
All API requests use this base:
https://leadflags.com
Example: POST https://leadflags.com/api/score-lead/
Endpoint
POST /api/score-lead
Accepts a JSON body with lead data. No required fields; send only what you have.
Headers (required)
| Header | Description |
|---|---|
Content-Type | Must be application/json |
X-API-Key | Your API key (required). Get it from the dashboard. |
X-API-Key is supported.
Body
Arbitrary JSON object—no fixed schema. We detect scoring fields by key aliases (case-insensitive, - and _ treated the same). Unknown keys are stored as metadata and not used for scoring.
| Canonical field | Accepted keys (examples) | Description |
|---|---|---|
| name | name, fullName, first_name, last_name, contact | Contact name (first + last combined if sent separately) |
email, e-mail, mail, emailAddress | Email address | |
| phone | phone, telephone, mobile, phoneNumber, cell | Phone (E.164 or national format) |
| message | message, body, notes, comment, content | Message or notes |
| source | source, utm_source, lead_source, channel | Lead source (e.g. website, campaign) |
| country | country, geo, countryCode, country_iso | Country (ISO2 or name). Consistency only (e.g. phone vs country) |
| — | Any other key | Stored as metadata; not used in scoring |
Schema-flexible input, deterministic output
LeadFlags accepts any JSON structure—nested objects, arrays, mixed keys. There is no fixed schema. We detect canonical fields (name, email, phone, message, source, country, etc.) via a versioned synonym dictionary and optional overrides. Same input with the same detector_version always yields the same normalized result.
Optional mapping override: If your payload uses non-standard keys, you can tell us explicitly with a reserved object _leadflags_map. Map a JSON path or key to a canonical field:
{
"contactEmail": "user@example.com",
"tel": "+34987654321",
"_leadflags_map": {
"contactEmail": "email",
"tel": "phone"
}
}
Only known canonical field names are allowed in the map. If a path is missing, a warning is added and detection continues with synonyms/heuristics.
Normalize-only endpoint: POST https://leadflags.comapi/normalize-lead returns the full normalization result (detector_version, canonical_lead, detected_fields, missing_fields, conflicts, warnings) and optionally score/quality/flags. Use it to inspect how we interpreted your payload without changing scoring logic. Query ?debug_mode=1 for full conflict and source_path details.
detected_fields indicates which canonical field was filled, from which path, and by which method (override, key synonym, or heuristic). It does not expose how the score is computed.
Request examples
All examples send the same JSON body and require X-API-Key. Replace YOUR_API_KEY with your key.
curl -X POST "https://leadflags.com/api/score-lead/" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"name":"Pieter van der Berg","email":"pieter@bedrijf.nl","phone":"+31612345678","message":"Interested in enterprise plan","source":"website","country":"NL"}'
fetch("https://leadflags.com/api/score-lead/", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "YOUR_API_KEY"
},
body: JSON.stringify({
name: "Jane Doe",
email: "jane@company.com",
phone: "+34912345678",
message: "Interested in enterprise plan",
source: "website",
country: "ES"
})
})
.then(res => res.json())
.then(data => console.log(data));
$ch = curl_init("https://leadflags.com/api/score-lead/");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Pieter van der Berg',
'email' => 'pieter@bedrijf.nl',
'phone' => '+31612345678',
'message' => 'Interested in enterprise plan',
'source' => 'website',
'country' => 'NL'
]),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: YOUR_API_KEY'
],
CURLOPT_RETURNTRANSFER => true
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
import requests
response = requests.post(
"https://leadflags.com/api/score-lead/",
headers={
"Content-Type": "application/json",
"X-API-Key": "YOUR_API_KEY"
},
json={
"name": "Jane Doe",
"email": "jane@company.com",
"phone": "+34912345678",
"message": "Interested in enterprise plan",
"source": "website",
"country": "ES"
}
)
data = response.json()
require 'net/http'
require 'json'
uri = URI("https://leadflags.com/api/score-lead/")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == 'https')
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-API-Key'] = 'YOUR_API_KEY'
req.body = {
name: 'Pieter van der Berg',
email: 'pieter@bedrijf.nl',
phone: '+31612345678',
message: 'Interested in enterprise plan',
source: 'website',
country: 'NL'
}.to_json
res = http.request(req)
data = JSON.parse(res.body)
Response format
On success (200), the response body is (example for the request above — complete lead, no flags):
{
"score": 100,
"quality": "high",
"flags": [],
"recommended_action": "call_immediately"
}
| Field | Type | Description |
|---|---|---|
score | number | 0–100. Higher = better quality. |
quality | string | high | medium | low |
flags | string[] | List of flag codes explaining deductions (see Flags reference). |
recommended_action | string | call_immediately | review_before_call | do_not_call |
Quality levels & recommended actions
Default thresholds (customizable via query params ?mid=80&low=50 for Enterprise):
| Score | Quality | Recommended action |
|---|---|---|
| ≥ 80 | high | call_immediately |
| 50–79 | medium | review_before_call |
| < 50 | low | do_not_call |
Flags reference
Flags explain why the score was reduced. Each flag has a severity (Low, Medium, High, Critical) that reflects its relative importance. Exact score weights are internal and may change over time.
Name
| Flag | Description | Typical cause | Severity |
|---|---|---|---|
missing_name | Name not provided or empty | Field omitted or blank | Medium |
very_short_name | Name too short (e.g. single letter) | Placeholder or typo | Medium |
numeric_name | Name is mostly numbers | Test data or fake | High |
random_name | Name looks random (e.g. jumbled chars) | Spam or test | High |
| Flag | Description | Typical cause | Severity |
|---|---|---|---|
missing_email | Email not provided or empty | Field omitted | High |
invalid_email | Email format invalid | Typo or invalid format | High |
disposable_email | Disposable/temp email domain | One-time email services | High |
free_email_domain | Free provider (Gmail, Yahoo, etc.) | Personal vs business | Low |
role_email | Role-based address (info@, support@) | Generic mailbox | Medium |
Phone
| Flag | Description | Typical cause | Severity |
|---|---|---|---|
missing_phone | Phone not provided or empty | Field omitted | High |
invalid_phone | Phone format invalid | Wrong format or typo | Critical |
too_short_phone | Too few digits | Incomplete number | High |
repeated_digits_phone | Mostly repeated digits (e.g. 1111111) | Placeholder or fake | Medium |
Message
| Flag | Description | Typical cause | Severity |
|---|---|---|---|
missing_message | Message not provided or empty | Field omitted | Medium |
short_message | Message too short | Low intent or placeholder | Medium |
generic_message | Generic or template-like text | Copy-paste or bot | Medium |
spam_keywords | Spam-like wording detected | Spam or abuse | High |
Source
| Flag | Description | Typical cause | Severity |
|---|---|---|---|
missing_source | Source not provided or empty | Field omitted | Low |
test_source | Source indicates test (e.g. "test", "demo") | Test submission | High |
unknown_source | Source not in known list | New or custom source | Low |
Country
| Flag | Description | Typical cause | Severity |
|---|---|---|---|
missing_country | Country/geo not provided | Field omitted | Low |
invalid_country | Country value could not be normalized | Typo or unknown code | Low |
country_phone_mismatch | Phone prefix does not match country | Wrong country or fake phone | High |
Thresholds, rate limits & support
Thresholds
A threshold is the limit at which something changes. In our case: score threshold and action threshold. Example: score ≥ 80 → call immediately; score ≥ 50 → review; score < 50 → do not call.
Custom thresholds (Enterprise)
Pass query parameters mid=X and low=X to set your own cutoffs.
Example: POST /api/score-lead?mid=70&low=40 — then score ≥ 70 is high, ≥ 40 is medium, < 40 is low. Defaults: mid=80, low=50.
Rate limit
How many requests you can make per minute. Examples: Starter 60 req/min, Growth 300 req/min, Scale 1,000 req/min. This protects the server, prevents abuse, and prioritizes premium clients.
Priority processing
When there's load: Starter uses the normal queue; Growth and Scale use the fast queue. At first this can be symbolic, but it sends a clear value message.
Support
Email support: We reply when we can. No guaranteed response times.
Fast / priority support: Response in < 24h. Priority over Starter. You don't need a full support team for this.
IP allowlist (Enterprise)
Only traffic from authorised IPs is accepted. Clients ask for it for security and compliance.
Early access to new features
Growth (and similar) see new features first, help validate them, and feel premium.
Errors
Error responses use standard HTTP status codes and a JSON body with error and optional message.
400 Bad Request — invalid_json
Request body is not valid JSON.
{
"error": "invalid_json"
}
401 Unauthorized
Missing or invalid X-API-Key header.
{
"error": "Unauthorized",
"message": "Valid X-API-Key header required"
}
405 Method Not Allowed
Endpoint only accepts POST.
{
"error": "Method Not Allowed"
}
Response includes Allow: POST header.
200 OK
Success. Body contains score, quality, flags, recommended_action.
Best practices
- Send only what you have. Don’t invent or guess data. Empty or omitted fields are scored accordingly.
- Use LeadFlags before CRM ingestion. Score at the gate so you don’t store or process low-quality leads.
- Log score and flags in your system. Keep a record for routing, analytics, and auditing.
- Treat
recommended_actionas a suggestion. You decide how to route (call immediately, queue for review, or do not call).