> ## Documentation Index
> Fetch the complete documentation index at: https://docs.truedy.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Scheduling & Delivery Logic

> How Truedy turns a campaign schedule into individual scheduled calls

# Scheduling & Delivery Logic

This guide explains what actually happens when you schedule a batch campaign.

At a high level:

* You define **when** you want calls to happen (timezone, daily window, working days).
* Truedy converts your contacts into **individual scheduled call slots** (`scheduled_at`).
* A background job then fires calls when those slots become due.

## Who this is for

* Teams creating outbound campaigns (sales, renewals, support outreach).
* Developers integrating scheduling into their systems and needing predictable behavior.

## Concepts: campaign vs campaign contacts

### Campaign (batch call)

The **campaign** is the configuration:

* Which agent to use
* Which contacts to call
* Phone rotation strategy (if multiple outbound numbers)
* Scheduling settings (timezone + time window + caps)
* Current campaign status (`draft`, `scheduled`, `active`, `paused`, etc.)

### Campaign contacts

Once scheduled, Truedy creates one row per contact, often called **campaign contacts**.

Each campaign contact receives its own:

* `status` (pending/scheduled/completed/failed/etc.)
* `scheduled_at` timestamp (the time Truedy will attempt to place the call)

All analytics are built from these campaign contact outcomes.

## Input parameters (what you control)

From the Public API `POST /batch-calls`, you can set:

* `schedule_type`
  * `immediate` (default): campaign may move forward right away
  * `scheduled`: you provide a start datetime and daily rules
* `scheduled_at` (required when `schedule_type = scheduled`)
  * ISO datetime indicating when the schedule window begins (used with `timezone`)
* `timezone` (default `UTC`)
  * IANA timezone (e.g. `America/New_York`)
* `start_time` and `end_time`
  * Daily window start/end (e.g. `09:00` to `18:00`)
* `working_days`
  * Allowed days of week (1=Mon ... 7=Sun)
* `max_calls_per_day`
  * Daily cap enforced by the scheduler
* `max_concurrent_calls`
  * Upper bound on how many calls can be in-flight at once

For full field definitions, see:

* [Create Batch Call](/api-reference/batch-calls#create-batch-call)

## Step-by-step: from draft to scheduled

You can schedule a campaign in two common ways:

### Option A: Provide scheduling on creation

If you create with `schedule_type: "scheduled"` and include `scheduled_at`, the campaign can be considered scheduled per the request.

### Option B: Create draft, then call the schedule endpoint

If you create the campaign as `draft`, you can explicitly schedule it later:

1. Create the batch call
2. Add contacts to the draft
3. Call `POST /batch-calls/{batch_call_id}/schedule`

This maps to the API docs:

* [Schedule Batch Call](/api-reference/batch-calls#schedule-batch-call)

## What the scheduler does (the delivery algorithm)

When you schedule a campaign, the scheduling engine:

1. Validates prerequisites:
   * The batch has an agent and outbound phone numbers
   * There is at least one pending contact
2. Prepares call payloads:
   * Applies phone rotation strategy (e.g. sequential/random/round robin)
3. Distributes contacts across days:
   * Uses `timezone`, `working_days`, and daily `start_time`/`end_time`
   * Enforces `max_calls_per_day`
4. Assigns each contact a unique `scheduled_at` timestamp:
   * These are the times each call becomes due
5. Enqueues delivery:
   * A background job later fires due contacts

This behavior is described directly in:

* [Schedule Batch Call](/api-reference/batch-calls#schedule-batch-call)

## Concurrency: what `max_concurrent_calls` really affects

`max_concurrent_calls` does not change the `scheduled_at` planning step.

Instead, it affects how many scheduled calls the background job can launch at the same time when multiple contacts become due.

Practical guidance:

* If calls are failing due to provider saturation, reduce `max_concurrent_calls`.
* If your campaign is too slow, increase gradually (and ensure your integration is not rate-limited).

## Status gating: when delivery is allowed

The scheduler only fires for campaigns in the correct state.

In practice:

* `active` and `scheduled` campaigns can deliver calls.
* `paused` campaigns should not deliver new calls (future scheduled contacts are held).

Pause/resume behavior is documented in:

* [Pause Batch Call](/api-reference/batch-calls#pause-batch-call)
* [Resume Batch Call](/api-reference/batch-calls#resume-batch-call)

## Updating schedule settings (safe rescheduling)

If you PATCH scheduling parameters while a campaign is in a compatible state:

* `scheduled_at`
* `start_time` / `end_time`
* `max_calls_per_day`
* `working_days`

Truedy will redistribute contacts across the new schedule.

See:

* [Update Batch Call](/api-reference/batch-calls#update-batch-call)

## Concrete cURL examples

### 1) Schedule batch call (create + schedule)

Create a scheduled campaign draft:

```bash theme={null}
curl -X POST "https://api.truedy.ai/api/public/v1/batch-calls" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: 33333333-3333-3333-3333-333333333333" \
  -d '{
    "name": "Delivery Logic Example",
    "agent_id": "1f4baf16-1540-4d91-9696-7f34b2f2cb77",
    "contact_source": "folder",
    "contact_folder_id": "6f4af8be-0a96-49b0-ac2a-a359f46cc966",
    "schedule_type": "scheduled",
    "scheduled_at": "2026-03-20T14:30:00Z",
    "timezone": "America/New_York",
    "start_time": "10:00",
    "end_time": "17:00",
    "working_days": [1,2,3,4,5],
    "max_concurrent_calls": 8,
    "max_calls_per_day": 120
  }'
```

### 2) Verify delivery planning (inspect scheduled contacts)

```bash theme={null}
curl "https://api.truedy.ai/api/public/v1/batch-calls/BATCH_CALL_ID/contacts?status=scheduled&limit=50&offset=0" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### 3) Adjust schedule later (redistribute future contacts)

```bash theme={null}
curl -X PATCH "https://api.truedy.ai/api/public/v1/batch-calls/BATCH_CALL_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "start_time": "09:30",
    "end_time": "16:30",
    "max_calls_per_day": 80
  }'
```

## Troubleshooting

### “Nothing is being delivered”

Common checks:

* Is the campaign in `scheduled` or `active` state (not `paused`)?
* Does today fall inside `working_days`?
* Is the current local time (per `timezone`) inside `start_time`/`end_time`?
* Did scheduling validate successfully (agent synced, outbound numbers assigned, contacts exist)?

### “Some contacts never change status”

Use the batch contacts endpoint:

* List contacts filtered by `status=scheduled` to confirm they were planned.
* Then filter by `status=failed` to identify provider/conversation failures.

## Next steps

* If you need runtime control: [Pause/Resume & Edit Campaigns](/guides/pause-resume-edit-campaigns)
* If you need outcomes: [Campaign Analytics & Reporting](/guides/campaign-analytics-and-reporting)
