Business hours
Calendars define when the company is open: weekly time slots, holidays, exceptions (special closures or openings) and manual overrides. Numbers, routes, menus and queues can follow a calendar. Scopes calendars:read / calendars:write.
Creating a calendar
curl -s -X POST https://centralino.exovoip.it/api/v1/calendars \
-H "X-API-Key: $EXOPBX_API_KEY" -H "Content-Type: application/json" \
-d '{"name":"Ufficio","timezone":"Europe/Rome","isDefault":true,
"closedDestination":{"type":"voicemail","extensionId":12},
"hours":[{"weekday":1,"start":"09:00","end":"13:00"},{"weekday":1,"start":"14:00","end":"18:00"}],
"preloadHolidays":true}'const { calendar } = await pbx.calendars.create({
name: 'Ufficio',
timezone: 'Europe/Rome',
isDefault: true,
closedDestination: { type: 'voicemail', extensionId: 12 },
hours: [
{ weekday: 1, start: '09:00', end: '13:00' },
{ weekday: 1, start: '14:00', end: '18:00' },
],
preloadHolidays: true, // festività nazionali italiane
});$calendar = $pbx->calendars->create([
'name' => 'Ufficio',
'timezone' => 'Europe/Rome',
'isDefault' => true,
'closedDestination' => ['type' => 'voicemail', 'extensionId' => 12],
'hours' => [
['weekday' => 1, 'start' => '09:00', 'end' => '13:00'],
['weekday' => 1, 'start' => '14:00', 'end' => '18:00'],
],
'preloadHolidays' => true,
])['calendar'];calendar = pbx.calendars.create({
"name": "Ufficio",
"timezone": "Europe/Rome",
"isDefault": True,
"closedDestination": {"type": "voicemail", "extensionId": 12},
"hours": [
{"weekday": 1, "start": "09:00", "end": "13:00"},
{"weekday": 1, "start": "14:00", "end": "18:00"},
],
"preloadHolidays": True,
})["calendar"]hours: time slots per weekday (weekdayfrom 1 = Monday to 7 = Sunday, times asHH:MM,24:00for end of day). Multiple slots on the same day are allowed.PUT /v1/calendars/:id/hoursreplaces all slots.openDestination/closedDestination: where calls go during and outside business hours (used by thecalendardestination).closedPromptIdplays a prompt before the closed destination.isDefault: the tenant's default calendar, used by feature codes*90–*93.preloadHolidays: loads national holidays;POST /v1/calendars/:id/holidayscan also add the local patron saint's day for the municipality (ISTAT code).
Forcing open or closed
An override takes precedence over business hours and exceptions. Use it for sudden closures (stocktaking, emergencies) or special openings.
# Chiuso per 2 ore
curl -s -X POST https://centralino.exovoip.it/api/v1/calendars/4/force \
-H "X-API-Key: $EXOPBX_API_KEY" -H "Content-Type: application/json" \
-d '{"state":"closed","mode":"hours","hours":2,"note":"Inventario"}'
# Stato attuale
curl -s https://centralino.exovoip.it/api/v1/calendars/4/status -H "X-API-Key: $EXOPBX_API_KEY"
# Ritorno agli orari normali
curl -s -X DELETE https://centralino.exovoip.it/api/v1/calendars/4/force -H "X-API-Key: $EXOPBX_API_KEY"await pbx.calendars.force(4, { state: 'closed', mode: 'hours', hours: 2, note: 'Inventario' });
const status = await pbx.calendars.status(4); // { state, reason, nextTransitionAt, force, ... }
await pbx.calendars.clearForce(4);$pbx->calendars->force(4, ['state' => 'closed', 'mode' => 'hours', 'hours' => 2, 'note' => 'Inventario']);
$status = $pbx->calendars->status(4);
$pbx->calendars->clearForce(4);pbx.calendars.force(4, {"state": "closed", "mode": "hours", "hours": 2, "note": "Inventario"})
status = pbx.calendars.status(4)
pbx.calendars.clear_force(4)Expiry modes (mode):
mode | Duration |
|---|---|
next_transition | Until the next change scheduled by the business hours. For a forced closure: until the next scheduled opening (e.g. from Friday evening to Monday morning). Default. |
hours | For hours hours (1–720) |
until | Until the date and time in until |
manual | Until removed with DELETE /v1/calendars/:id/force |
# Aperto fino alla prossima variazione prevista dagli orari
curl -s -X POST https://centralino.exovoip.it/api/v1/calendars/4/force -H "X-API-Key: $EXOPBX_API_KEY" \
-H "Content-Type: application/json" -d '{"state":"open","mode":"next_transition"}'
# Chiuso fino a una data e ora precise
curl -s -X POST https://centralino.exovoip.it/api/v1/calendars/4/force -H "X-API-Key: $EXOPBX_API_KEY" \
-H "Content-Type: application/json" -d '{"state":"closed","mode":"until","until":"2026-12-27T08:00:00.000Z"}'
# Chiuso finché non viene rimossa la forzatura
curl -s -X POST https://centralino.exovoip.it/api/v1/calendars/4/force -H "X-API-Key: $EXOPBX_API_KEY" \
-H "Content-Type: application/json" -d '{"state":"closed","mode":"manual"}'await pbx.calendars.force(4, { state: 'open', mode: 'next_transition' });
await pbx.calendars.force(4, { state: 'closed', mode: 'until', until: '2026-12-27T08:00:00.000Z' });
await pbx.calendars.force(4, { state: 'closed', mode: 'manual' });$pbx->calendars->force(4, ['state' => 'open', 'mode' => 'next_transition']);
$pbx->calendars->force(4, ['state' => 'closed', 'mode' => 'until', 'until' => '2026-12-27T08:00:00.000Z']);
$pbx->calendars->force(4, ['state' => 'closed', 'mode' => 'manual']);pbx.calendars.force(4, {"state": "open", "mode": "next_transition"})
pbx.calendars.force(4, {"state": "closed", "mode": "until", "until": "2026-12-27T08:00:00.000Z"})
pbx.calendars.force(4, {"state": "closed", "mode": "manual"})GET /v1/calendars/:id/status returns the current status (open / closed), the reason (force, exception, weekly, no_hours), the next change and any active override. The override history (from the control panel, API or phone) is at GET /v1/calendars/:id/overrides.
Each override generates the calendar.override real-time event (also delivered to webhooks) and, if configured, a notification.
From the phone: enabled extensions use *90 (closed), *91 (open), *92 (restore business hours) and *93 (status), with an optional PIN. See Extensions. Each extension can also follow a company calendar and switch itself off or on with *94 / *95: see Extension hours and status.
Exceptions and simulation
Exceptions cover periods with different hours: vacations, long weekends, special openings. The simulator returns the calendar status at any given moment, with details for that day.
curl -s -X POST https://centralino.exovoip.it/api/v1/calendars/4/exceptions \
-H "X-API-Key: $EXOPBX_API_KEY" -H "Content-Type: application/json" \
-d '{"name":"Chiusura estiva","type":"closed","dateFrom":"2026-08-10","dateTo":"2026-08-21"}'
curl -s -X POST https://centralino.exovoip.it/api/v1/calendars/4/simulate \
-H "X-API-Key: $EXOPBX_API_KEY" -H "Content-Type: application/json" -d '{"at":"2026-08-12T10:00:00+02:00"}'await pbx.calendars.createException(4, {
name: 'Chiusura estiva',
type: 'closed',
dateFrom: '2026-08-10',
dateTo: '2026-08-21',
});
const sim = await pbx.calendars.simulate(4, { at: '2026-08-12T10:00:00+02:00' });$pbx->calendars->createException(4, ['name' => 'Chiusura estiva', 'type' => 'closed', 'dateFrom' => '2026-08-10', 'dateTo' => '2026-08-21']);
$sim = $pbx->calendars->simulate(4, ['at' => '2026-08-12T10:00:00+02:00']);pbx.calendars.create_exception(4, {"name": "Chiusura estiva", "type": "closed", "dateFrom": "2026-08-10", "dateTo": "2026-08-21"})
sim = pbx.calendars.simulate(4, {"at": "2026-08-12T10:00:00+02:00"})iCal (.ics) import and export: POST /v1/calendars/:id/ical/import (text or URL), GET /v1/calendars/:id/ical/export.ics; with icalUrl set, synchronization runs periodically (POST /v1/calendars/:id/ical/sync to force it).
Linking a number
curl -s -X PUT https://centralino.exovoip.it/api/v1/numbers/7/calendar \
-H "X-API-Key: $EXOPBX_API_KEY" -H "Content-Type: application/json" \
-d '{"calendarId":4,"closedDestination":{"type":"voicemail","extensionId":12}}'await pbx.numbers.setCalendar(7, {
calendarId: 4,
closedDestination: { type: 'voicemail', extensionId: 12 },
});$pbx->numbers->setCalendar(7, ['calendarId' => 4, 'closedDestination' => ['type' => 'voicemail', 'extensionId' => 12]]);pbx.numbers.set_calendar(7, {"calendarId": 4, "closedDestination": {"type": "voicemail", "extensionId": 12}})Endpoints
| Request | Scope | SDK method |
|---|---|---|
GET /v1/calendars | calendars:read | calendars.list() |
POST /v1/calendars | calendars:write | calendars.create() |
GET /v1/calendars/holidays | calendars:read | calendars.holidays() |
GET /v1/calendars/:id | calendars:read | calendars.get() |
PATCH /v1/calendars/:id | calendars:write | calendars.update() |
DELETE /v1/calendars/:id | calendars:write | calendars.delete() |
PUT /v1/calendars/:id/hours | calendars:write | calendars.setHours() · py: calendars.set_hours() |
GET /v1/calendars/:id/status | calendars:read | calendars.status() |
POST /v1/calendars/:id/force | calendars:write | calendars.force() |
DELETE /v1/calendars/:id/force | calendars:write | calendars.clearForce() · py: calendars.clear_force() |
GET /v1/calendars/:id/overrides | calendars:read | calendars.overrides() |
POST /v1/calendars/:id/simulate | calendars:read | calendars.simulate() |
POST /v1/calendars/:id/holidays | calendars:write | calendars.preloadHolidays() · py: calendars.preload_holidays() |
GET /v1/calendars/:id/exceptions | calendars:read | calendars.listExceptions() · py: calendars.list_exceptions() |
POST /v1/calendars/:id/exceptions | calendars:write | calendars.createException() · py: calendars.create_exception() |
PATCH /v1/calendars/:id/exceptions/:exceptionId | calendars:write | calendars.updateException() · py: calendars.update_exception() |
DELETE /v1/calendars/:id/exceptions/:exceptionId | calendars:write | calendars.deleteException() · py: calendars.delete_exception() |
POST /v1/calendars/:id/ical/import | calendars:write | calendars.importIcal() · py: calendars.import_ical() |
GET /v1/calendars/:id/ical/export.ics | calendars:read | calendars.exportIcal() · py: calendars.export_ical() |
POST /v1/calendars/:id/ical/sync | calendars:write | calendars.syncIcal() · py: calendars.sync_ical() |
