API Reference

Overview

LeadFlags scores inbound leads with predefined quality criteria and returns a numeric score, quality level, risk flags, positive signals, and a recommended action.

JSON-first. All requests and responses use Content-Type: application/json. No form-encoded or XML.
Deterministic. Same input always produces the same output. No AI, no enrichment, no profiling. Rules are transparent and auditable.

Use LeadFlags before CRM ingestion to filter low-quality leads, reduce wasted calls, and focus your team on leads that convert.

Try in Postman — import the collection and run requests from Postman.

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)

HeaderDescription
Content-TypeMust be application/json
X-API-KeyYour API key (required). Get it from the dashboard.
Authorization: Bearer — Coming soon. Currently only 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 fieldAccepted keys (examples)Description
namename, fullName, first_name, last_name, contactContact name (first + last combined if sent separately)
emailemail, e-mail, mail, emailAddressEmail address
phonephone, telephone, mobile, phoneNumber, cellPhone (E.164 or national format)
messagemessage, body, notes, comment, contentMessage or notes
sourcesource, utm_source, lead_source, channelLead source (e.g. website, campaign)
countrycountry, geo, countryCode, country_isoCountry (ISO2 or name). Consistency only (e.g. phone vs country)
job_titlejob_title, title, position, roleJob title (optional). Can influence score positively for senior B2B roles.
industryindustry, sector, verticalIndustry (optional). Recognized; not used in scoring.
company_sizecompany_size, companysize, size, employeesCompany size (optional). Recognized; not used in scoring.
websitewebsite, url, homepage, domainOptional. Used for cross-field check with email.
Any other keyReturned in unrecognized_fields when present; 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 known key aliases and optional _leadflags_map 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 key matching or heuristics.

Mapping affects detection reliability, not scoring weight. If a canonical field is successfully detected—via key aliases or explicit mapping—the normalized result and score remain identical. Mapping helps ensure fields are detected correctly, but does not change how the score is calculated.

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 alias match, 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":"Alex","email":"alex@fastgrowth-labs.co","phone":"+44 7700 900123","company":"FastGrowth Labs","job_title":"Consultant","industry":"Growth Consulting","company_size":"1-10","country":"UK","source":"facebook_ads","message":"Hi, looking for more info."}'
fetch("https://leadflags.com/api/score-lead/", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": "YOUR_API_KEY"
  },
  body: JSON.stringify({
    name: "Alex",
    email: "alex@fastgrowth-labs.co",
    phone: "+44 7700 900123",
    company: "FastGrowth Labs",
    job_title: "Consultant",
    industry: "Growth Consulting",
    company_size: "1-10",
    country: "UK",
    source: "facebook_ads",
    message: "Hi, looking for more info."
  })
})
  .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' => 'Alex',
    'email' => 'alex@fastgrowth-labs.co',
    'phone' => '+44 7700 900123',
    'company' => 'FastGrowth Labs',
    'job_title' => 'Consultant',
    'industry' => 'Growth Consulting',
    'company_size' => '1-10',
    'country' => 'UK',
    'source' => 'facebook_ads',
    'message' => 'Hi, looking for more info.'
  ]),
  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": "Alex",
        "email": "alex@fastgrowth-labs.co",
        "phone": "+44 7700 900123",
        "company": "FastGrowth Labs",
        "job_title": "Consultant",
        "industry": "Growth Consulting",
        "company_size": "1-10",
        "country": "UK",
        "source": "facebook_ads",
        "message": "Hi, looking for more info."
    }
)
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: 'Alex',
  email: 'alex@fastgrowth-labs.co',
  phone: '+44 7700 900123',
  company: 'FastGrowth Labs',
  job_title: 'Consultant',
  industry: 'Growth Consulting',
  company_size: '1-10',
  country: 'UK',
  source: 'facebook_ads',
  message: 'Hi, looking for more info.'
}.to_json
res = http.request(req)
data = JSON.parse(res.body)

Response format

On success (200), the response body includes score, quality, risk flags, positive signals, and recommended action. Example:

{
  "score": 73,
  "quality": "high",
  "risk_flags": ["random_name"],
  "positive_signals": ["quality_message", "business_domain_detected"],
  "recommended_action": "standard_priority"
}
FieldTypeDescription
scorenumber0–100. Higher = better quality.
qualitystringstrong | high | medium | low
risk_flagsstring[]Signals that reduced the score (e.g. missing_phone, disposable_email, random_name). See Flags reference.
positive_signalsstring[]Trust signals (e.g. business_domain_detected, consistent_identity, quality_message).
recommended_actionstringhigh_priority | standard_priority | review | discard. CRM-agnostic; use to route or prioritize.
unrecognized_fieldsstring[] (optional)Top-level keys from the request that were not recognized. Only present when non-empty.
detection_metaobject (optional)Technical metadata about how fields were detected. Only when ?detection_meta=1 (or in /api/normalize-lead when score is included).
country and country_sent are not currently included in the response body. Geographic fields in the request are used only for consistency checks (e.g. phone vs country).

Example response with detection_meta:

{
  "score": 73,
  "quality": "high",
  "risk_flags": ["random_name"],
  "positive_signals": ["quality_message", "business_domain_detected"],
  "recommended_action": "standard_priority",
  "detection_meta": {
    "email": { "source": "contactEmail", "method": "explicit_map" },
    "phone": { "source": "tel", "method": "synonym" }
  }
}

