HarborCRM Task Solving Skill
Overview
HarborCRM tasks involve reconciling data from multiple API endpoints (events/tradeshows, CRM, finance, policies) into structured JSON outputs. Tasks fall into three main categories:
- Event/Trade Show Reconciliation (train_001, train_004) — Post-event sponsor finance and lead handoff
- Trade Show Prospecting (train_002, train_005) — Exhibitor qualification and ranking for campaigns
- Import Batch Preparation (train_003) — CRM import batch deduplication, suppression, and cleaning
Environment & API Access
- Base URL: Use only
GDPEVO_ENV_BASE_URL=http://34.46.77.124:8001. Do NOT use localhost unless the remote URL itself points there.
- Authentication: Typically none required for these public API endpoints.
- Request method: GET for all listed endpoints.
- Do not call global list/index endpoints to discover IDs. Only query endpoints and entity IDs explicitly named in the task prompt.
Common API Endpoint Patterns
Events / Trade Shows
GET /api/events/{event_id} — Event metadata (name, dates, lead opportunity amount, follow-up due dates)
GET /api/events/{event_id}/orders — Sponsor orders
GET /api/events/{event_id}/badges — Badge scans/attendees
GET /api/events/{event_id}/sponsor_packages — Available sponsor tiers
GET /api/tradeshows — List of trade shows
GET /api/tradeshows/{show_id}/exhibitors — Exhibitor list with platforms, booth, country, website
GET /api/tradeshows/{show_id}/meeting_interest — Interest scores and demo requests
CRM
GET /api/crm/accounts — Existing accounts (match by company name normalization)
GET /api/crm/contacts — Existing contacts (match by email/phone normalization)
GET /api/crm/opportunities — Existing opportunities
GET /api/crm/campaign_members?event_id={event_id} — Campaign membership status
Finance
GET /api/finance/invoices?event_id={event_id} — Invoice status, paid amounts, open balances
Import Batches
GET /api/import_batches — List batches
GET /api/import_batches/{batch_id}/raw_contacts — Raw import rows
GET /api/import_batches/{batch_id}/suppression — Suppression list
Policies
GET /api/policies — Campaign policies defining qualification criteria, platform enums, priority tiers, exclusion rules. Always fetch and read policies — they contain the ground truth for qualification logic.
Core Business Rules
1. Always Read Policies First
The /api/policies endpoint contains the authoritative rules for:
- What platforms/technologies qualify (e.g., AUV, ROV, Underwater Camera)
- What relationship types are excluded (distributor, service_provider, sensor_vendor, research)
- Priority tier thresholds and opportunity sizing
- Exclusion reasons and their controlled vocabulary
2. Sponsor vs. Non-Sponsor Separation
- Sponsors are handled via finance reconciliation (invoice status, open balances, follow-up dates)
- Non-sponsor leads are evaluated for CRM import qualification
- Never treat a sponsor attendee as a qualified non-sponsor lead
- Inactive/canceled sponsor records are excluded
3. CRM Account Matching
- Match exhibitors/import records to existing CRM accounts by normalized company name (case-insensitive, whitespace-normalized)
- If a matching account exists, set
crm_action to update_existing (or equivalent)
- If no match exists, set
crm_action to create_account
- If disqualified/excluded, set
crm_action to no_import
4. Contact Normalization
- Email: lowercase, trimmed. Empty string when missing.
- Phone: digits only. Empty string when missing.
- Match existing contacts by normalized email or normalized phone.
5. Exclusion Rules (Common Patterns)
Records are excluded for these reasons (controlled vocabulary varies slightly by task):
sponsor_attendee — Part of a sponsoring company
non_business_badge — Personal/non-commercial attendee
existing_disqualified — Already in CRM as disqualified
missing_contact — No usable contact information
distributor_only / service_only / sensor_only / research_only — Not a manufacturer/OEM
not_target_market — Outside campaign scope
6. Finance Status Values
Controlled values for sponsor finance status:
paid_deferred — Paid but revenue deferred
open_invoice — Has unpaid invoice (report open balance separately)
proposal_only — Sponsorship proposed but not contracted
7. Opportunity Sizing (Prospecting Tasks)
Priority tiers map to fixed USD amounts:
- Tier A: USD 120,000
- Tier B: USD 90,000
- Tier C: USD 50,000
Tier assignment rules (from train_005):
- A: Demo-requested qualified leads with interest score ≥ 90
- B: Demo-requested qualified leads with interest score ≥ 80
- C: All other qualified leads
Sorting & Ordering Rules
Always follow the exact ordering specified in the answer template:
| Field / List |
Sort Key |
Direction |
sponsor_statuses |
account_name |
ascending |
qualified_lead_accounts |
account_name |
ascending |
excluded_records |
company_name, then contact_name |
ascending |
qualified_exhibitors |
company_name |
ascending |
excluded_near_misses |
company_name |
ascending |
excluded_exhibitors |
company_name |
ascending |
ranked_leads |
rank (1-based contiguous) |
ascending |
badge_decisions |
badge_id |
ascending |
campaign_member_actions |
subject_key |
ascending |
badge_only_contacts |
company_name |
ascending |
clean_contacts |
clean_contact_id |
ascending |
duplicate_keys |
key |
ascending |
removed_rows |
row_id |
ascending |
existing_crm_overlap_account_ids |
account ID |
ascending |
platforms enum |
Fixed: AUV, ROV, Underwater Camera |
— |
qualified_non_sponsor_account_names |
name |
ascending |
unpaid_sponsor_account_names |
name |
ascending |
Ranking Logic (Prospecting Tasks)
For ranked lead lists, the sort order is:
- Demo requested (true before false)
- Interest score descending (higher first)
- Broader platform coverage (more platforms first)
- Company name ascending (alphabetical tie-breaker)
Ranks must be 1-based and contiguous (no gaps).
Import Batch Cleaning Rules (train_003 Pattern)
- Deduplicate by key (typically normalized email or company+contact). Keep the row with the earliest
captured_at or latest based on policy. Record winner and removed row IDs.
- Suppress rows matching the suppression list (by email or other key).
- Remove unusable rows missing required contact fields.
- CRM action assignment: For each surviving row, check against existing CRM accounts/contacts and assign
create_account, update_existing, no_import, or suppress.
- Campaign member count: Count surviving cleaned contacts that are importable (not
no_import or suppress).
Output Conventions
- Return exactly one JSON object. No markdown code fences, no explanatory prose outside the JSON.
- Use only the keys declared in the answer template. Do not add extra fields.
- Use
null (not empty string) for nullable fields unless the template specifies empty string.
- Dates:
YYYY-MM-DD format.
- Currency: integer USD (no decimals).
- Counts: integers.
- Booleans: JSON
true/false (not strings).
- Enum values: use exactly the allowed strings (case-sensitive).
Common Pitfalls
- Forgetting to fetch policies — The policies endpoint contains critical qualification logic that cannot be inferred from the prompt alone.
- Wrong base URL — Always use the remote URL from
environment_access.md, not localhost.
- Incorrect sorting — Many tasks have multi-key sorts. Read the template carefully.
- Non-contiguous ranks — After filtering, recompute ranks to be 1-based contiguous.
- Case-sensitive matching — Company names and emails must be normalized before matching.
- Including sponsors as leads — Sponsor attendees are never qualified non-sponsor leads.
- Missing open balance — For
open_invoice sponsors, report both the package amount and the open balance.
- Platform ordering — The enum order AUV → ROV → Underwater Camera is fixed; do not alphabetize.
- Duplicate handling — In import batches, removed duplicates still count toward removal totals and should be listed with reason
"duplicate".
- Campaign member count — Only count rows that will actually be imported (exclude
no_import and suppress).
Workflow Checklist
- Read
MANIFEST.md and environment_access.md
- Read the task prompt and answer template
- Fetch all relevant API endpoints (do not skip policies)
- Normalize all text fields for matching (company names, emails, phones)
- Apply qualification/exclusion rules per policies
- Compute derived fields (opportunity amounts, ranks, counts)
- Sort all lists per template rules
- Validate output against template (required keys, enum values, types)
- Emit pure JSON only
1---2name: self-attempt-01-283description: HarborCRM Task Solving Skill4---5# HarborCRM Task Solving Skill67## Overview89HarborCRM tasks involve reconciling data from multiple API endpoints (events/tradeshows, CRM, finance, policies) into structured JSON outputs. Tasks fall into three main categories:10111. **Event/Trade Show Reconciliation** (train_001, train_004) — Post-event sponsor finance and lead handoff122. **Trade Show Prospecting** (train_002, train_005) — Exhibitor qualification and ranking for campaigns133. **Import Batch Preparation** (train_003) — CRM import batch deduplication, suppression, and cleaning1415## Environment & API Access1617- **Base URL**: Use only `GDPEVO_ENV_BASE_URL=http://34.46.77.124:8001`. Do NOT use localhost unless the remote URL itself points there.18- **Authentication**: Typically none required for these public API endpoints.19- **Request method**: GET for all listed endpoints.20- **Do not** call global list/index endpoints to discover IDs. Only query endpoints and entity IDs explicitly named in the task prompt.2122## Common API Endpoint Patterns2324### Events / Trade Shows25- `GET /api/events/{event_id}` — Event metadata (name, dates, lead opportunity amount, follow-up due dates)26- `GET /api/events/{event_id}/orders` — Sponsor orders27- `GET /api/events/{event_id}/badges` — Badge scans/attendees28- `GET /api/events/{event_id}/sponsor_packages` — Available sponsor tiers29- `GET /api/tradeshows` — List of trade shows30- `GET /api/tradeshows/{show_id}/exhibitors` — Exhibitor list with platforms, booth, country, website31- `GET /api/tradeshows/{show_id}/meeting_interest` — Interest scores and demo requests3233### CRM34- `GET /api/crm/accounts` — Existing accounts (match by company name normalization)35- `GET /api/crm/contacts` — Existing contacts (match by email/phone normalization)36- `GET /api/crm/opportunities` — Existing opportunities37- `GET /api/crm/campaign_members?event_id={event_id}` — Campaign membership status3839### Finance40- `GET /api/finance/invoices?event_id={event_id}` — Invoice status, paid amounts, open balances4142### Import Batches43- `GET /api/import_batches` — List batches44- `GET /api/import_batches/{batch_id}/raw_contacts` — Raw import rows45- `GET /api/import_batches/{batch_id}/suppression` — Suppression list4647### Policies48- `GET /api/policies` — Campaign policies defining qualification criteria, platform enums, priority tiers, exclusion rules. **Always fetch and read policies** — they contain the ground truth for qualification logic.4950## Core Business Rules5152### 1. Always Read Policies First53The `/api/policies` endpoint contains the authoritative rules for:54- What platforms/technologies qualify (e.g., AUV, ROV, Underwater Camera)55- What relationship types are excluded (distributor, service_provider, sensor_vendor, research)56- Priority tier thresholds and opportunity sizing57- Exclusion reasons and their controlled vocabulary5859### 2. Sponsor vs. Non-Sponsor Separation60- **Sponsors** are handled via finance reconciliation (invoice status, open balances, follow-up dates)61- **Non-sponsor leads** are evaluated for CRM import qualification62- Never treat a sponsor attendee as a qualified non-sponsor lead63- Inactive/canceled sponsor records are excluded6465### 3. CRM Account Matching66- Match exhibitors/import records to existing CRM accounts by **normalized company name** (case-insensitive, whitespace-normalized)67- If a matching account exists, set `crm_action` to `update_existing` (or equivalent)68- If no match exists, set `crm_action` to `create_account`69- If disqualified/excluded, set `crm_action` to `no_import`7071### 4. Contact Normalization72- **Email**: lowercase, trimmed. Empty string when missing.73- **Phone**: digits only. Empty string when missing.74- Match existing contacts by normalized email or normalized phone.7576### 5. Exclusion Rules (Common Patterns)77Records are excluded for these reasons (controlled vocabulary varies slightly by task):78- `sponsor_attendee` — Part of a sponsoring company79- `non_business_badge` — Personal/non-commercial attendee80- `existing_disqualified` — Already in CRM as disqualified81- `missing_contact` — No usable contact information82- `distributor_only` / `service_only` / `sensor_only` / `research_only` — Not a manufacturer/OEM83- `not_target_market` — Outside campaign scope8485### 6. Finance Status Values86Controlled values for sponsor finance status:87- `paid_deferred` — Paid but revenue deferred88- `open_invoice` — Has unpaid invoice (report open balance separately)89- `proposal_only` — Sponsorship proposed but not contracted9091### 7. Opportunity Sizing (Prospecting Tasks)92Priority tiers map to fixed USD amounts:93- **Tier A**: USD 120,00094- **Tier B**: USD 90,00095- **Tier C**: USD 50,0009697Tier assignment rules (from train_005):98- **A**: Demo-requested qualified leads with interest score ≥ 9099- **B**: Demo-requested qualified leads with interest score ≥ 80100- **C**: All other qualified leads101102## Sorting & Ordering Rules103104Always follow the exact ordering specified in the answer template:105106| Field / List | Sort Key | Direction |107|-------------|----------|-----------|108| `sponsor_statuses` | `account_name` | ascending |109| `qualified_lead_accounts` | `account_name` | ascending |110| `excluded_records` | `company_name`, then `contact_name` | ascending |111| `qualified_exhibitors` | `company_name` | ascending |112| `excluded_near_misses` | `company_name` | ascending |113| `excluded_exhibitors` | `company_name` | ascending |114| `ranked_leads` | rank (1-based contiguous) | ascending |115| `badge_decisions` | `badge_id` | ascending |116| `campaign_member_actions` | `subject_key` | ascending |117| `badge_only_contacts` | `company_name` | ascending |118| `clean_contacts` | `clean_contact_id` | ascending |119| `duplicate_keys` | `key` | ascending |120| `removed_rows` | `row_id` | ascending |121| `existing_crm_overlap_account_ids` | account ID | ascending |122| `platforms` enum | Fixed: AUV, ROV, Underwater Camera | — |123| `qualified_non_sponsor_account_names` | name | ascending |124| `unpaid_sponsor_account_names` | name | ascending |125126## Ranking Logic (Prospecting Tasks)127128For ranked lead lists, the sort order is:1291. **Demo requested** (true before false)1302. **Interest score** descending (higher first)1313. **Broader platform coverage** (more platforms first)1324. **Company name** ascending (alphabetical tie-breaker)133134Ranks must be **1-based and contiguous** (no gaps).135136## Import Batch Cleaning Rules (train_003 Pattern)1371381. **Deduplicate** by key (typically normalized email or company+contact). Keep the row with the earliest `captured_at` or latest based on policy. Record winner and removed row IDs.1392. **Suppress** rows matching the suppression list (by email or other key).1403. **Remove unusable** rows missing required contact fields.1414. **CRM action assignment**: For each surviving row, check against existing CRM accounts/contacts and assign `create_account`, `update_existing`, `no_import`, or `suppress`.1425. **Campaign member count**: Count surviving cleaned contacts that are importable (not `no_import` or `suppress`).143144## Output Conventions145146- Return **exactly one JSON object**. No markdown code fences, no explanatory prose outside the JSON.147- Use only the keys declared in the answer template. Do not add extra fields.148- Use `null` (not empty string) for nullable fields unless the template specifies empty string.149- Dates: `YYYY-MM-DD` format.150- Currency: integer USD (no decimals).151- Counts: integers.152- Booleans: JSON `true`/`false` (not strings).153- Enum values: use exactly the allowed strings (case-sensitive).154155## Common Pitfalls1561571. **Forgetting to fetch policies** — The policies endpoint contains critical qualification logic that cannot be inferred from the prompt alone.1582. **Wrong base URL** — Always use the remote URL from `environment_access.md`, not localhost.1593. **Incorrect sorting** — Many tasks have multi-key sorts. Read the template carefully.1604. **Non-contiguous ranks** — After filtering, recompute ranks to be 1-based contiguous.1615. **Case-sensitive matching** — Company names and emails must be normalized before matching.1626. **Including sponsors as leads** — Sponsor attendees are never qualified non-sponsor leads.1637. **Missing open balance** — For `open_invoice` sponsors, report both the package amount and the open balance.1648. **Platform ordering** — The enum order AUV → ROV → Underwater Camera is fixed; do not alphabetize.1659. **Duplicate handling** — In import batches, removed duplicates still count toward removal totals and should be listed with reason `"duplicate"`.16610. **Campaign member count** — Only count rows that will actually be imported (exclude `no_import` and `suppress`).167168## Workflow Checklist1691701. Read `MANIFEST.md` and `environment_access.md`1712. Read the task prompt and answer template1723. Fetch all relevant API endpoints (do not skip policies)1734. Normalize all text fields for matching (company names, emails, phones)1745. Apply qualification/exclusion rules per policies1756. Compute derived fields (opportunity amounts, ranks, counts)1767. Sort all lists per template rules1778. Validate output against template (required keys, enum values, types)1789. Emit pure JSON only