andibase

Daily Lead Qualification Agent

Agent-ready recipe for qualifying new lead candidates from a folder of database exports on a daily schedule

Open Markdown

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:

  1. A lead-source-company data definition.
  2. A lead-candidate data definition.
  3. A lead-candidate-feedback data definition.
  4. One agent that reviews imported companies and generates candidate leads.
  5. 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-company definition for normalized companies imported from a folder of database exports
  • a lead-candidate definition for qualified candidate companies
  • a lead-candidate-feedback definition for reviewer feedback on candidates
  • a lead-qualification-agent agent
  • a daily-lead-qualification automation

At the end of the flow:

  • new companies land in lead-source-company
  • promising companies are turned into lead-candidate rows
  • candidates move into an awaiting-feedback state
  • 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:

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-company rows 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:

  1. Create the lead-source-company definition first.
  2. Create the lead-candidate definition second.
  3. Create the lead-candidate-feedback definition third.
  4. In the current API, data definition handles are generated from name. Use name: "Lead Source Company", name: "Lead Candidate", and name: "Lead Candidate Feedback" so the created handles normalize to lead-source-company, lead-candidate, and lead-candidate-feedback.
  5. If your execution environment can resolve the created definition ids, you may use relationship fields. If it cannot, use text foreign keys such as sourceCompanyRecordId and candidateRecordId, because the public HTTP API still expects dataDefinitionId for relationship fields.
  6. 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.
  7. Create the daily automation last.
  8. Do not claim that the agent read a folder directly unless the execution environment actually had a file-reading capability for that folder.
  9. If folder ingestion is not available, keep lead-source-company rows or automation logs marked as pending and explain what is still missing.
  10. 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: new
  • importedAt: 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-feedback
  • candidateBatchDate: today
  • feedbackRequestedAt: now
  • feedbackRequestNote: 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, and fields
  • the handle is derived from name
  • data rows are created with data, not attributes

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, or ignored
  • 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:

  1. Reviewer adds one feedback row per candidate.
  2. Candidate status is updated to approved, rejected, or remains awaiting-feedback.
  3. 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 feedbackRequestNote that says what the reviewer should validate
  • store a short note in lastFeedbackSummary such as Waiting for reviewer decision

Step 5: Daily operating loop

Use this operating loop for the ongoing workflow:

  1. New database exports are normalized into lead-source-company.
  2. The daily automation runs and asks the agent to review only rows that are still new.
  3. The agent creates lead-candidate rows for promising companies and leaves them in awaiting-feedback.
  4. Reviewers add rows to lead-candidate-feedback and optionally update the candidate status.
  5. The next daily run reads recent feedback before qualifying the next batch of newly imported companies.

An agent should consider the recipe complete only if all of the following are true:

  1. lead-source-company exists and includes the required fields.
  2. lead-candidate exists and includes either the sourceCompanyId relationship or the sourceCompanyRecordId fallback used by the current public HTTP API flow.
  3. lead-candidate-feedback exists and includes either the candidateId relationship or the candidateRecordId fallback used by the current public HTTP API flow.
  4. lead-qualification-agent exists.
  5. daily-lead-qualification exists and includes one enabled cron trigger.
  6. At least one sample lead-source-company row can be created successfully.
  7. At least one candidate can be created with candidateStatus: awaiting-feedback and a non-empty feedbackRequestNote.
  8. 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

On this page