Daily Lead Qualification Agent
Agent-ready recipe for qualifying new lead candidates from a folder of database exports on a daily schedule
This recipe tells an agent exactly how to build a daily lead-qualification workflow in andibase.
The workflow is designed for teams that keep lead sources in a folder of database exports and want an agent to turn those imports into candidate companies, request reviewer feedback, and keep bringing in newly available companies every day.
Goal
Create five things in one workspace:
- A
lead-source-companydata definition. - A
lead-candidatedata definition. - A
lead-candidate-feedbackdata definition. - One agent that reviews imported companies and generates candidate leads.
- One automation that runs once per day and asks the agent to process newly available companies and queue them for feedback.
Expected outcome
After following this recipe, the workspace should have:
- a
lead-source-companydefinition for normalized companies imported from a folder of database exports - a
lead-candidatedefinition for qualified candidate companies - a
lead-candidate-feedbackdefinition for reviewer feedback on candidates - a
lead-qualification-agentagent - a
daily-lead-qualificationautomation
At the end of the flow:
- new companies land in
lead-source-company - promising companies are turned into
lead-candidaterows - candidates move into an
awaiting-feedbackstate - reviewer feedback is captured in
lead-candidate-feedback - the next daily run uses that feedback while continuing to process newly imported companies
Use the API documented in:
- Data Model
- Get started (for AI Agents)
- Data definitions API
- Data API
- Agents API
- Automations API
- Agent Tools
Current runtime constraints
The current workspace agent runtime is strong at structured workspace data, but it does not expose a direct tool for browsing arbitrary workspace folders or reading workspace files.
That means this recipe should treat the folder with database exports as an ingestion source, not as the agent's live runtime tool surface.
Use this pattern:
- import the folder content into normalized
lead-source-companyrows first - let the agent qualify only those structured source rows
- use the daily automation to process newly imported rows once per day
- use candidate state plus feedback rows to request review without pretending there is an interactive approval tool if one is not available
- if direct folder-to-data sync is not available yet, mark ingestion as pending instead of pretending the folder was processed
Agent instructions
When an agent executes this recipe, it should follow these rules:
- Create the
lead-source-companydefinition first. - Create the
lead-candidatedefinition second. - Create the
lead-candidate-feedbackdefinition third. - In the current API, data definition handles are generated from
name. Usename: "Lead Source Company",name: "Lead Candidate", andname: "Lead Candidate Feedback"so the created handles normalize tolead-source-company,lead-candidate, andlead-candidate-feedback. - If your execution environment can resolve the created definition ids, you may use relationship fields. If it cannot, use text foreign keys such as
sourceCompanyRecordIdandcandidateRecordId, because the public HTTP API still expectsdataDefinitionIdfor relationship fields. - Create the lead-qualification agent with instructions focused on deduping source companies, scoring fit, generating candidate rows, and requesting feedback through candidate state plus feedback records.
- Create the daily automation last.
- Do not claim that the agent read a folder directly unless the execution environment actually had a file-reading capability for that folder.
- If folder ingestion is not available, keep
lead-source-companyrows or automation logs marked as pending and explain what is still missing. - Ask the user only when a missing choice materially changes qualification quality, such as the ideal customer profile, geography, segment, or excluded industries.
Step 1: Create the data model
Create three data definitions: lead-source-company, lead-candidate, and lead-candidate-feedback.
1. Lead Source Company definition
Use name: "Lead Source Company". The current API derives the handle from the name, so this will create the lead-source-company handle.
Recommended fields:
{
"name": "Lead Source Company",
"description": "Normalized companies imported from a folder of database exports or other source datasets.",
"fields": {
"companyName": {
"name": "Company name",
"type": "text"
},
"websiteDomain": {
"name": "Website domain",
"type": "text"
},
"industry": {
"name": "Industry",
"type": "text"
},
"country": {
"name": "Country",
"type": "text"
},
"employeeRange": {
"name": "Employee range",
"type": "text"
},
"revenueRange": {
"name": "Revenue range",
"type": "text"
},
"sourceFolderPath": {
"name": "Source folder path",
"type": "text"
},
"sourceFileName": {
"name": "Source file name",
"type": "text"
},
"sourceRecordKey": {
"name": "Source record key",
"type": "text"
},
"importedAt": {
"name": "Imported at",
"type": "timestamp"
},
"qualificationState": {
"name": "Qualification state",
"type": "select",
"options": [
{ "value": "new", "label": "New", "color": "blue" },
{ "value": "processed", "label": "Processed", "color": "green" },
{ "value": "duplicate", "label": "Duplicate", "color": "zinc" },
{ "value": "ignored", "label": "Ignored", "color": "stone" },
{ "value": "pending-ingestion", "label": "Pending ingestion", "color": "amber" }
]
},
"qualificationNotes": {
"name": "Qualification notes",
"type": "text",
"variant": "long-text"
}
}
}Recommended defaults for imported source rows:
qualificationState:newimportedAt: now
2. Lead Candidate definition
Use name: "Lead Candidate". The current API derives the handle from the name, so this will create the lead-candidate handle.
Recommended fields:
{
"name": "Lead Candidate",
"description": "Qualified potential lead candidates generated from imported companies.",
"fields": {
"sourceCompanyRecordId": {
"name": "Source company record id",
"description": "Store the linked lead-source-company row id when the client only has public HTTP API access.",
"type": "text"
},
"companyName": {
"name": "Company name",
"type": "text"
},
"websiteDomain": {
"name": "Website domain",
"type": "text"
},
"industry": {
"name": "Industry",
"type": "text"
},
"country": {
"name": "Country",
"type": "text"
},
"fitScore": {
"name": "Fit score",
"type": "number"
},
"priority": {
"name": "Priority",
"type": "select",
"options": [
{ "value": "low", "label": "Low", "color": "zinc" },
{ "value": "medium", "label": "Medium", "color": "blue" },
{ "value": "high", "label": "High", "color": "amber" },
{ "value": "critical", "label": "Critical", "color": "red" }
]
},
"candidateStatus": {
"name": "Candidate status",
"type": "select",
"options": [
{ "value": "new", "label": "New", "color": "blue" },
{ "value": "awaiting-feedback", "label": "Awaiting feedback", "color": "amber" },
{ "value": "approved", "label": "Approved", "color": "green" },
{ "value": "rejected", "label": "Rejected", "color": "stone" },
{ "value": "duplicate", "label": "Duplicate", "color": "zinc" }
]
},
"whyNow": {
"name": "Why now",
"type": "text",
"variant": "long-text"
},
"qualificationReason": {
"name": "Qualification reason",
"type": "text",
"variant": "long-text"
},
"recommendedNextStep": {
"name": "Recommended next step",
"type": "text",
"variant": "long-text"
},
"feedbackRequestedAt": {
"name": "Feedback requested at",
"type": "timestamp"
},
"feedbackRequestNote": {
"name": "Feedback request note",
"type": "text",
"variant": "long-text"
},
"lastFeedbackSummary": {
"name": "Last feedback summary",
"type": "text",
"variant": "long-text"
},
"candidateBatchDate": {
"name": "Candidate batch date",
"type": "date"
}
}
}Recommended defaults for agent-created candidate rows:
candidateStatus:awaiting-feedbackcandidateBatchDate: todayfeedbackRequestedAt: nowfeedbackRequestNote: short explanation of what the reviewer should validate
3. Lead Candidate Feedback definition
Use name: "Lead Candidate Feedback". The current API derives the handle from the name, so this will create the lead-candidate-feedback handle.
Recommended fields:
{
"name": "Lead Candidate Feedback",
"description": "Reviewer feedback on qualified lead candidates.",
"fields": {
"candidateRecordId": {
"name": "Candidate record id",
"description": "Store the linked lead-candidate row id when the client only has public HTTP API access.",
"type": "text"
},
"reviewerName": {
"name": "Reviewer name",
"type": "text"
},
"verdict": {
"name": "Verdict",
"type": "select",
"options": [
{ "value": "approve", "label": "Approve", "color": "green" },
{ "value": "reject", "label": "Reject", "color": "stone" },
{ "value": "needs-work", "label": "Needs work", "color": "amber" }
]
},
"notes": {
"name": "Notes",
"type": "text",
"variant": "long-text"
},
"createdAtManual": {
"name": "Created at manual",
"type": "timestamp"
}
}
}If your client already has the created definition ids, you can replace the text foreign keys with relationship fields:
{
"sourceCompanyId": {
"name": "Source company",
"type": "relationship",
"dataDefinitionId": "<lead-source-company-definition-id>"
},
"candidateId": {
"name": "Candidate",
"type": "relationship",
"dataDefinitionId": "<lead-candidate-definition-id>"
}
}4. Example create requests
Create the definitions with POST /api/v1/data-definitions.
Current API note:
- the create payload uses
name,description, andfields - the handle is derived from
name - data rows are created with
data, notattributes
Example for lead-source-company:
curl -X POST "https://andibase.com/api/v1/data-definitions" \
-H "Authorization: Bearer $ANDI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Lead Source Company",
"description": "Normalized companies imported from a folder of database exports or other source datasets.",
"fields": {
"companyName": { "name": "Company name", "type": "text" },
"websiteDomain": { "name": "Website domain", "type": "text" },
"industry": { "name": "Industry", "type": "text" },
"country": { "name": "Country", "type": "text" },
"sourceFolderPath": { "name": "Source folder path", "type": "text" },
"sourceFileName": { "name": "Source file name", "type": "text" },
"sourceRecordKey": { "name": "Source record key", "type": "text" },
"importedAt": { "name": "Imported at", "type": "timestamp" },
"qualificationState": {
"name": "Qualification state",
"type": "select",
"options": [
{ "value": "new", "label": "New", "color": "blue" },
{ "value": "processed", "label": "Processed", "color": "green" },
{ "value": "pending-ingestion", "label": "Pending ingestion", "color": "amber" }
]
}
}
}'Create sample imported company rows with POST /api/v1/data-definitions/lead-source-company/data/upsert-many.
curl -X POST "https://andibase.com/api/v1/data-definitions/lead-source-company/data/upsert-many" \
-H "Authorization: Bearer $ANDI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"data": {
"companyName": "Northwind Logistics",
"websiteDomain": "northwind-logistics.example",
"industry": "Logistics",
"country": "Chile",
"employeeRange": "200-500",
"revenueRange": "10M-50M",
"sourceFolderPath": "/imports/leads",
"sourceFileName": "march-db-export.csv",
"sourceRecordKey": "northwind-logistics-001",
"importedAt": "2026-03-20T09:00:00.000Z",
"qualificationState": "new"
}
}
]
}'Step 2: Create the lead qualification agent
Create one workspace agent with handle lead-qualification-agent.
Recommended behavior:
- review only newly imported source companies that have not been processed yet
- avoid duplicate candidates by domain, company name, or obvious near-duplicates
- score fit against the current ideal customer profile
- create candidate rows only when the company looks promising enough
- mark candidates as
awaiting-feedback, store the rationale, and leave a clear feedback request note - update source rows to
processed,duplicate, orignored - use reviewer feedback to improve future qualification decisions without rewriting historical facts
Recommended agent payload:
{
"name": "Lead Qualification Agent",
"handle": "lead-qualification-agent",
"description": "Reviews imported companies, creates lead candidates, and tracks feedback.",
"model": "openai/gpt-5.4",
"capabilities": {
"webAccess": false,
"browserAccess": false,
"objectsAccess": true
},
"instructions": "You manage a lead-qualification workflow. Use the lead-source-company, lead-candidate, and lead-candidate-feedback data definitions as the source of truth. Be action-oriented: make reasonable assumptions, complete the work when the intended outcome is clear, and avoid asking for confirmation on every small step. Review only source companies whose qualificationState is new. Create lead-candidate rows only when the company appears to be a meaningful prospect based on industry, geography, size, and stated ICP rules. Avoid duplicates by checking existing candidate company names and domains first. When you create a candidate, set candidateStatus to awaiting-feedback, explain the qualificationReason clearly, store a practical recommendedNextStep, and write a short feedbackRequestNote that tells the reviewer what to validate or challenge. After processing a source row, update its qualificationState to processed, duplicate, or ignored. When user feedback appears in lead-candidate-feedback, incorporate it into future judgments but do not alter historical reviewer feedback. Never pretend that direct folder sync happened unless the source rows already exist."
}Create the agent with POST /api/v1/agents.
Step 3: Create the daily automation
Create one automation with handle daily-lead-qualification.
The automation should run once per day and call the agent to process newly imported source companies.
Current platform note:
- automations can run on
cron - automation scripts can call an agent with
api.callAgent(...) - if folder-to-data ingestion is not available yet, the automation should still run and report that ingestion is pending instead of silently failing
Recommended automation payload:
{
"name": "Daily lead qualification",
"handle": "daily-lead-qualification",
"description": "Runs once per day to qualify newly imported companies and create candidate leads.",
"enabled": true,
"config": {
"triggers": [
{
"type": "cron",
"enabled": true,
"config": {
"cron": "0 8 * * *"
}
}
],
"script": {
"source": "async function main(input, api) {\\n api.log('daily lead qualification started', { input });\\n\\n const reply = await api.callAgent({\\n handle: 'lead-qualification-agent',\\n prompt: [\\n 'Process newly imported lead-source-company rows since the last daily run.',\\n 'Create lead-candidate rows for qualified companies.',\\n 'Avoid duplicates against existing lead-candidate rows.',\\n 'Update each processed source row qualificationState.',\\n 'For every new candidate, set candidateStatus to awaiting-feedback and write a concise feedbackRequestNote.',\\n 'Summarize the candidate batch so a reviewer can quickly approve, reject, or request changes.',\\n 'If there are no new source rows because folder ingestion is not set up yet, state that ingestion is pending and do not invent candidates.',\\n 'Explain why every new candidate is promising and what feedback is needed next.'\\n ].join(' ')\\n });\\n\\n return { ok: true, chatId: reply.chatId, reply: reply.reply };\\n}\\n\\nmain;"
}
}
}Create the automation with POST /api/v1/automations.
Step 4: Feedback loop
When reviewers give feedback on candidates, store it in lead-candidate-feedback.
Recommended operating pattern:
- Reviewer adds one feedback row per candidate.
- Candidate status is updated to
approved,rejected, or remainsawaiting-feedback. - On the next daily run, the agent can inspect recent feedback rows before scoring new companies.
If the current execution environment cannot proactively ask the user for feedback, use data state to request it:
- mark candidates as
awaiting-feedback - set
feedbackRequestedAt - store a short
feedbackRequestNotethat says what the reviewer should validate - store a short note in
lastFeedbackSummarysuch asWaiting for reviewer decision
Step 5: Daily operating loop
Use this operating loop for the ongoing workflow:
- New database exports are normalized into
lead-source-company. - The daily automation runs and asks the agent to review only rows that are still
new. - The agent creates
lead-candidaterows for promising companies and leaves them inawaiting-feedback. - Reviewers add rows to
lead-candidate-feedbackand optionally update the candidate status. - The next daily run reads recent feedback before qualifying the next batch of newly imported companies.
Recommended acceptance checks
An agent should consider the recipe complete only if all of the following are true:
lead-source-companyexists and includes the required fields.lead-candidateexists and includes either thesourceCompanyIdrelationship or thesourceCompanyRecordIdfallback used by the current public HTTP API flow.lead-candidate-feedbackexists and includes either thecandidateIdrelationship or thecandidateRecordIdfallback used by the current public HTTP API flow.lead-qualification-agentexists.daily-lead-qualificationexists and includes one enabled cron trigger.- At least one sample
lead-source-companyrow can be created successfully. - At least one candidate can be created with
candidateStatus: awaiting-feedbackand a non-emptyfeedbackRequestNote. - The recipe makes it explicit that folder ingestion is pending when direct folder sync is not available.
Minimal delivery summary
When the agent finishes, it should report:
- the created data definition handles
- the created agent handle
- the created automation handle
- whether folder-to-data ingestion is working or still pending
- how reviewer feedback is requested and captured
- any assumptions made for ICP, geography, company size, or excluded industries