Running a campaign
Floor rules
Hooks can't change these. They're checked at the moment of dialing, whatever a hook said a while ago:
- the Do Not Call list;
- calling hours in the lead's timezone;
- the audit row in
dial_attempts; - pacing;
- the recording notice.
A number on the list is kept out earlier too, when a lead goes in, whatever onLead said: the check at the dial is for a number listed since.
Pacing is a workspace setting, at most dialCps a second and dialConcurrent at once. You set those in Settings if you connected your own telephony account; on one we run for you, we set them, so ask us.
Hooks
onAnswer, on a queue target, decides what an answered call is. Return nothing and a held rep joins straight away. Return actions and they run, and a held rep goes free.call.enterFlow(flow, input?)hands the call to a flow, whose input is{ lead }by default. A flow target takes the call itself.afterCallis for side effects, such as logging to your CRM. It runs with retries when the rep files a disposition, or when wrap-up runs out, and seesoutcome,repId,disposition,notesand anycallbackAt. What it returns changes nothing: the sequence says what's next. It runs once per call, with the disposition if it's in by then.- A hook that keeps failing returns its leads, backs off, then pauses the campaign (
pausedReason: 'hook_failing').
Parked leads
A lead parked as step_failed stays parked until someone looks. hcc.campaigns.retryLead(campaign, leadId) puts it back at the step it stopped at, within a minute. For a lead parked just after a call, that means another dial.
A .call handler that keeps failing for one lead holds that lead back and parks it after five tries, with lastOutcome: 'hook_failed'. It takes three different leads failing with none getting through to count towards pausing the campaign, so a handler that's down for everyone pauses it before the list parks.
Late dispositions
A rep can file a disposition after the wrap-up, for as long as it's their last call. If the lead already left on that call as exit or finished, your .goal still runs on it, and a callback time brings it back into the sequence. A lead taken out, or on Do Not Call, stays out.
Change the steps
Steps go by position, with an optional name that follows them: .call(sales, { name: 'second_call' }). hcc deploy warns when a change moves leads who are mid-sequence, and a lead whose step no longer exists parks as step_failed, with lastOutcome: 'step_missing'.
Test a campaign
Test a sequence with a clock you move:
const t = testCampaign(renewals);
const run = await t.start(t.lead());
await run.callEnded({ outcome: 'no_answer' });
t.clock.advance('30m');
await run.tick();