SIVO
Schedules and holidays

Configuration

Schedules and holidays

How to configure business hours, holidays and time-based rules in SIVO. From the basic case to multi-timezone with exceptions.

Updated:
configurationschedulesholidays

Schedules in SIVO control what happens with calls depending on date and time. They answer questions your end customer always asks:

  • “Is your call center open right now?”
  • “If I call on December 25th, what happens?”
  • “What if I call at 2 AM?”

SIVO manages them at two levels: at the DID level (quick gate) and at the IVR level (granular).

Key concepts

Schedule

A schedule is an object that defines:

  • Name (e.g. Madrid Office, Support 24×7, Sales M-F).
  • Timezone (IANA format: Europe/Madrid, America/New_York).
  • Hours per day of week (open and close).
  • Holidays (specific dates treated as closed).
  • One-off exceptions (extra closures or special openings).

It’s independent of any call — create once and reuse.

DID-level schedule

Each DID (inbound number) can have:

  • schedule_id — which schedule to evaluate.
  • out_of_hours_ivr_flow_id — which IVR to run when out of hours.

It’s the quick gate: call comes in, SIVO checks the clock, decides normal IVR or alternative IVR. Without going through the main flow.

IVR-level schedule

Inside any IVR, the condition node with type schedule allows branching at any point:

[start] → [play welcome] → [condition: business hours?]
                                ├─ yes → [main menu]
                                └─ no  → [hangup with "we're closed" audio]

It’s granular: you can have multiple schedule checks in the same flow (e.g. “first check office hours, then inside the sales menu check sales-specific reduced hours”).

Create your first schedule

Step by step

  1. Settings → Schedules → + New schedule.
  2. Fill in:
    • Name: Main office.
    • Timezone: start typing Europe/Madrid (autocomplete).
    • Hours per day in JSON format or via the calendar UI.
    • Holidays as array of dates (YYYY-MM-DD).
  3. Save.

Hours format

JSON with key per day (mon, tue, wed, thu, fri, sat, sun):

{
  "mon": { "open": "09:00", "close": "18:00" },
  "tue": { "open": "09:00", "close": "18:00" },
  "wed": { "open": "09:00", "close": "18:00" },
  "thu": { "open": "09:00", "close": "18:00" },
  "fri": { "open": "09:00", "close": "15:00" },
  "sat": null,
  "sun": null
}
  • null or missing day = closed all day.

  • To split the day (e.g. 09-14 and 16-20), use array:

    "mon": [
      { "open": "09:00", "close": "14:00" },
      { "open": "16:00", "close": "20:00" }
    ]
  • For continuous 24h: { "open": "00:00", "close": "23:59" }.

Holidays

Array of dates in YYYY-MM-DD format:

[
  "2026-01-01",
  "2026-01-06",
  "2026-04-03",
  "2026-04-04",
  "2026-05-01",
  "2026-12-25",
  "2026-12-26"
]

If the date falls on a holiday, the schedule treats it as closed all day, ignoring day-of-week.

One-off exceptions

For special cases (closing on a Friday for a long weekend, opening on a Saturday for a campaign):

"exceptions": [
  { "date": "2026-04-30", "closed": true,  "reason": "May Day bridge" },
  { "date": "2026-11-29", "open": "09:00", "close": "21:00", "reason": "Black Friday" }
]

Exceptions override what the normal schedule would say for that day.

Assign a schedule to a DID

  1. Settings → DIDs → your number → Schedule.
  2. Select the schedule you created.
  3. Out-of-hours IVR: pick the IVR to run if out of hours.
  4. Save.

From that moment, each inbound call:

  • If in hours → runs the normal IVR.
  • If out → runs the alternative IVR (typically with “we’re closed, come back Monday at 9”).

Typical pattern: “closed” IVR

Create a simple IVR for out-of-hours:

