Lead Qualifier
ivy.agent.lead-qualifier · v1.0.0
ivyx
Takes a website form submission, reads the company's site, has Jev say whether it is a business customer, and creates a CRM contact and a Slack line when it is; otherwise marks the lead low priority through your API.
Steps
In the order the agent's file lists them, each with where its inputs come from and the values the file fixes. A branch or a loop is a step too.
- 01Ivy NodeRead the company's siteivy.node.web-page-text
Fetches a web page and returns its readable text and title, without scripts, styles or markup.
- takes
urlfrom the agent's input,payload.company_website- returns
site_title
- 02Ivy NodeIs it a business customer?ivy.node.jev-noul
Asks TypeSafe Jev a yes-or-no question about a text or JSON state and returns noul, the probability that the answer is yes, with answer true when it reaches the threshold.
- takes
statefrom step 1,text- set
instructionsJudge from this website text whether the company sells software or online services to other businesses.criteria{"true":"A company that sells software, data or online services to other businesses.","false":"A consumer business, a...threshold0.5- secret
jev_api_keythe stored secret jev-api-key- returns
qualified
- 03Ivy NodeWrite the Slack lineivy.node.template-render
Fills {{ name }} placeholders in a text template from a mapping, and reports which names were missing.
- takes
valuesfrom the agent's input,payload- set
templateQualified lead: {{ name }} at {{ company }} ({{ email }})on_missingempty
- 04BranchOnly a qualified lead
Lets what follows run only when answer of step 2 is true.
- 05Ivy NodeCreate the CRM contactivy.node.http-request
Sends one HTTP request to a URL and returns the status, headers and body, parsed as JSON when the response is JSON.
Runs only on the then branch of step 4.
- takes
urlfrom the agent's input,crm_urljson.emailfrom the agent's input,payload.emailjson.namefrom the agent's input,payload.namejson.companyfrom the agent's input,payload.companyjson.company_websitefrom the agent's input,payload.company_website- set
methodPOST- secret
tokenthe stored secret crm-api-key- returns
crm_status
- 06Ivy NodeTell the sales channelivy.node.http-request
Sends one HTTP request to a URL and returns the status, headers and body, parsed as JSON when the response is JSON.
Runs only on the then branch of step 4.
- takes
json.textfrom step 3,text- set
methodPOST- secret
urlthe stored secret slack-webhook-url
- 07Ivy NodeMark the lead low priorityivy.node.http-request
Sends one HTTP request to a URL and returns the status, headers and body, parsed as JSON when the response is JSON.
Runs only on the else branch of step 4.
- takes
urlfrom the agent's input,leads_urljson.emailfrom the agent's input,payload.emailjson.site_titlefrom step 1,title- set
methodPATCH- secret
tokenthe stored secret saas-api-key- returns
saas_status
Nodes it brings
Adding this agent in IVYX Studio adds these nodes with it. A node your workspace already has is kept as it is, even at another version.
What it touches
Collected from what each of its nodes declares, plus the model call when a step is a model turn. A declaration is the author's statement, and it is what policy rules select on.
What it needs
- A stored secret named
crm-api-key. The agent's file carries the name, never the value. - A stored secret named
jev-api-key. The agent's file carries the name, never the value. - A stored secret named
saas-api-key. The agent's file carries the name, never the value. - A stored secret named
slack-webhook-url. The agent's file carries the name, never the value.
Inputs
| Field | Type | Description |
|---|---|---|
| payloadrequired | object | The event your service posted: email, name, company and company_website from the form. |
| crm_urlrequired | string | The CRM's contacts endpoint; a POST creates a contact. The stand-in's address is used in the cases. |
| leads_urlrequired | string | Your API's leads endpoint; a PATCH with email and priority marks a lead. The stand-in's address is used in the cases. |
Outputs
| Field | Type | Description |
|---|---|---|
| qualified | boolean | True when Jev judged the company a business customer. |
| site_title | string | The title of the company's site. |
| crm_status | integer | The CRM's HTTP status; absent when the lead was not qualified. |
| saas_status | integer | Your API's HTTP status; absent when the lead was qualified. |
Tests
2 of 2 test cases passed on Oct 7, 2026, in the publisher's own environment, before this version was published. The registry keeps that record; it does not run the cases again.
Requires: python:3.9, the stored secret jev-api-key, the CRM's API key stored as the secret crm-api-key, a Slack incoming webhook URL stored as the secret slack-webhook-url, your own API's key stored as the secret saas-api-key, for the cases, the stand-in API on 127.0.0.1:18910 (api-standin.py), which the secrets crm-1, gh-1, saas-1 and the stand-in's /slack/hook address satisfy
- software-company
A B2B analytics company's site qualifies the lead: a CRM contact and a Slack line.
- given
payload{"email":"ada@acme.io","name":"Ada Lovelace","company":"Acme Analytics","company_website":"http://127.0.0.1:18910/pag...crm_urlhttp://127.0.0.1:18910/crm/contactsleads_urlhttp://127.0.0.1:18910/leads- expects
qualifiedequals truesite_titleequals Acme Analyticscrm_statusequals 201
- bakery
A bakery's site does not qualify: the lead is marked low priority.
- given
payload{"email":"rosa@rosasbakery.example","name":"Rosa","company":"Rosa's Bakery","company_website":"http://127.0.0.1:18910...crm_urlhttp://127.0.0.1:18910/crm/contactsleads_urlhttp://127.0.0.1:18910/leads- expects
qualifiedequals falsesite_titleequals Rosa's Bakerysaas_statusequals 200