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

# Scheduling

> Control when campaign messages are delivered — once, on a recurring cadence, or fired on demand by a workflow

When you create a campaign, the schedule step asks how it should send: **one-time**, **recurring**, or **triggered**. One-time and recurring are clock-based; triggered hands timing off to a workflow. This page covers all three.

***

## Choosing How to Send

The schedule step opens with three modes:

* **One-time** — Send the campaign once. Choose immediate, scheduled, or local-time delivery.
* **Recurring** — Send on a repeating schedule. Serval automatically creates a new **run** each time the schedule fires.
* **Triggered** — A [workflow](/sections/documentation/workflows/overview) decides when the campaign fires — on an incoming webhook, a Serval event, a **schedule (cron)** you build, or any condition. A scheduling workflow can drive it on a cadence like a recurring campaign, *plus* react to events and branch on logic.

Pick **one-time** for announcements, event logistics, or any message that only needs to go out once. Pick **recurring** for periodic check-ins, pulse surveys, or any communication you want to repeat on a cadence. Pick **triggered** when *something else* decides when to send.

***

## One-Time Schedules

One-time campaigns support three delivery modes.

### Immediate

Messages are sent as soon as you run the campaign. Serval begins delivering to all recipients right away.

**Best for:** Urgent announcements, time-sensitive updates, or situations where delivery timing doesn't matter.

### Scheduled

Pick a specific date, time, and timezone. All recipients receive the message at the same moment.

**Best for:** Coordinated announcements tied to a specific moment, such as a product launch, a policy effective date, or a scheduled maintenance window.

<Note>
  If you run a scheduled campaign before the chosen time, Serval queues the messages and delivers them when the time arrives. If the time has already passed, messages are sent immediately.
</Note>

### Local Time

Pick a wall-clock time, and each recipient receives the message at that time in their own timezone.

Serval groups recipients into timezone batches and delivers each batch at the appropriate time. Between batches, the campaign status shows **Waiting for next batch**.

**Best for:** Messages that should arrive during business hours for each recipient, such as a benefits enrollment reminder or an IT survey.

***

## Recurring Schedules

Recurring campaigns repeat on a cron-based schedule. When you select **Recurring**, you configure three things: frequency, delivery mode, and whether to run immediately on activation.

### Frequency

Choose how often the campaign fires:

| Frequency            | What you configure                                                                                          |
| -------------------- | ----------------------------------------------------------------------------------------------------------- |
| **Every N minutes**  | Interval in minutes (minimum 15)                                                                            |
| **Every N hours**    | Interval in hours                                                                                           |
| **Day of the week**  | Select specific weekdays (e.g., every Monday and Wednesday) plus a time of day                              |
| **Day of the month** | Select specific dates (e.g., the 1st and 15th) plus a time of day. You can also select "last day of month." |
| **Month(s)**         | Select specific months, specific days within those months, and a time of day                                |

For day-of-week, day-of-month, and month frequencies you also pick a **time of day** and **timezone** for delivery.

### Delivery mode

For fixed-time frequencies (day of week, day of month, months), you choose how the time is interpreted:

* **Specific timezone** — Everyone receives the message at the same moment, using the timezone you selected.
* **Local time** — Each recipient receives the message at the chosen time in their own timezone, the same way [one-time local scheduling](#local-time) works.

<Note>
  Minute and hour interval schedules always deliver to everyone at the same time. The local-time option is only available for fixed-time frequencies.
</Note>

### Run immediately

When you activate a recurring campaign, you can choose whether to fire the first run right away or wait for the next regular cycle:

* **Run immediately** (default) — The first run starts as soon as you activate the series. For local-time delivery, recipients whose local time has already passed that day receive the message right away.
* **Wait for next cycle** — The series waits until the next scheduled fire time before sending the first run.

### Upcoming sends preview

The schedule step shows a preview of the next several upcoming send times so you can verify the cadence is correct before activating.

### Runs

Each time a recurring schedule fires, Serval creates a new **run** that goes through its own send lifecycle. You can view all runs from the parent campaign's **Runs** tab and drill into any individual run to see its delivery and response data.

***

## Triggered

A **triggered** campaign sends when a [workflow](/sections/documentation/workflows/overview) fires it, through the **Trigger Campaign** action — the workflow owns the timing, instead of a fixed schedule set on the campaign. Each fire sends the campaign's message to its configured recipients, with per-fire values passed in from the workflow.

What sets off that workflow is up to you: an incoming webhook from another system, a Serval event (a ticket created, access granted), a **schedule (cron)** you build in Workflows, or any condition you can express. So "triggered" isn't limited to external events — a scheduling workflow can drive it on a cadence the way a recurring campaign does, while *also* reacting to events and branching on logic that a plain schedule can't.

### How it works

A triggered campaign is a **template**: a saved message, recipient list, and delivery channel that never sends on its own. A workflow fires it by calling the **Trigger Campaign** action with the campaign's ID. Each fire spawns a new **run** (like a recurring campaign's runs), delivers to the recipients configured on the campaign **the moment it fires**, and fills any `{{Trigger.*}}` variables in the message from values the workflow passes in.

