ProductsDocsGuidesPricing
Sign inGet Started

Intelligence APIs for developers.

Product

  • All APIs
  • Support Triage
  • Pricing

Developers

  • Getting started
  • Guides
  • API reference
  • Errors
  • Dashboard

Company

  • About
  • Security
  • Contact

Legal

  • Privacy Policy
  • Terms of Service
© Kovca. kovca.io
GuidesOperations

Qualifying and Routing Inbound Leads in Minutes

Qualify inbound demo requests against your own criteria, route them to the right territory, and keep research and support requests out of sales queues. Code in curl, TypeScript and Python.

Lead Intelligence7 min readcURL · TypeScript · Python
hotwarmnurture

On Tuesday at 4:52 p.m., a VP at a distribution company fills in your contact form. They're replacing their tool for 120 reps, need the Salesforce integration, and want to sign before the end of next month. The form goes into a shared inbox along with a student's research question, a password reset, and a two-person bakery asking for a quote. Someone gets to it Thursday. By then, the VP has had two demos with your competitors.

Speed-to-lead is mostly a routing problem. This guide builds a qualification step that runs as soon as a form is submitted. It applies your criteria, picks the territory, and gets the purchase-ready leads to a rep while they're still at their desk. The qualification step uses the Kovca Lead Intelligence API.

Qualify on what they said, against what you sell

Lead scoring often goes wrong in one of two ways. It scores the person, using signals you shouldn't use, or it guesses at things nobody stated. This design avoids both:

  • Intent comes from the message. Is this a purchase, an evaluation, research, or not a sales inquiry at all?
  • Fit comes from your criteria. Minimum budget and served countries are checked exactly, in code. Plain-language criteria such as "needs at least 50 seats" are checked one at a time, each with its own result.
  • Nothing is inferred. Budget, authority, and company size that weren't stated aren't guessed. The API has no fields for personal or demographic traits and doesn't use them.

The pipeline

  1. 1
    Form or chat
    message, company, budget, timeline
  2. 2
    Your enrichment
    optional: CRM match, existing-customer check
  3. 3
    Kovca Lead Intelligence
    qualification, intent, fit, territory
    Support / successexisting customer or not a sales inquiry
  4. 4
    CRM
    owner by territory, priority by qualification
  5. 5
    Rep alert
    Slack or email for hot leads only

1Capture the facts you'll judge on

Send what the form collected, without filling in gaps. message is required (up to 4,000 characters). company.country must be a two-letter ISO code. budget is an amount and a currency. If your form doesn't ask for a budget, leave it out, and missing_information will tell you it would help.

Put your rules in qualification_criteria and routing_rules. They travel with every request, so changing a criterion is a config change, not a model retrain.

2Qualify

curl

cURL
curl https://kovca.io/api/v1/leads/qualify \
  -H "Authorization: Bearer $KOVCA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "We'\''re replacing our current tool for 120 sales reps and need the Salesforce integration. Can you send pricing for an annual plan? We want to sign before the end of next month.",
    "company": { "name": "Northwind Traders", "industry": "Wholesale distribution", "size": "500-1000", "country": "US" },
    "budget": { "amount": 60000, "currency": "USD" },
    "product_interest": "Sales analytics",
    "qualification_criteria": {
      "min_budget": { "amount": 20000, "currency": "USD" },
      "countries": ["US", "CA", "GB", "DE"],
      "criteria": ["Needs at least 50 user seats", "Uses Salesforce or plans to integrate with it"]
    },
    "routing_rules": [
      { "territory": "North America", "countries": ["US", "CA"] },
      { "territory": "EMEA", "countries": ["GB", "DE", "FR"] }
    ]
  }'
JSON
{
  "request_id": "req_Fy4pV8nKs2QwL6tM",
  "qualification": "hot",
  "intent": "purchase_ready",
  "fit": "meets_criteria",
  "recommended_action": "prioritize_followup",
  "territory": "North America",
  "signals": ["explicit_buying_intent", "near_term_timeline", "budget_stated", "requirements_stated"],
  "criteria_results": [
    { "criterion": "min_budget", "result": "met", "evidence": "qualification_criteria.min_budget" },
    { "criterion": "countries", "result": "met", "evidence": "qualification_criteria.countries" },
    { "criterion": "Needs at least 50 user seats", "result": "met", "evidence": "qualification_criteria.criteria[0]" },
    { "criterion": "Uses Salesforce or plans to integrate with it", "result": "met", "evidence": "qualification_criteria.criteria[1]" }
  ],
  "missing_information": []
}

Every criterion is reported separately. When a rep asks why a lead was marked hot, the answer is in criteria_results.

TypeScript (Node 18+)

TypeScript
type LeadResult = {
  request_id: string;
  qualification: "hot" | "warm" | "cold" | "unqualified" | "insufficient_information";
  intent: "purchase_ready" | "evaluating" | "information_request" | "existing_customer_or_support" | "not_a_sales_inquiry" | "unclear";
  fit: "meets_criteria" | "does_not_meet" | "unknown" | "not_evaluated";
  recommended_action: "prioritize_followup" | "standard_followup" | "nurture" | "request_information" | "manual_review";
  territory: string | null;
  signals: string[];
  criteria_results: { criterion: string; result: "met" | "not_met" | "unknown"; evidence: string }[];
  missing_information: ("budget" | "timeline" | "company" | "qualification_criteria")[];
};

