> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.talkif.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.talkif.ai/_mcp/server.

# Schedules

> Call one contact on a recurrence — once at a set time, daily, weekly, monthly or on a cron expression — with retries, an end date or run cap, and the contact's own timezone.

A schedule is a standing instruction: *call this contact, with this flow, at this time, this often*. A daily medication check-in, a weekly progress call, a reminder the day before every monthly appointment, a single call at 4 pm next Tuesday. Where a campaign is a list run once, a schedule is one person, run repeatedly.

## What a schedule is made of

| Setting                      | What it decides                                                                                                                                                                                                    |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Contact**                  | The one person this schedule calls. Their record fills the `{{contact.*}}` prompt variables on every run                                                                                                           |
| **Flow** and **from number** | As for any outbound call — a published flow; a number that can place calls                                                                                                                                         |
| **Frequency**                | `once`, `daily`, `weekly` (choose weekdays), `monthly` (day 1–28), or `custom_cron`                                                                                                                                |
| **Time**                     | Wall-clock `HH:MM`, interpreted in the **contact's** timezone — their explicit zone if set, otherwise the zone derived from their phone number. Runs happen at that local time whatever the season; DST is handled |
| **Retry on failure**         | Whether an unanswered or failed run is retried: delay between tries (1–1440 min, default 30) and how many (0–10, default 2)                                                                                        |
| **End date** / **Max runs**  | Stop after a date, or after N runs, whichever comes first                                                                                                                                                          |

Day 28 is the monthly ceiling on purpose: "the 31st" doesn't exist in most months, and a schedule that silently skips February is worse than one that won't let you set it.

## Lifecycle

**`active`** `running`

Will fire at the next occurrence.

---

**`paused`** `stopped`

Occurrences are skipped until resumed; the recurrence rule is unchanged.

---

**`completed`** `terminal`

`once` fired, or the end date / max runs was reached.

---

**`canceled`** `terminal`

Deleted by you; won't fire again.

---

A run is an ordinary outbound call: it goes through the same checks — Do Not Call, legal calling hours, balance, capacity — and the same queue. If the contact's number is added to the DNC list, the schedule's runs are refused from then on. If the account is at capacity when the run fires, the call queues with a 10-minute time-to-live; a scheduled call that can't be placed within that window is dropped for that occurrence rather than surprising the contact an hour late.

## Retries

With retries on, a run whose call ends `no_answer`, `busy`, `failed` or `canceled` is tried again after the retry delay, up to the maximum. An *answered* call is a successful run — even a short one — and isn't retried. Retries belong to the run, not the schedule: a daily schedule with 2 retries can place up to 3 calls in a day, then waits for tomorrow.

## Setting one up

#### Dashboard

