Skip to main content
A background dialer worker continuously polls for contacts that are due to be called, allocates a bounded number of concurrent call slots, and starts one call attempt per eligible contact. Each attempt picks a phone number, places the call, waits for it to finish, and then either marks the contact done or schedules a retry. This page explains how pacing, retries, and outcomes are decided. For where contacts and configurations come from, see Campaigns overview. For how finished attempts surface, see Results and analytics.

Concurrency

Two limits bound how many calls run at once.
LimitScopeDefaultMaximum
concurrency_limitPer organization1010000
Global concurrency limitAcross all organizations50
The per-org limit is read or changed through the /campaign/concurrency-settings endpoint. If an organization has never set one, it falls back to 10.
curl https://api.anyreach.ai/campaign/concurrency-settings \
  -H "Authorization: Bearer <token>" \
  -H "X-Anyreach-Org: <organization_id>"
Reading requires one of campaigns:read, campaigns:manage, concurrency:read, or concurrency:manage. Updating requires campaigns:manage or concurrency:manage. Active calls for an organization counts contacts in either the locked or in_progress status. An organization is at capacity when its active count reaches its concurrency_limit.

How slots are allocated

On each cycle the dialer computes how many global slots are free (global limit − total active calls across all orgs). If none are free, it waits for the next cycle. Free slots are then distributed proportionally to each organization’s concurrency_limit, but never more than the room that organization has left (concurrency_limit − active calls). Whole slots are handed out first by proportional floor, then any leftover slots go to the organizations with the largest fractional shares.
The per-org limit caps your simultaneous calls, but the shared global limit means the exact number running at any instant also depends on demand from other organizations.

The dialer loop

The dialer runs as a continuous worker, polling roughly once per second by default. Each cycle does the following.
1

Sweep stale locks

Contacts stuck in locked longer than the threshold are returned to pending so they can be retried.
2

Find eligible timezones

For each active campaign configuration, the dialer evaluates the calling windows and works out which timezones are currently inside their window. Contacts not attached to a campaign bypass timezone filtering and are always eligible.
3

Allocate slots

It reads per-org concurrency stats, computes free global slots, and allocates them proportionally (see above).
4

Fetch contacts

It pulls due contacts that fit the allocation and the eligible timezones, ordered by priority. A contact is due when it is pending and any pending retry time has passed.
5

Start attempts

It starts one call attempt per fetched contact, in parallel.
Contacts are ordered by campaign priority (highest first); contacts with no campaign sort above all campaign-priority contacts. Multiple dialer instances can run safely — row locking prevents the same contact from being dialed twice.

A single attempt

Each attempt selects which phone number to call based on how many attempts the contact has already had, places the call, and waits for it to complete.

Phone number selection

A contact can carry several phone numbers in order. Each number is tried up to max_attempts_per_phone_number times (default 2) before the dialer moves on to the next number in the list. When every number has used all of its attempts, the contact is exhausted.
attempts 0,1  -> phone_numbers[0]   (number 1, attempts 1 and 2)
attempts 2,3  -> phone_numbers[1]   (number 2, attempts 1 and 2)
attempts 4,5  -> phone_numbers[2]   (number 3, attempts 1 and 2)
...           -> exhausted once the list runs out

Per-call timeout

After the call is initiated, the attempt waits up to 30 minutes for a completion signal. If no signal arrives in that window, the attempt is recorded as a timeout.

Attempt outcomes

OutcomeMeaning
completedThe call finished and reported success.
failedThe call ended without reporting success (and did not time out).
timeoutNo completion signal arrived within the 30-minute window.

Retries and resolution

What happens after an attempt depends on whether it completed and whether any attempts remain.
Result of attemptNext state of contact
completedContact marked completed — no further calls.
Not completed, attempts remainRetry scheduled about an hour later (RETRY_DELAY_MINUTES = 60).
Not completed, no attempts remainContact marked exhausted.
A scheduled retry sets a retry_after time roughly one hour ahead; the dialer will not pick the contact up again until that time has passed. When retried, the attempt count advances, which is what eventually moves selection to the next phone number and then to exhaustion.
“Attempts remain” means the next attempt index still maps to a phone number in the contact’s list, given max_attempts_per_phone_number. A contact with no phone numbers at all is exhausted immediately.

Where to go next

Campaigns overview

How campaigns, contacts, and configurations fit together.

Results and analytics

Inspect attempts, dispositions, and call outcomes.