// Load these from config so sales ops can change them without a deploy.
const qualification_criteria = {
  min_budget: { amount: 20000, currency: "USD" },
  countries: ["US", "CA", "GB", "DE"],
  criteria: ["Needs at least 50 user seats", "Uses Salesforce or plans to integrate with it"],
};
const routing_rules = [
  { territory: "North America", countries: ["US", "CA"] },
  { territory: "EMEA", countries: ["GB", "DE", "FR"] },
];

export async function qualifyLead(form: {
  message: string;
  company?: { name?: string; industry?: string; size?: string; country?: string };
  budget?: { amount: number; currency: string };
  timeline?: string;
}): Promise<LeadResult> {
  const res = await fetch("https://kovca.io/api/v1/leads/qualify", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.KOVCA_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ ...form, message: form.message.slice(0, 4000), qualification_criteria, routing_rules }),
  });
  if (!res.ok) throw new Error(`lead qualification failed: ${res.status}`);
  return res.json();
}

Python

Python
import os, requests

def qualify_lead(form, criteria, routing):
    body = {**form, "message": form["message"][:4000],
            "qualification_criteria": criteria, "routing_rules": routing}
    r = requests.post(
        "https://kovca.io/api/v1/leads/qualify",
        headers={"Authorization": f"Bearer {os.environ['KOVCA_API_KEY']}"},
        json=body, timeout=30,
    )
    r.raise_for_status()
    return r.json()

If the call fails, create the lead in your CRM anyway, unassigned and tagged qualification_pending. A lead that waits for a retry is better than one that disappears.

3Route in the CRM

Qualification is decided by the first rule that matches, so it maps directly onto CRM behavior:

qualificationrecommended_actionWhat happens
hotprioritize_followupAssign to the territory owner now and send an alert. Explicit buying intent with a near-term timeline.
warmstandard_followupAssign to the territory queue with a normal SLA.
coldnurtureEnroll in nurture. Early research or an information request.
unqualifiednurtureA criterion isn't met, such as a budget below your minimum or a country you don't serve.
unqualifiedmanual_reviewNot a sales inquiry, or an existing customer. Send to support or customer success, not sales.
insufficient_informationrequest_informationIntent is unclear, such as "Interested. Call me." Send a short reply asking what they're looking for.

territory is the first routing rule whose countries include company.country, or null. Have a fallback owner for null so no lead goes unassigned.

Watch the currency. A budget in a different currency from min_budget comes back as unknown, not met or not_met. Kovca never converts currencies. If you sell in several currencies, either normalize the budget in your form or send a min_budget per region.

4Alert only for hot leads

Send real-time alerts only for hot. If every warm lead pings a rep, they'll mute the channel within a week. Put warm and cold leads in the CRM views your team already works through, and show criteria_results on the lead record so the rep sees what was met before the first call.

Rolling it out

  1. Tag, don't route. For two weeks, write qualification and criteria_results to the CRM and let the existing process run. Ask reps to flag any lead they disagree with.
  2. Route hot and unqualified first. These are the two ends where routing saves the most time: fast follow-up for buyers, and support requests kept out of sales queues.
  3. Tune the criteria, not the code. Most disagreements come from a criterion that's worded vaguely. Rewrite it as something a person could check from the message.

Volume and cost

One request per inbound lead. The Free plan covers 1,000 successful requests a month at 10 per minute, which is more than most inbound forms receive. Pay As You Go is $0.005 per successful request at 60 per minute, with a spending cap (pricing). Lead messages aren't stored by Kovca.

Try it on last month's form fills

Take twenty submissions, including the ones sales complained about, and run them through with your real criteria. Sign up free, create a key, and paste one into the playground. The qualification rules are in the Lead Intelligence docs.

On this page

  • Qualify on what they said, against what you sell
  • The pipeline
  • Rolling it out
  • Volume and cost
  • Try it on last month's form fills

Lead Intelligence

Qualify inbound leads by stated intent and your criteria, and recommend follow-up priority.

Try it in the playground

Keep going

Lead IntelligenceAPI reference
billingtechnicalneeds_review
Support Triage7 min read

Automating Support Ticket Triage in Your Helpdesk Pipeline

Build a support ticket triage pipeline: webhook intake, category and priority classification, human-review routing, and safe fallbacks. Code in curl, TypeScript and Python.

Read guide
monitorcontact_carrierinvestigate
Shipment Intelligence6 min read

Catching Delivery Exceptions Before Your Customers Do

Turn raw carrier tracking events into routed delivery exceptions: late and stalled parcels, address problems, losses and disputes. A scheduled pipeline with curl, TypeScript and Python.

Read guide
same_variantdifferent_variantuncertain
Catalog Matching9 min read

How to Match Supplier Product Catalogs to Your Own SKUs

A practical guide to matching supplier feeds to your SKUs: blocking candidates, using GTINs safely, handling variants, and routing uncertain matches. Code in curl, TypeScript and Python.

Read guide