Save departments, hours and holidays
Creates or updates the rooftop's departments, hours and holidays, and pushes them to the live agent.
The request is passed through as-is to the department settings save, using your token. That save does the following, in order:
- Resolves copy markers.
isSameAsSales/isSameAsServiceon built-in departments andsameAson custom departments copy another department's hours.address.sameAscopies another department's address.sameAsnames that are not departments, such as"rooftop address", are stored as-is. - If
holidaysis sent, pushes the holidays to the live agent. Holidays no longer in the list are disabled. A holiday withdepartments: "all"applies to every department, including departments added later. - Pushes department contacts to the call-transfer settings and the department contact registry. Custom departments left out of a sent
departmentslist are deleted, and their contacts are deactivated. - Pushes department hours to the agent's availability settings.
- If sales hours are included, it syncs the rooftop working days. This uses
timezone, or the rooftop's stored timezone. A failure in this sync is logged only. - Removes availability hours for deleted custom departments.
- Saves
departments,holidaysand the timezone used on the rooftop configuration. Other rooftop settings are kept.
Leaving out departments leaves departments as they are. Sending an array, even an empty one, replaces the custom departments. Leaving out holidays leaves holidays as they are. Sending an array replaces the holiday set. Built-in departments are never deleted.
The save does not update any checklist task.
Before you call it
-
You need
enterpriseIdandteamId. -
Read the current state with Get departments, hours and holidays. Send back full lists so nothing is deleted by mistake.
-
If you send day-by-day sales hours, either send
timezoneor make sure the rooftop has a timezone saved by Save rooftop details. Otherwise the call returns 400. -
Authentication: Bearer token of a signed-in console user. It is checked twice: here, and again by the department settings save, which also needs a user ID in the token.
-
Rate limit: Not documented
-
Processing: Synchronous. Waits for every push (30 second limit on the pass-through). Pushes run in sequence and are not rolled back: if a later step fails, the earlier pushes stay applied and the stored configuration is not updated.
Request
- Start from the GET response.
- Send
departmentsonly if you want to change departments, and send the complete list. - Send
holidaysonly if you want to change holidays, and send the complete list. - Use
isSameAsSales/isSameAsServiceon built-in departments andsameAson custom ones. - The save does not mark the checklist task. See the Rooftop Setup introduction.
| Parameter | Type | Required | Description |
|---|---|---|---|
| Headers | — | — | Sent as HTTP headers. |
Authorization | string | Required | Bearer YOUR_TOKEN |
Content-Type | string | Required | application/json |
| Body | — | — | Sent as a JSON body. |
enterpriseId | string | Required | Dealer group ID. |
teamId | string | Required | Rooftop ID. |
enterpriseName | string | Optional | Stored on the configuration record. |
teamName | string | Optional | Stored on the configuration record. |
timezone | string | Optional | IANA timezone used for the sales working-days sync. |
departments | array | Optional | Complete department list. Leave it out to keep departments as they are. |
departments[].name | string | Required | sales, service, parts, finance, or a custom name. |
departments[].contact | object | Optional | name (required), phone (required), email (valid email), isIvr (default false), extension, and the six transfer_* booleans. |
departments[].timings | object | Optional | Either all seven days (monday…sunday), each {isAvailable (required), startTime, endTime, isTransferAvailable (default true), weekOverrides}, or a copy marker. |
departments[].timings.isSameAsSales | boolean | Optional | Built-in only, and not on sales. |
departments[].timings.isSameAsService | boolean | Optional | Built-in only, and not on sales or service. Use only one of the two flags. |
departments[].timings.sameAs | string | Optional | Custom departments only. The name of the department to copy. |
departments[].address | any | Optional | Address object, or {sameAs: "<department name or 'rooftop address'>"}. |
holidays | array | Optional | Complete holiday list. Leave it out to keep holidays as they are. |
holidays[].name | string | Required | Holiday name. |
holidays[].reason | string | Required | Reason. |
holidays[].date | string | Required | Date. |
holidays[].recurrence | string or null | Optional | monthly or yearly. |
holidays[].open | boolean | Required | true if open with special hours. |
holidays[].hours | array | Optional | {start, end} windows. Defaults to []. |
holidays[].departments | "all" or string[] | Required | "all", or at least one department name. |
Sample request
curl -X POST "https://api.spyne.ai/console/v1/product-onboarding/vini/departments" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enterpriseId": "<enterpriseId>",
"teamId": "<teamId>",
"timezone": "America/Chicago",
"departments": [
{
"name": "sales",
"contact": { "name": "Sales", "phone": "+15550100000", "isIvr": false },
"timings": {
"monday": { "isAvailable": true, "startTime": "09:00", "endTime": "18:00" },
"tuesday": { "isAvailable": true, "startTime": "09:00", "endTime": "18:00" },
"wednesday": { "isAvailable": true, "startTime": "09:00", "endTime": "18:00" },
"thursday": { "isAvailable": true, "startTime": "09:00", "endTime": "18:00" },
"friday": { "isAvailable": true, "startTime": "09:00", "endTime": "18:00" },
"saturday": { "isAvailable": true, "startTime": "10:00", "endTime": "16:00" },
"sunday": { "isAvailable": false }
}
},
{ "name": "service", "timings": { "isSameAsSales": true } }
],
"holidays": [
{ "name": "Thanksgiving", "reason": "Public holiday", "date": "2026-11-26", "recurrence": "yearly", "open": false, "hours": [], "departments": "all" }
]
}'import requests
day = lambda start, end: {"isAvailable": True, "startTime": start, "endTime": end}
payload = {
"enterpriseId": "<enterpriseId>",
"teamId": "<teamId>",
"timezone": "America/Chicago",
"departments": [
{
"name": "sales",
"contact": {"name": "Sales", "phone": "+15550100000", "isIvr": False},
"timings": {
"monday": day("09:00", "18:00"), "tuesday": day("09:00", "18:00"),
"wednesday": day("09:00", "18:00"), "thursday": day("09:00", "18:00"),
"friday": day("09:00", "18:00"), "saturday": day("10:00", "16:00"),
"sunday": {"isAvailable": False},
},
},
{"name": "service", "timings": {"isSameAsSales": True}},
],
"holidays": [
{"name": "Thanksgiving", "reason": "Public holiday", "date": "2026-11-26",
"recurrence": "yearly", "open": False, "hours": [], "departments": "all"}
],
}
res = requests.post(
"https://api.spyne.ai/console/v1/product-onboarding/vini/departments",
json=payload,
headers={"Authorization": "Bearer YOUR_TOKEN"},
)
print(res.status_code, res.json())const day = (startTime, endTime) => ({ isAvailable: true, startTime, endTime });
const res = await fetch("https://api.spyne.ai/console/v1/product-onboarding/vini/departments", {
method: "POST",
headers: { Authorization: "Bearer YOUR_TOKEN", "Content-Type": "application/json" },
body: JSON.stringify({
enterpriseId: "<enterpriseId>",
teamId: "<teamId>",
timezone: "America/Chicago",
departments: [
{
name: "sales",
contact: { name: "Sales", phone: "+15550100000", isIvr: false },
timings: {
monday: day("09:00", "18:00"), tuesday: day("09:00", "18:00"), wednesday: day("09:00", "18:00"),
thursday: day("09:00", "18:00"), friday: day("09:00", "18:00"), saturday: day("10:00", "16:00"),
sunday: { isAvailable: false },
},
},
{ name: "service", timings: { isSameAsSales: true } },
],
holidays: [
{ name: "Thanksgiving", reason: "Public holiday", date: "2026-11-26", recurrence: "yearly", open: false, hours: [], departments: "all" },
],
}),
});
console.log(res.status, await res.json());Sample response
{ "success": true, "message": "Departments configured successfully" }{
"success": false,
"message": "Validation error",
"errors": [{ "code": "custom", "path": ["departments", 1, "timings"], "message": "service supports only isSameAsSales" }]
}{ "success": false, "message": "timezone is required", "error": "timezone is required" }{ "message": "Invalid Token", "error": true, "code": "ERROR_AUTH_401", "details": "", "data": null }{ "status": "401", "message": "Invalid Auth key" }{ "message": "...", "error": true, "code": "ERROR_REQ_TIMEOUT_408", "details": "", "data": null }{ "success": false, "message": "Failed to create Vini departments", "error": "Error creating holidays for departments" }{ "message": "No response received from external API", "error": "timeout of 30000ms exceeded" }400 Bad Request (rule checks). Other messages with the same shape:
Circular sameAs reference for work shift timings involving department "<name>"Circular sameAs reference for address involving department "<name>"Cannot copy work shift timings for department "<name>": source department "<source>" has no work shift timings
Response fields
| Parameter | Type | Description |
|---|---|---|
success | boolean | true on 201. |
message | string | Departments configured successfully. |
Errors
| Status | Reason | When it happens |
|---|---|---|
400 | Validation error | Body fails the schema. Examples: missing enterpriseId/teamId, bad email, a holiday without departments, sameAs on a built-in department, both copy flags set, sales copying another department. |
400 | timezone is required | Sales day-by-day hours sent, with no timezone and no stored rooftop timezone. |
400 | Circular sameAs / Cannot copy work shift timings | A copy marker loops, or points at a department with no hours. |
401 | ERROR_AUTH_401 / Invalid Auth key / User ID not found in token | Token missing or invalid, or no user ID in it. |
408 | ERROR_REQ_TIMEOUT_408 | Console token check could not reach the auth service. |
500 | Failed to create Vini departments | A push failed. error names which one: holidays, contacts, working hours, or deleting working hours for removed departments. Earlier pushes stay applied. |
500 | No response received from external API / Error setting up request | The department settings save did not answer in 30 seconds, or the request could not be sent. |
Next
Re-read with Get departments, hours and holidays to confirm. The save does not complete the "Department Details & Holidays" checklist task (see the Rooftop Setup introduction). Employee Directory uses the departments saved here for its department dropdown.
- Called before: Get departments, hours and holidays.
- Called after: Mark a checklist task done (no call found in the code; to be confirmed), List the employee directory.
Updated about 1 hour ago
