# eracle/openoutfind architecture

> Architecture diagram of the GitHub repository eracle/openoutfind, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-25). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/eracle/openoutfind
- Repository: https://github.com/eracle/openoutfind

## Overview

OpenOutFind is a self-hosted CLI that turns a product description and campaign objective into qualified B2B leads, with a written fit reason, then exports them as CSV or JSON Lines. The documented run spans ICP setup, licensed-source discovery, LLM qualification, confidence-gated optional email lookup, and export; CRM records retain leads and verdicts across runs. The README documents the workflow and capabilities; sampled source confirms key provider, model, selection, and state responsibilities, but not all orchestration call sites.

Main entry points: [`openoutfind/core/job.py`](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/job.py) (Run orchestration), [`openoutfind/crm/models/lead.py`](https://github.com/eracle/openoutfind/blob/main/openoutfind/crm/models/lead.py) (CRM records), [`openoutfind/core/pipeline/qualify.py`](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/qualify.py) (Lead qualification), [`openoutfind/enrichment/lookup.py`](https://github.com/eracle/openoutfind/blob/main/openoutfind/enrichment/lookup.py) (Email lookup)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_run["Campaign run"]
  node_find["Find command<br/>[find.py]"]
  node_job["Run orchestration<br/>[job.py]"]
  node_config["Campaign config<br/>[config.py]"]
end

subgraph group_learning["Discovery and qualification"]
  node_icp["ICP generation<br/>[icp.py]"]
  node_discovery["Lead discovery<br/>[discovery.py]"]
  node_discovery_walk["Query exploration<br/>[discover.py]"]
  node_selection["Query selection<br/>[select.py]"]
  node_vocabulary["Keyword vocabulary<br/>[vocabulary.py]"]
  node_qualify["Lead qualification<br/>[qualify.py]"]
  node_qualifier["LLM qualifier<br/>[qualifier.py]"]
  node_embeddings["Profile embeddings<br/>[embeddings.py]"]
  node_ready["Lookup readiness<br/>[ready_pool.py]"]
end

subgraph group_records["Campaign records"]
  node_crm[("CRM records<br/>[lead.py]")]
  node_deal_state["Deal state<br/>[deals.py]"]
end

subgraph group_integrations["External services and output"]
  node_enrichment["Email lookup<br/>[lookup.py]"]
  node_bettercontact_lookup["Paid email provider<br/>[bettercontact.py]"]
  node_hub["Contact hub<br/>[service.py]"]
  node_lead_finder["BetterContact Lead Finder"]
  node_llm["LLM service<br/>[llm.py]"]
  node_export["Lead export<br/>[export.py]"]
end

node_operator(("Campaign operator"))
node_recipient(("CSV or JSONL consumer"))

node_operator -->|"requests leads"| node_find
node_find -->|"loads settings"| node_config
node_find -->|"starts run"| node_job
node_job -->|"prepares ICP"| node_icp
node_icp -->|"generates profile"| node_llm
node_job -->|"explores queries"| node_discovery_walk
node_discovery_walk -->|"selects queries"| node_selection
node_selection -->|"expands keywords"| node_vocabulary
node_discovery_walk -->|"requests pages"| node_discovery
node_discovery -->|"searches profiles"| node_lead_finder
node_discovery -->|"records leads"| node_crm
node_job -->|"qualifies candidates"| node_qualify
node_qualify -->|"judges profiles"| node_qualifier
node_qualifier -->|"asks for verdict"| node_llm
node_qualify -->|"embeds profiles"| node_embeddings
node_qualify -->|"stores verdicts"| node_crm
node_qualify -->|"updates deals"| node_deal_state
node_selection -->|"reads lead labels"| node_crm
node_ready -->|"reads qualified leads"| node_crm
node_ready -->|"promotes lookup state"| node_deal_state
node_job -->|"resolves addresses"| node_enrichment
node_enrichment -.->|"checks cached email"| node_hub
node_enrichment -->|"submits lookup"| node_bettercontact_lookup
node_enrichment -->|"stores address"| node_crm
node_job -->|"exports campaign"| node_export
node_export -->|"reads lead records"| node_crm
node_export -->|"writes rows"| node_recipient

click node_find "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/management/commands/find.py"
click node_job "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/job.py"
click node_config "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/config.py"
click node_icp "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/icp.py"
click node_discovery "https://github.com/eracle/openoutfind/blob/main/openoutfind/discovery.py"
click node_discovery_walk "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/discover.py"
click node_selection "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/select.py"
click node_vocabulary "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/vocabulary.py"
click node_qualify "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/qualify.py"
click node_qualifier "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/ml/qualifier.py"
click node_embeddings "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/ml/embeddings.py"
click node_ready "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/ready_pool.py"
click node_crm "https://github.com/eracle/openoutfind/blob/main/openoutfind/crm/models/lead.py"
click node_deal_state "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/db/deals.py"
click node_enrichment "https://github.com/eracle/openoutfind/blob/main/openoutfind/enrichment/lookup.py"
click node_bettercontact_lookup "https://github.com/eracle/openoutfind/blob/main/openoutfind/enrichment/bettercontact.py"
click node_hub "https://github.com/eracle/openoutfind/blob/main/openoutfind/contacts/service.py"
click node_llm "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/llm.py"
click node_export "https://github.com/eracle/openoutfind/blob/main/openoutfind/core/export.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_find,node_job,node_config toneBlue
class node_icp,node_discovery,node_discovery_walk,node_selection,node_vocabulary,node_qualify,node_qualifier,node_embeddings,node_ready toneAmber
class node_crm,node_deal_state toneMint
class node_enrichment,node_bettercontact_lookup,node_hub,node_lead_finder,node_llm,node_export toneRose
class node_operator,node_recipient toneIndigo
```

## Components

### Campaign run

- [Find command](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/management/commands/find.py): `openoutfind/core/management/commands/find.py`
- [Run orchestration](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/job.py): `openoutfind/core/job.py`
- [Campaign config](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/config.py): `openoutfind/core/config.py`

### Discovery and qualification

- [ICP generation](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/icp.py): `openoutfind/core/pipeline/icp.py`
- [Lead discovery](https://github.com/eracle/openoutfind/blob/main/openoutfind/discovery.py): `openoutfind/discovery.py`
- [Query exploration](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/discover.py): `openoutfind/core/pipeline/discover.py`
- [Query selection](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/select.py): `openoutfind/core/pipeline/select.py`
- [Keyword vocabulary](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/vocabulary.py): `openoutfind/core/pipeline/vocabulary.py`
- [Lead qualification](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/qualify.py): `openoutfind/core/pipeline/qualify.py`
- [LLM qualifier](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/ml/qualifier.py): `openoutfind/core/ml/qualifier.py`
- [Profile embeddings](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/ml/embeddings.py): `openoutfind/core/ml/embeddings.py`
- [Lookup readiness](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/pipeline/ready_pool.py): `openoutfind/core/pipeline/ready_pool.py`

### Campaign records

- [CRM records](https://github.com/eracle/openoutfind/blob/main/openoutfind/crm/models/lead.py): `openoutfind/crm/models/lead.py`
- [Deal state](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/db/deals.py): `openoutfind/core/db/deals.py`

### External services and output

- [Email lookup](https://github.com/eracle/openoutfind/blob/main/openoutfind/enrichment/lookup.py): `openoutfind/enrichment/lookup.py`
- [Paid email provider](https://github.com/eracle/openoutfind/blob/main/openoutfind/enrichment/bettercontact.py): `openoutfind/enrichment/bettercontact.py`
- [Contact hub](https://github.com/eracle/openoutfind/blob/main/openoutfind/contacts/service.py): `openoutfind/contacts/service.py`
- **BetterContact Lead Finder**
- [LLM service](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/llm.py): `openoutfind/core/llm.py`
- [Lead export](https://github.com/eracle/openoutfind/blob/main/openoutfind/core/export.py): `openoutfind/core/export.py`

### Other components

- **Campaign operator**
- **CSV or JSONL consumer**

## Connections

- Campaign operator → Find command: requests leads
- Find command → Campaign config: loads settings
- Find command → Run orchestration: starts run
- Run orchestration → ICP generation: prepares ICP
- ICP generation → LLM service: generates profile
- Run orchestration → Query exploration: explores queries
- Query exploration → Query selection: selects queries
- Query selection → Keyword vocabulary: expands keywords
- Query exploration → Lead discovery: requests pages
- Lead discovery → BetterContact Lead Finder: searches profiles
- Lead discovery → CRM records: records leads
- Run orchestration → Lead qualification: qualifies candidates
- Lead qualification → LLM qualifier: judges profiles
- LLM qualifier → LLM service: asks for verdict
- Lead qualification → Profile embeddings: embeds profiles
- Lead qualification → CRM records: stores verdicts
- Lead qualification → Deal state: updates deals
- Query selection → CRM records: reads lead labels
- Lookup readiness → CRM records: reads qualified leads
- Lookup readiness → Deal state: promotes lookup state
- Run orchestration → Email lookup: resolves addresses
- Email lookup → Contact hub: checks cached email
- Email lookup → Paid email provider: submits lookup
- Email lookup → CRM records: stores address
- Run orchestration → Lead export: exports campaign
- Lead export → CRM records: reads lead records
- Lead export → CSV or JSONL consumer: writes rows