[start] → [play "Thanks for calling. We're open Monday to Friday, 9 to 18.
            If your matter is urgent, leave a message at the end"]
        → [voicemail mailbox="[email protected]"]
        → [hangup]

Assign it as out_of_hours_ivr_flow_id on all your DIDs and callers know what to expect.

Granular schedule inside the IVR

When you need more sophisticated logic than “open or not”, use the condition.schedule node inside the IVR:

[start]

[menu "Press 1 sales, 2 support, 3 billing"]
   ├─ 1 → [condition.schedule "Sales M-F 9-18"]
   │         ├─ true  → [queue sales]
   │         └─ false → [play "Sales is M-F 9 to 18"] → [hangup]
   ├─ 2 → [queue support]   (support is 24×7)
   └─ 3 → [condition.schedule "Billing M-Th 10-14"]
             ├─ true  → [queue billing]
             └─ false → [voicemail mailbox="billing"]

You can have multiple different schedules per department. Useful when sales, support and billing have different hours.

Multi-timezone

For companies with international presence, create a schedule per region:

ScheduleZoneHours
Madrid OfficeEurope/MadridM-F 09-18
NYC OfficeAmerica/New_YorkM-F 09-18
Singapore OfficeAsia/SingaporeM-F 09-18

And assign them to their corresponding DIDs. SIVO evaluates each one in its local timezone, not UTC. When it’s 14:00 in Madrid, it’s 08:00 in NYC.

Global now variable

Inside the IVR you can access {{now}} (current UTC timestamp). Useful for more complex conditions that don’t fit in a schedule:

[condition: {{now}} > 2026-01-01 AND {{now}} < 2026-01-08]
   ↓ true  → [play "Three Kings campaign: gift if you order today"]

Special cases

DST changes (summer/winter time)

SIVO uses IANA timezones, which carry DST changes internally. When Spain switches from UTC+1 to UTC+2 in March, the schedule still says “09:00 to 18:00” in local time and SIVO handles the adjustment.

You don’t have to do anything. Neither in March nor October.

Schedules that cross midnight

Some businesses (bars, night support) open at 20:00 and close at 03:00 the next day. Configure like this:

"fri": { "open": "20:00", "close": "23:59" },
"sat": [
  { "open": "00:00", "close": "03:00" },
  { "open": "20:00", "close": "23:59" }
]

The schedule treats each day as an independent bucket, so split the hours into two slots.

Extraordinary temporary closure

If you need to close suddenly (incident, training, event), you have two options:

Option A — One-off exception: edit the schedule and add an exception for today with closed: true.

Option B — Temporary DID override: from the DID detail, mark “Override schedule” and pick another IVR. Return to normal when the event ends. Don’t touch the schedule.

Option B is faster and doesn’t affect other DIDs sharing the schedule.

Test the schedule with the simulator

Before going live, always test with the IVR simulator:

  1. Open the IVR designer.
  2. Click Simulate.
  3. In the simulator panel there’s a Clock override — you can force the time to any moment.
  4. Test: Monday 09:00, Friday 16:00 (reduced hours), Saturday, December 25th…
  5. The simulator highlights which node executes and which schedule branch is taken.

Without this, schedule bugs only show in production when a customer calls Sunday and hears the sales message.

Schedule API

For programmatic management:

GET    /api/schedules
POST   /api/schedules
GET    /api/schedules/{id}
PUT    /api/schedules/{id}
DELETE /api/schedules/{id}

Useful for syncing from your internal HR system (which already has official holidays and hours per office). E.g., a weekly cron updating the year’s holidays from your DB.

Reports

In the supervisor wallboard you’ll see an indicator if any queue is closed per its assigned schedule. Useful so the supervisor doesn’t wonder why an agent is available but no calls come in — it’s because DIDs are out-of-hours.

Best practices

  1. One schedule per department or office, not one per DID. Reuse.
  2. Name schedules descriptively: Support M-F 9-18 better than Schedule 1.
  3. Keep holidays updated at year start — schedule a recurring task each January.
  4. Always test with the simulator before publishing.
  5. Out-of-hours IVR with voicemail — don’t let the caller hang up without a leave-message option. You lose sales.
  6. Multi-timezone: name schedules with the zone (Madrid Office vs NYC Office) to avoid confusion.
  7. One-off exceptions better than editing the schedule: if you close only April 30th for a bridge, use an exception. Don’t change the general fri.close.
  8. Audit: any schedule change goes to audit_logs. Review who changed what if calls start dropping due to unexpected schedule.