Quality levels & recommended actions

Score is mapped to a quality level and a generic recommended action (customizable via ?mid=70&low=40 for Enterprise):

QualityRecommended action
stronghigh_priority
highstandard_priority
mediumreview
lowdiscard

Use recommended_action to route leads in your CRM or workflow. Actions are CRM-agnostic (no call-specific wording).

Flags reference

The API returns two lists: risk_flags (signals that reduce the score) and positive_signals (trust signals). Internal detection details (e.g. which key was used for a field) are not exposed in the response. Below are the semantic flags you may see; relative severity is implied by their presence, not by numeric weights.

Name

FlagDescriptionTypical causeSeverity
missing_nameName not provided or emptyField omitted or blankMedium
very_short_nameName too short (e.g. single letter)Placeholder or typoMedium
numeric_nameName is mostly numbersTest data or fakeHigh
random_nameName looks random (e.g. jumbled chars)Spam or testHigh

Email

FlagDescriptionTypical causeSeverity
missing_emailEmail not provided or emptyField omittedHigh
invalid_emailEmail format invalidTypo or invalid formatHigh
disposable_emailDisposable/temp email domainOne-time email servicesHigh
free_email_domainFree provider (Gmail, Yahoo, etc.)Personal vs businessLow
role_emailRole-based address (info@, support@)Generic mailboxMedium

Phone

FlagDescriptionTypical causeSeverity
missing_phonePhone not provided or emptyField omittedHigh
invalid_phonePhone format invalidWrong format or typoCritical
too_short_phoneToo few digitsIncomplete numberHigh
repeated_digits_phoneMostly repeated digits (e.g. 1111111)Placeholder or fakeMedium
fake_phone_patternObvious fake pattern (e.g. all same digit, long sequential)Test or placeholder numberHigh
unlikely_phone_length_for_countryDigit count doesn’t match typical length for countrye.g. ES with 7 digits, US with 9 — suspicious, not invalidLow

Message

FlagDescriptionTypical causeSeverity
missing_messageMessage not provided or emptyField omittedMedium
short_messageMessage too shortLow intent or placeholderMedium
generic_messageGeneric or template-like textCopy-paste or botMedium
spam_keywordsSpam-like wording detectedSpam or abuseHigh
suspicious_messageMessage looks like junk, spam, or very low qualityURLs, spam terms, or gibberishHigh

Source

FlagDescriptionTypical causeSeverity
missing_sourceSource not provided or emptyField omittedLow
test_sourceSource indicates test (e.g. "test", "demo")Test submissionHigh
unknown_sourceSource not in known listNew or custom sourceLow

Country

FlagDescriptionTypical causeSeverity
missing_countryCountry/geo not providedField omittedLow
invalid_countryCountry value could not be normalizedTypo, unknown code, or language code (e.g. EN is English, not a country—use GB for UK)Low
country_phone_mismatchPhone prefix does not match countryWrong country or fake phoneHigh
Language codes vs country. If you send a language code (e.g. EN, ES) instead of a country code, we flag invalid_country. We may still infer a country from the phone prefix for consistency checks (e.g. country: "EN" + phone: "+31612345678" → we use NL for phone/country checks, so we do not flag country_phone_mismatch). If there is no phone, we infer a default from the language code (e.g. EN → GB) for internal consistency only; the flag invalid_country remains.

Cross-field & consistency

These flags compare two or more fields. They reduce confidence but do not invalidate the lead by themselves.

FlagDescriptionTypical causeSeverity
name_email_mismatchName looks real but email local-part is random or initials don’t matche.g. "Juan Pérez" + asdf123@gmail.comMedium
email_domain_website_mismatchPersonal email (Gmail, etc.) but website is a company domaine.g. juan@gmail.com + empresa-industrial.com — often unserious or freelanceMedium
source_email_inconsistencyPaid source (LinkedIn, Google Ads) but disposable or role emailPaid leads usually don’t use temp/role emails — reasonable suspicionMedium

Positive signals (positive_signals)

These appear in positive_signals and indicate trust or quality signals.

SignalDescription
business_domain_detectedEmail is from a non-free, non-disposable domain (business email).
consistent_identityName, email, phone, country and message look coherent and complete.
quality_messageMessage has sufficient length and clear intent (no spam/generic copy).
commercial_intentMessage suggests buying or commercial interest.
Note. Detection method flags (e.g. which key was used for a field) are internal. Use ?detection_meta=1 or the normalize-lead endpoint if you need technical visibility into field detection.

Thresholds, rate limits & support

Thresholds

Thresholds define how score ranges map to quality and recommended action. Custom thresholds are available for Enterprise.

Custom thresholds (Enterprise)

Pass query parameters mid=X and low=X to set your own cutoffs (e.g. ?mid=70&low=40).

Optional query parameters

ParameterDescription
mid, lowScore thresholds (see above).
detection_meta=1Include detection_meta in the response (how each canonical field was detected: source path and method). Useful for integrators and QA. Example: POST /api/score-lead?detection_meta=1.

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, risk_flags, positive_signals, 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_action as a suggestion. Use high_priority, standard_priority, review, or discard to route leads in your CRM or workflow.