/* HeyReach client. Verified against their public API: - base https://api.heyreach.io/api/public - auth X-API-KEY header, NOT Authorization: Bearer - add POST /campaign/AddLeadsToCampaignV2, max 100 leads per call, body { campaignId, accountLeadPairs } - V2 returns { addedLeadsCount, updatedLeadsCount, failedLeadsCount }. V1 returns a bare integer, which is why we use V2. Two traps worth knowing before you wire this up: - the campaign has to be running. Adding to a DRAFT one fails, and /campaign/Resume rejects DRAFT with a misleading message. - a campaign whose list has emptied auto-pauses, so a push can succeed and still send nothing until it is resumed. */ const BASE = 'https://api.heyreach.io/api/public'; async function call(path, key, init = {}) { const r = await fetch(BASE + path, { ...init, headers: { 'X-API-KEY': key, 'content-type': 'application/json', ...(init.headers || {}) } }); if (!r.ok) throw new Error(`heyreach_${r.status}: ${(await r.text()).slice(0, 300)}`); return r.status === 204 ? {} : r.json(); } export const checkKey = (key) => call('/auth/CheckApiKey', key); export const listAccounts = (key) => call('/linkedinaccount/GetAll', key, { method: 'POST', body: JSON.stringify({ offset: 0, limit: 100 }) }); /* One approved prospect -> one accountLeadPair. The LinkedIn message rides along as a custom field so the sequence can drop it in with {{message}}. */ export function pair(p, linkedInAccountId) { return { linkedInAccountId, lead: { firstName: p.first_name, lastName: p.last_name, profileUrl: p.linkedin_url, companyName: p.company, position: p.title, location: [p.city, p.state].filter(Boolean).join(', '), customUserFields: [ { name: 'icebreaker', value: p.icebreaker }, { name: 'message', value: p.linkedin_message }, { name: 'trigger', value: p.trigger_summary }, { name: 'source', value: p.trigger_source } ] } }; } export async function push(campaignId, approved, accountId, key) { const pairs = approved.map((p) => pair(p, accountId)); const totals = { addedLeadsCount: 0, updatedLeadsCount: 0, failedLeadsCount: 0 }; for (let i = 0; i < pairs.length; i += 100) { const r = await call('/campaign/AddLeadsToCampaignV2', key, { method: 'POST', body: JSON.stringify({ campaignId, accountLeadPairs: pairs.slice(i, i + 100), resumePausedCampaign: true }) }); for (const k of Object.keys(totals)) totals[k] += r[k] || 0; } return totals; }