From card scan to pipeline-ready opportunity.
How Vinc-captured leads land in Odoo CRM, who picks them up, and where you tune the rules.
01How vCard leads map to crm.lead
When a visitor submits the lead form on a card, Vinc creates one crm.lead record (Odoo CRM's opportunity model). The mapping:
name— derived from the cardholder + visitor (e.g., "Lead from Jane's card — John Smith")contact_name,email_from,phone— straight from the formpartner_vcard_id— Vinc-specific: links back to the card that captured the leadtag_ids— the lead tags configured on the card at capture timetype=opportunitydescription— the visitor's notes, if your form's notes field is enabled
02Lead routing rules
Vinc auto-assigns each new opportunity to the card's owner. The resolution order is:
- Look up a
res.userswhose login matches the card'semail. If found, that user becomesuser_id. - Otherwise fall back to the user who created the card record (
partner.create_uid), unless that's a system / public user (id ≤ 4). - If neither resolves, the opportunity is left unassigned and Odoo's standard team rules take over.
For more sophisticated patterns — round-robin across a sales team, geo-based routing, account-based routing — layer Odoo's standard CRM lead-assignment rules on top. They run after Vinc's user_id assignment, so configure them to either trust Vinc's pick or override it explicitly.
create_uid is skipped for system users, so cards created via the public /get-started route by anonymous visitors don't accidentally get assigned to OdooBot.
03Pipeline integration
Vinc-captured leads behave like any other CRM opportunity. They show up in:
- The standard pipeline kanban (filterable by Vinc tag)
- List, calendar, and graph views
- The user dashboard's "Recent Leads" widget (Vinc-only filter)
The "Vinc-only filter" is a saved search on partner_vcard_id ≠ False — useful for measuring how Vinc compares to your other lead sources.
04Lead tags as your reporting backbone
Lead tags applied at capture time are the single most useful piece of structured data for downstream reporting. Treat them as the spec for your weekly review:
- Source attribution — which card / channel / event drove this lead
- Intent qualification — what the visitor was after
- Follow-up triage — who picks this up next
Educate cardholders on tags during onboarding. Tags applied 30 days later, when context is gone, are noise.
05Look-alike matches
Every lead captured through a card automatically gets scored against records you already have. The point isn't to find duplicates; it's to give the rep a memory aid. When a fresh prospect arrives, the lead form shows up to three companies and three open opportunities that resemble them, each annotated with the specific signals that matched and the conversation context (won/lost/active, deal size, last note, owner).
The signals (similarity, not equality)
Five signals, weighted to sum to 100:
- Industry hierarchy (weight: 35) — exact match on
industry_idgets the full 35; sibling-industry match (same parent in theres.partner.industrytree) gets 17. So "SaaS" and "IT Services" still match because both sit under "Software". - Geographic gradient (weight: 25) — same city = full 25; same state = 15; same country = 8. Closer geography weighs more, no all-or-nothing penalty.
- Deal-size bucket (weight: 20) —
expected_revenuebucketed into micro (<$10k), small ($10–50k), mid ($50–200k), large ($200k+). Same bucket = full 20; adjacent bucket = 10. For company candidates, the bucket is derived from their highest historicalexpected_revenuein CRM. - Tag word-overlap (weight: 15) — fuzzy on tag NAMES split into words. "FinTech B2B" matches "Financial Services" because both contain a domain word. Cross-model (lead vs partner) so a
crm.tagcan match ares.partner.category. - Description Jaccard (weight: 5, capped) — token overlap between the lead's
descriptionand the candidate's notes. Small weight; mostly a tiebreaker / serendipity signal.
A candidate needs ≥ 25 points to surface. Top 3 per bucket are shown.
The conversation-starter card
Each surfaced match gets a small card on the lead form with the actual context the rep needs to talk:
- Subtitle — industry, city or country (whatever's populated)
- Outcome — for companies: their best-deal label (Won / Lost / Active), amount, close date, owner. Lost reason if present. For open leads: stage, amount, owner.
- Snippet — first 120 chars of the partner's
commentor the lead'sdescription. The fastest "what was this about" cue.
Example card:
Acme Corp — SaaS, San Francisco
same industry (SaaS), same city (San Francisco), same deal-size band (mid)
Won deal · $87,000 · closed Mar 2026 · Sarah Chen
"Migrated from Salesforce, valued our API rate limits"
Pool definitions
- Companies pool: active companies in
res.partner, excluding pure suppliers (supplier_rank > 0withcustomer_rank == 0). The lead's own partner is excluded. Capped at 2,000. - Open-leads pool: opportunities (
type='opportunity') not yet won (probability < 100) and not lost (probability > 0). The current lead is excluded once saved. Capped at 2,000.
Quality scales with your CRM hygiene
The feature is only as good as the data on your existing records. Highest-leverage things to populate:
- Industry on customer companies — the heaviest signal. Set
industry_idon the partner record. - City on customer addresses — much sharper than country alone.
- Tags with meaningful words (not "important" or "to-follow"). The fuzzy matcher splits on words, so "B2B SaaS" and "FinTech B2B" share the "b2b" signal.
- Notes in
comment(partner) ordescription(lead). Even one sentence per record makes the snippet card useful.
A new tenant on day one with sparse customer records sees a hint on every lead explaining what's missing.
What it doesn't do
- No enrichment. Works on the data already in your database. Doesn't call Apollo, Clearbit, ZoomInfo or anyone else. Privacy posture is unchanged.
- No historical reprocessing. Matches are computed live each time a lead is opened. Adding industry to a customer today benefits every lead opened tomorrow.
- No write-back. Read-only suggestion surface.
- No multi-currency normalization. Deal-size buckets use the lead's expected_revenue value as-is. Mixed-currency tenants will see noisier matches until we add per-company normalization in a future pass.
06Event binding (trade shows, conferences, open houses)
Cards have an Active Event field (in the Lead Collection Form Settings group on the card form). When a rep is staffing a booth, they pick the event there for the day. Every lead captured by that card while the field is set gets stamped with crm.lead.event_id = , which means:
- A small Captured at:
badge appears under the lead's title in CRM, so anyone opening the lead immediately sees the source. - Standard Odoo CRM filters work out of the box — saved searches like "leads from SaaStr 2026", "ROI per event", "conversion rate by show". The
event_crmmodule already provides the field; Vinc just hooks captures into it.
What it doesn't do
- No event registration is created. An earlier version did this so the prospect appeared on the event's official attendee list, but every
event.registrationtriggers Odoo's "thanks for registering" email plus any pre-scheduled day-of reminders — meaning a single scan spammed the prospect with up to 10 emails about an event they never knowingly signed up for. The lead-sideevent_idis enough for the reporting; if a rep wants the prospect on the official attendee list, it's two clicks to add them manually from CRM. - One active event per card. The rep has to manually clear the field after the show. We may add a daily cron later that nulls it the day after
event.date_end— for now it's manual. - No booth-level tagging. Odoo's
event_boothmodule supports "which booth at the show" — easy to layer on if anyone asks.
event_crm module (Odoo Community, free) is a hard dependency of Vinc. On install or upgrade, Odoo pulls it in automatically — no manual setup.