Skip to main content
Version: v2

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:

  1. The Calling API / SDK — lets you trigger a call to any number.
  2. 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 Authorization in 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

  1. Picker: A scheduled job / loop checks each active campaign, and if it's currently within calling hours and no call is in_progress for that campaign/agent, pops the next pending contact.
  2. Dial: Call the contact's number and campaign metadata (job_id = campaign ID, candidate_id = contact ID). Mark the contact dialing.
  3. Wait: Do nothing else for this agent/line until a webhook arrives — don't fire the next call in parallel on the same agent.
  4. Webhook handler: On call.summary, look up the contact via metadata.candidate_id, record the outcome (connected, no-answer, busy, failed), mark it completed or requeue it for retry.
  5. 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.
  6. Retry logic: If the outcome was no-answer/busy/failed, requeue with next_attempt_at pushed forward (e.g. +2 hours), up to max_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_attempts per 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_id and 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/.