**Schedules → New Schedule.** Pick the contact, flow and from-number; choose the frequency and the time (shown in the contact's timezone); optionally turn on retries and set an end date or run cap.

#### API

### Request

POST [https://api.talkif.ai/api/v1/schedules](https://api.talkif.ai/api/v1/schedules)

```curl
curl -X POST https://api.talkif.ai/api/v1/schedules \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "contactId": "a3bb189e-8bf9-3888-9912-ace4e6543002",
  "flowId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "frequency": "daily",
  "fromPhoneNumber": "+15551234567",
  "name": "Daily Follow-up Call",
  "providerId": "c9bf9e57-1685-4c89-bafb-ff5af830be8a",
  "scheduleTime": "09:00"
}'
```

```typescript
import { TalkifClient } from "@talkif/sdk";

async function main() {
    const client = new TalkifClient({
        token: "YOUR_TOKEN_HERE",
    });
    await client.schedules.createSchedule({
        contactId: "a3bb189e-8bf9-3888-9912-ace4e6543002",
        flowId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        frequency: "daily",
        fromPhoneNumber: "+15551234567",
        name: "Daily Follow-up Call",
        providerId: "c9bf9e57-1685-4c89-bafb-ff5af830be8a",
        scheduleTime: "09:00",
    });
}
main();

```

```python
import requests

url = "https://api.talkif.ai/api/v1/schedules"

payload = {
    "contactId": "a3bb189e-8bf9-3888-9912-ace4e6543002",
    "flowId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "frequency": "daily",
    "fromPhoneNumber": "+15551234567",
    "name": "Daily Follow-up Call",
    "providerId": "c9bf9e57-1685-4c89-bafb-ff5af830be8a",
    "scheduleTime": "09:00"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.talkif.ai/api/v1/schedules"

	payload := strings.NewReader("{\n  \"contactId\": \"a3bb189e-8bf9-3888-9912-ace4e6543002\",\n  \"flowId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\",\n  \"frequency\": \"daily\",\n  \"fromPhoneNumber\": \"+15551234567\",\n  \"name\": \"Daily Follow-up Call\",\n  \"providerId\": \"c9bf9e57-1685-4c89-bafb-ff5af830be8a\",\n  \"scheduleTime\": \"09:00\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.talkif.ai/api/v1/schedules")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"contactId\": \"a3bb189e-8bf9-3888-9912-ace4e6543002\",\n  \"flowId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\",\n  \"frequency\": \"daily\",\n  \"fromPhoneNumber\": \"+15551234567\",\n  \"name\": \"Daily Follow-up Call\",\n  \"providerId\": \"c9bf9e57-1685-4c89-bafb-ff5af830be8a\",\n  \"scheduleTime\": \"09:00\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.talkif.ai/api/v1/schedules")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"contactId\": \"a3bb189e-8bf9-3888-9912-ace4e6543002\",\n  \"flowId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\",\n  \"frequency\": \"daily\",\n  \"fromPhoneNumber\": \"+15551234567\",\n  \"name\": \"Daily Follow-up Call\",\n  \"providerId\": \"c9bf9e57-1685-4c89-bafb-ff5af830be8a\",\n  \"scheduleTime\": \"09:00\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.talkif.ai/api/v1/schedules', [
  'body' => '{
  "contactId": "a3bb189e-8bf9-3888-9912-ace4e6543002",
  "flowId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "frequency": "daily",
  "fromPhoneNumber": "+15551234567",
  "name": "Daily Follow-up Call",
  "providerId": "c9bf9e57-1685-4c89-bafb-ff5af830be8a",
  "scheduleTime": "09:00"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.talkif.ai/api/v1/schedules");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"contactId\": \"a3bb189e-8bf9-3888-9912-ace4e6543002\",\n  \"flowId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\",\n  \"frequency\": \"daily\",\n  \"fromPhoneNumber\": \"+15551234567\",\n  \"name\": \"Daily Follow-up Call\",\n  \"providerId\": \"c9bf9e57-1685-4c89-bafb-ff5af830be8a\",\n  \"scheduleTime\": \"09:00\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "contactId": "a3bb189e-8bf9-3888-9912-ace4e6543002",
  "flowId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "frequency": "daily",
  "fromPhoneNumber": "+15551234567",
  "name": "Daily Follow-up Call",
  "providerId": "c9bf9e57-1685-4c89-bafb-ff5af830be8a",
  "scheduleTime": "09:00"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.talkif.ai/api/v1/schedules")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

[Pause](/api-reference/schedules/pause-schedule) and [resume](/api-reference/schedules/resume-schedule) without losing the rule; [update](/api-reference/schedules/update-schedule) to change it; [delete](/api-reference/schedules/delete-schedule) to cancel.

> **One contact, many schedules**
>
> A contact can have several schedules — a weekly check-in and a monthly review — and each keeps its own run history. Calls placed by a schedule carry `source: scheduled` on the call record, so they're easy to separate from direct and campaign calls in history and analytics.

## Next

#### [Campaigns](/calls/campaigns)

For a list rather than one person.

#### [Contacts](/calls/contacts)

The contact record a schedule calls.