<Note>
  The trigger controls *when* the campaign sends and supplies per-fire variables. It does **not** choose recipients — those stay statically configured on the campaign (users, groups, or a CSV), exactly like a one-time or recurring campaign.
</Note>

### When to use it

| Scenario                                                                                              | Why triggered                                           |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| An external system posts a webhook ("new hire added in the HRIS") and you want to message that person | The webhook payload drives the send and personalizes it |
| A Serval event (ticket escalated, access granted) should kick off a templated message                 | A workflow listens for the event and fires the campaign |
| Send logic too complex for a cron schedule (conditional, multi-step)                                  | The workflow decides whether and when to fire           |

For a straightforward time-based send, the built-in [one-time](#one-time-schedules) or [recurring](#recurring-schedules) modes are simpler — no workflow needed. Reach for **triggered** when an external system, a Serval event, or conditional logic should decide when to send — or when you want the schedule to live in a workflow alongside other steps.

### Creating a triggered campaign

<Steps>
  <Step title="Choose Triggered in the schedule step">
    Create a campaign as usual (see [Create a Campaign](/sections/documentation/campaigns/create-campaign)). On the **Schedule** step, pick **Triggered**. The date/cadence options are replaced with a panel explaining that the campaign fires from a workflow.
  </Step>

  <Step title="Write your message with trigger variables">
    Compose the message as normal. Wherever you want a value the workflow supplies per fire, insert a [trigger variable](#trigger-variables) like `{{Trigger.incidentTitle}}`. Per-recipient `{{Recipient.*}}` variables still personalize each message alongside them.
  </Step>

  <Step title="Save the campaign">
    Save to create the campaign as a draft. Saving assigns the campaign an **ID** — the workflow needs this ID to fire it. The triggered panel shows the ID (copyable) once the campaign is saved.
  </Step>

  <Step title="Build the triggering workflow">
    From the triggered panel, click **Create trigger in Workflows**. Serval opens the workflow builder pre-seeded to wire up the **Trigger Campaign** action for this campaign — including any trigger variables your message references. You choose how the workflow is triggered (a webhook, a Serval event, a schedule, …) when you build it.
  </Step>

  <Step title="Activate the campaign">
    Back on the campaign, click **Activate**. Only an active campaign fires — until you activate it, the workflow's calls are ignored (nothing sends). Pause it any time to stop fires without deleting it.
  </Step>
</Steps>

### Trigger variables

Trigger variables carry values from the workflow into the message. Reference one as `{{Trigger.<key>}}` in the message or subject — for example, `{{Trigger.incidentTitle}}`.

<Note>
  A trigger variable is **one value per fire** — the *same* for every recipient of that send. That's different from `{{Recipient.*}}` and `{{CSV.*}}` variables, which vary per recipient. Use trigger variables for facts about the **event** that fired the campaign (an incident title, a system name), and recipient variables to address each person.
</Note>

The two mix freely. For example, `Hi {{Recipient.FirstName}}, a new incident ({{Trigger.incidentTitle}}) was opened — please take a look.` renders each recipient's own first name next to the one incident from this fire. (Note that `{{Trigger.*}}` does **not** address the recipient — the campaign sends to its statically configured recipients, and the trigger value is shared across all of them.)

The set of `{{Trigger.*}}` keys your message references is the campaign's **fire-time contract**: every referenced key must be supplied on each fire, or that fire is rejected and nothing sends. (An empty value is allowed; a *missing* key is not.) The workflow you build from **Create trigger in Workflows** is pre-seeded to map each key, so it satisfies the contract out of the box.

<Note>
  Trigger variables only resolve for a triggered campaign. If you reference `{{Trigger.*}}` in a one-time or recurring campaign, Serval flags it and blocks activation — those campaigns have no trigger to fill the value.
</Note>

You can preview how the message renders, and use **Send preview** to send yourself a test with sample values for each trigger variable, before activating.

### Wiring the workflow

The **Trigger Campaign** action is what fires the campaign from a workflow. **Create trigger in Workflows** sets it up for you, but it helps to understand what it does:

| Input                     | Purpose                                                                                                                      |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **Campaign**              | The campaign to fire, by ID.                                                                                                 |
| **Trigger variables**     | The values that fill `{{Trigger.*}}` in the message, read from the trigger payload. Supply every key the message references. |
| **Event ID** *(optional)* | A stable de-duplication key — see below.                                                                                     |

Serval de-duplicates fires by event ID so the same event never sends twice:

* **Provide an event ID** (e.g. the webhook's delivery ID) and re-deliveries of that same event are ignored — the campaign fires exactly once for that event, even if the workflow runs again later.
* **Omit it** and the event ID defaults to the workflow run's ID, so an automatic retry of a run doesn't double-send — one run, one send. A new run sends again.

<Warning>
  Don't hardcode a constant event ID. Serval treats it as "this one event," so the campaign would fire once and then quietly ignore every later call. Use the trigger's natural delivery ID, or omit it to get one-send-per-run.
</Warning>

### Activating, pausing, and runs

Triggered campaigns use the same lifecycle as recurring campaigns: **Activate** to arm the campaign so workflow fires send, **Pause** to stop sending on new fires (a run already in progress finishes), and **Resume** to re-arm it (fires that arrived while paused are not replayed). Each fire creates a **run** on the campaign's **Runs** tab with its own delivery and response data.

### Good to know

* **Recipients are static.** The trigger fires the send and supplies per-fire variables — not the audience. Set recipients on the campaign itself.
* **Activate it first.** A draft or paused campaign ignores fires (nothing sends, and the workflow doesn't error).
* **Same team only.** A workflow can only fire campaigns in its own team.

***

## Choosing a Schedule

| Scenario                                             | Recommended mode                             |
| ---------------------------------------------------- | -------------------------------------------- |
| "Send this right now"                                | One-time — Immediate                         |
| "Everyone should see this at 2:00 PM ET on March 15" | One-time — Scheduled                         |
| "Everyone should see this at 9:00 AM their time"     | One-time — Local time                        |
| Urgent security notice or outage alert               | One-time — Immediate                         |
| Weekly IT satisfaction pulse every Monday            | Recurring — Day of week, local time          |
| Monthly compliance check on the 1st                  | Recurring — Day of month                     |
| Quarterly tool check-in                              | Recurring — Month(s), select Jan/Apr/Jul/Oct |
| "When our HRIS posts a new hire, welcome them"       | Triggered — fired by a workflow              |
| "When a ticket is escalated, message the requester"  | Triggered — fired by a workflow              |

***

## Related

<CardGroup cols={2}>
  <Card title="Create a Campaign" icon="plus" href="/sections/documentation/campaigns/create-campaign">
    Walk through the campaign wizard step by step
  </Card>

  <Card title="Manage Campaigns" icon="sliders" href="/sections/documentation/campaigns/manage-campaigns">
    Run, activate, stop, and manage campaign lifecycle
  </Card>

  <Card title="Workflows" icon="diagram-project" href="/sections/documentation/workflows/overview">
    Build the workflow that fires a triggered campaign
  </Card>

  <Card title="Analytics" icon="chart-line" href="/sections/documentation/campaigns/analytics">
    Track delivery progress and response data
  </Card>
</CardGroup>
