Building a Custom Autodialer with FreJun
An autodialer is, at its core, a simple loop: take a list of contacts, call them one after another with a controlled gap in between, and log the outcome of each call. Since FreJun already exposes calling as a programmable API, you can build this loop entirely on your own backend — no separate autodialer product needed.
This guide walks through the architecture, the exact API calls involved, pacing/compliance considerations, and a working reference implementation.
1. How autodialing works on top of FreJun
At a high level, your system becomes the "dialer engine" and FreJun becomes the calling layer:
The two building blocks that make this possible are:
- The Calling API / SDK — lets you trigger a call to any number.
- Webhooks — tell your backend when a call has ended (and give you the outcome), so you know when it's safe to dial the next contact.
2. Prerequisites
Before you start:
- Authentication credentials — either:
- An OAuth 2.0 app (recommended for production integrations, supports webhooks), or
- An API Key (simpler, good for server-to-server/internal tools, if using API only for making calls).
- See
Setup and Authorizationin the FreJun docs for how to create these.
- A virtual number provisioned in your FreJun account.
- A publicly reachable HTTPS endpoint on your backend to receive webhooks.
3. Core API / SDK: triggering a call
Please go through this documentation about how to integrate FreJun calling into your platform.
4. Knowing when a call ends: Webhooks
Your dialer engine needs to know when a call finishes before it can safely place the next one. Don't poll — subscribe to webhooks instead.
Make sure to follow the webhook documentation on how to subscribe to FreJun calling webhooks.
5. Designing the dialer engine
This is the part you build. A minimal, reliable design looks like this:
5.1 Data model
campaigns
id, name, status, pacing_seconds, calling_hours_start, calling_hours_end, max_attempts
campaign_contacts
id, campaign_id, candidate_number, candidate_name,
status (pending | dialing | completed | failed | skipped),
attempts, last_call_id, last_outcome, next_attempt_at
5.2 The loop
- Picker: A scheduled job / loop checks each active campaign, and if it's currently within calling hours and no call is
in_progressfor that campaign/agent, pops the nextpendingcontact. - Dial: Call the contact's number and campaign metadata (
job_id= campaign ID,candidate_id= contact ID). Mark the contactdialing. - Wait: Do nothing else for this agent/line until a webhook arrives — don't fire the next call in parallel on the same agent.
- Webhook handler: On
call.summary, look up the contact viametadata.candidate_id, record the outcome (connected,no-answer,busy,failed), mark itcompletedor requeue it for retry. - Pacing gap: After marking the contact done, schedule the next pick-up after your configured pacing interval (e.g. 30–60 seconds) rather than immediately — this avoids hammering the API / SDK and gives agents breathing room between calls.
- Retry logic: If the outcome was
no-answer/busy/failed, requeue withnext_attempt_atpushed forward (e.g. +2 hours), up tomax_attempts.
5.3 Concurrency
Each user_email can only be on one call at a time — that's your natural concurrency unit. If you want to dial faster, add more licensed agents to the campaign and distribute the load accordingly.
6. Pacing, calling hours & compliance
Since you're now responsible for the dialing logic (rather than a packaged autodialer product), build these guardrails in yourself:
- Respect calling-hour windows — don't dial outside the hours permitted in the region you're calling into.
- Honor DND/opt-out lists — filter your contact list against any Do-Not-Call registry or explicit opt-outs before queuing (please note that this is handled on FreJun side too).
- Cap retry attempts — set a sane
max_attemptsper contact (e.g. 3) with cooldown between attempts, rather than retrying immediately. - Add a pacing gap between calls, even when agents are free — a small delay (20–60s) avoids overwhelming agents and keeps call quality high.
- Log every attempt — store the
call_idand outcome for every dial, not just the final one, so you have a full audit trail. - Rate limits — Please check the rate limiting policy at https://frejun.com/docs/getting-started/rate-limiting/.