From 8a8a6ab64dbf442a6afcb9d8dcd2a82f737999b7 Mon Sep 17 00:00:00 2001 From: Simone Cervini Date: Thu, 6 Aug 2026 10:39:37 +0200 Subject: [PATCH] feat(calendar-client): add working hours definitions --- .changeset/calendar-client-expanded-api.md | 2 +- .../calendar-client/src/openapi-runtime.json | 49 +++ clients/calendar-client/src/openapi.d.ts | 318 ++++++++++++++- clients/calendar-client/src/openapi.json | 377 +++++++++++++++++- 4 files changed, 737 insertions(+), 9 deletions(-) diff --git a/.changeset/calendar-client-expanded-api.md b/.changeset/calendar-client-expanded-api.md index 19b731a9..d2a11503 100644 --- a/.changeset/calendar-client-expanded-api.md +++ b/.changeset/calendar-client-expanded-api.md @@ -2,4 +2,4 @@ "@epilot/calendar-client": minor --- -Update the Calendar API definitions with calendar and event mutations, Outlook calendar integration, event sharing, and absence management operations. +Update the Calendar API definitions with calendar and event mutations, Outlook calendar integration, event sharing, absence management, and working hours operations. diff --git a/clients/calendar-client/src/openapi-runtime.json b/clients/calendar-client/src/openapi-runtime.json index 70294fca..a8c33e83 100644 --- a/clients/calendar-client/src/openapi-runtime.json +++ b/clients/calendar-client/src/openapi-runtime.json @@ -28,6 +28,10 @@ "in": "query", "name": "include_busy" }, + { + "in": "query", + "name": "working_hours_granularity" + }, { "in": "query", "name": "query" @@ -186,6 +190,51 @@ { "in": "query", "name": "include_busy" + }, + { + "in": "query", + "name": "working_hours_granularity" + } + ], + "responses": {} + } + }, + "/v1/calendar/working-hours/users/{user_id}": { + "get": { + "operationId": "getWorkingHours", + "parameters": [ + { + "in": "path", + "name": "user_id", + "required": true + } + ], + "responses": {} + }, + "put": { + "operationId": "putWorkingHours", + "parameters": [ + { + "in": "path", + "name": "user_id", + "required": true + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": {} + } + }, + "responses": {} + }, + "delete": { + "operationId": "deleteWorkingHours", + "parameters": [ + { + "in": "path", + "name": "user_id", + "required": true } ], "responses": {} diff --git a/clients/calendar-client/src/openapi.d.ts b/clients/calendar-client/src/openapi.d.ts index a7d68071..9157a96e 100644 --- a/clients/calendar-client/src/openapi.d.ts +++ b/clients/calendar-client/src/openapi.d.ts @@ -62,11 +62,11 @@ declare namespace Components { absence_adjustment_id?: string; reason?: string | null; } - export type AbsenceIntervalSource = "calendar_event" | "absence_adjustment"; + export type AbsenceIntervalSource = "calendar_event" | "absence_adjustment" | "working_hours"; /** * Calendar status targeted by the absence adjustment. */ - export type AbsenceStatus = "busy" | "oof"; + export type AbsenceStatus = "oof" | "busy"; /** * Optional producer-defined reference stored with an absence adjustment. */ @@ -673,6 +673,10 @@ declare namespace Components { * Include busy inputs in addition to out-of-office absence. Defaults to false. */ include_busy?: boolean; + /** + * At what granularity working hours contribute to absence. "time" (default): all time outside a user's working windows counts as absent. "day": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals. + */ + working_hours_granularity?: "time" | "day"; } export interface SearchNowAbsenceBody { /** @@ -984,6 +988,10 @@ declare namespace Components { * Include busy inputs in addition to out-of-office absence. Defaults to false. */ include_busy?: boolean; + /** + * At what granularity working hours contribute to absence. "time" (default): all time outside a user's working windows counts as absent. "day": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals. + */ + working_hours_granularity?: "time" | "day"; } export type Sensitivity = "normal" | "personal" | "private" | "confidential"; export interface ShareEventBody { @@ -992,6 +1000,195 @@ declare namespace Components { */ user_id: string; } + /** + * A wall-clock working window within a single day. + */ + export interface TimeWindow { + /** + * 24h wall-clock time ("HH:mm") in the timezone of the working-hours record. + */ + start: string; // ^([01]\d|2[0-3]):[0-5]\d$ + /** + * 24h wall-clock time ("HH:mm") in the timezone of the working-hours record. + */ + end: string; // ^([01]\d|2[0-3]):[0-5]\d$ + } + /** + * Full replacement of the working-hours record. All weekdays are required; an empty array means a day off. Users without a working-hours record are treated as always available. + */ + export interface UpsertWorkingHoursBody { + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + monday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + tuesday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + wednesday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + thursday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + friday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + saturday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + sunday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * IANA timezone the working windows are expressed in. Defaults to Europe/Berlin. + */ + timezone?: string; + } + /** + * Recurring weekly working hours of a user. The absence of a record means the user is treated as always available. + */ + export interface WorkingHours { + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + monday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + tuesday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + wednesday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + thursday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + friday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + saturday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * Working windows for a day, sorted and non-overlapping. An empty array means a day off. + */ + sunday: [ + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow?, + /* A wall-clock working window within a single day. */ TimeWindow? + ]; + /** + * IANA timezone the working windows are expressed in. + */ + timezone: string; + user_id: string; + updated_by: string; + created_at: string; // date-time + updated_at: string; // date-time + } } } declare namespace Paths { @@ -1089,6 +1286,19 @@ declare namespace Paths { export type $502 = Components.Schemas.Error; } } + namespace DeleteWorkingHours { + namespace Parameters { + export type UserId = string; + } + export interface PathParameters { + user_id: Parameters.UserId; + } + namespace Responses { + export interface $204 { + } + export type $404 = Components.Schemas.Error; + } + } namespace GetAbsenceAdjustment { namespace Parameters { export type AdjustmentId = string; @@ -1142,6 +1352,10 @@ declare namespace Paths { */ export type To = string; // date-time export type UserId = string; + /** + * At what granularity working hours contribute to absence. "time" (default): all time outside a user's working windows counts as absent. "day": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals. + */ + export type WorkingHoursGranularity = "time" | "day"; } export interface PathParameters { user_id: Parameters.UserId; @@ -1150,6 +1364,7 @@ declare namespace Paths { from: /* Start of the time window (inclusive). Maximum window: 31 days. */ Parameters.From /* date-time */; to: /* End of the time window (exclusive). Must be after from. */ Parameters.To /* date-time */; include_busy?: /* Include busy inputs in addition to out-of-office absence. Defaults to false. */ Parameters.IncludeBusy; + working_hours_granularity?: /* At what granularity working hours contribute to absence. "time" (default): all time outside a user's working windows counts as absent. "day": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals. */ Parameters.WorkingHoursGranularity; } namespace Responses { export interface $200 { @@ -1166,6 +1381,18 @@ declare namespace Paths { export type $400 = Components.Schemas.Error; } } + namespace GetWorkingHours { + namespace Parameters { + export type UserId = string; + } + export interface PathParameters { + user_id: Parameters.UserId; + } + namespace Responses { + export type $200 = /* Recurring weekly working hours of a user. The absence of a record means the user is treated as always available. */ Components.Schemas.WorkingHours; + export type $404 = Components.Schemas.Error; + } + } namespace ListAbsenceAdjustments { namespace Parameters { /** @@ -1279,11 +1506,16 @@ declare namespace Paths { * End of the time window (exclusive). Must be after from. */ export type To = string; // date-time + /** + * At what granularity working hours contribute to absence. "time" (default): all time outside a user's working windows counts as absent. "day": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals. + */ + export type WorkingHoursGranularity = "time" | "day"; } export interface QueryParameters { from: /* Start of the time window (inclusive). Maximum window: 31 days. */ Parameters.From /* date-time */; to: /* End of the time window (exclusive). Must be after from. */ Parameters.To /* date-time */; include_busy?: /* Include busy inputs in addition to out-of-office absence. Defaults to false. */ Parameters.IncludeBusy; + working_hours_granularity?: /* At what granularity working hours contribute to absence. "time" (default): all time outside a user's working windows counts as absent. "day": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals. */ Parameters.WorkingHoursGranularity; query?: /* Optional user directory search query. */ Parameters.Query; limit?: /* Maximum users to return. */ Parameters.Limit; offset?: /* User directory offset. */ Parameters.Offset; @@ -1337,6 +1569,19 @@ declare namespace Paths { export type $404 = Components.Schemas.Error; } } + namespace PutWorkingHours { + namespace Parameters { + export type UserId = string; + } + export interface PathParameters { + user_id: Parameters.UserId; + } + export type RequestBody = /* Full replacement of the working-hours record. All weekdays are required; an empty array means a day off. Users without a working-hours record are treated as always available. */ Components.Schemas.UpsertWorkingHoursBody; + namespace Responses { + export type $200 = /* Recurring weekly working hours of a user. The absence of a record means the user is treated as always available. */ Components.Schemas.WorkingHours; + export type $400 = Components.Schemas.Error; + } + } namespace SearchAbsence { export type RequestBody = Components.Schemas.SearchAbsenceBody; namespace Responses { @@ -1529,6 +1774,36 @@ export interface OperationMethods { data?: any, config?: AxiosRequestConfig ): OperationResponse + /** + * getWorkingHours - getWorkingHours + * + * Get the recurring weekly working hours of a user. 404 means no record exists and the user is treated as always available. + */ + 'getWorkingHours'( + parameters?: Parameters | null, + data?: any, + config?: AxiosRequestConfig + ): OperationResponse + /** + * putWorkingHours - putWorkingHours + * + * Create or fully replace the working hours of a user in the caller organization. This is a full replace, not a merge. + */ + 'putWorkingHours'( + parameters?: Parameters | null, + data?: Paths.PutWorkingHours.RequestBody, + config?: AxiosRequestConfig + ): OperationResponse + /** + * deleteWorkingHours - deleteWorkingHours + * + * Delete the working hours of a user. The user is then treated as always available again. + */ + 'deleteWorkingHours'( + parameters?: Parameters | null, + data?: any, + config?: AxiosRequestConfig + ): OperationResponse /** * listCalendars - listCalendars * @@ -1600,7 +1875,7 @@ export interface OperationMethods { * Handles both the subscription-validation handshake (echoes the * `validationToken` query param as `text/plain`) and change notifications. * Each notification is trusted only after its HMAC-signed `clientState` is - * verified and matched against the stored subscription — identity is never + * verified and matched against the stored subscription – identity is never * read from the request body. */ 'outlookWebhook'( @@ -1813,6 +2088,38 @@ export interface PathsDictionary { config?: AxiosRequestConfig ): OperationResponse } + ['/v1/calendar/working-hours/users/{user_id}']: { + /** + * getWorkingHours - getWorkingHours + * + * Get the recurring weekly working hours of a user. 404 means no record exists and the user is treated as always available. + */ + 'get'( + parameters?: Parameters | null, + data?: any, + config?: AxiosRequestConfig + ): OperationResponse + /** + * putWorkingHours - putWorkingHours + * + * Create or fully replace the working hours of a user in the caller organization. This is a full replace, not a merge. + */ + 'put'( + parameters?: Parameters | null, + data?: Paths.PutWorkingHours.RequestBody, + config?: AxiosRequestConfig + ): OperationResponse + /** + * deleteWorkingHours - deleteWorkingHours + * + * Delete the working hours of a user. The user is then treated as always available again. + */ + 'delete'( + parameters?: Parameters | null, + data?: any, + config?: AxiosRequestConfig + ): OperationResponse + } ['/v1/calendar']: { /** * listCalendars - listCalendars @@ -1893,7 +2200,7 @@ export interface PathsDictionary { * Handles both the subscription-validation handshake (echoes the * `validationToken` query param as `text/plain`) and change notifications. * Each notification is trusted only after its HMAC-signed `clientState` is - * verified and matched against the stored subscription — identity is never + * verified and matched against the stored subscription – identity is never * read from the request body. */ 'post'( @@ -2046,3 +2353,6 @@ export type SearchAbsenceBody = Components.Schemas.SearchAbsenceBody; export type SearchNowAbsenceBody = Components.Schemas.SearchNowAbsenceBody; export type Sensitivity = Components.Schemas.Sensitivity; export type ShareEventBody = Components.Schemas.ShareEventBody; +export type TimeWindow = Components.Schemas.TimeWindow; +export type UpsertWorkingHoursBody = Components.Schemas.UpsertWorkingHoursBody; +export type WorkingHours = Components.Schemas.WorkingHours; diff --git a/clients/calendar-client/src/openapi.json b/clients/calendar-client/src/openapi.json index 8085b387..28208039 100644 --- a/clients/calendar-client/src/openapi.json +++ b/clients/calendar-client/src/openapi.json @@ -17,6 +17,10 @@ { "name": "Calendar Events", "description": "Calendar events" + }, + { + "name": "Working Hours", + "description": "Recurring weekly working hours of users" } ], "servers": [ @@ -74,6 +78,20 @@ "default": false } }, + { + "in": "query", + "name": "working_hours_granularity", + "description": "At what granularity working hours contribute to absence. \"time\" (default): all time outside a user's working windows counts as absent. \"day\": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals.", + "schema": { + "type": "string", + "enum": [ + "time", + "day" + ], + "description": "At what granularity working hours contribute to absence. \"time\" (default): all time outside a user's working windows counts as absent. \"day\": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals.", + "default": "time" + } + }, { "in": "query", "name": "query", @@ -701,6 +719,20 @@ "description": "Include busy inputs in addition to out-of-office absence. Defaults to false.", "default": false } + }, + { + "in": "query", + "name": "working_hours_granularity", + "description": "At what granularity working hours contribute to absence. \"time\" (default): all time outside a user's working windows counts as absent. \"day\": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals.", + "schema": { + "type": "string", + "enum": [ + "time", + "day" + ], + "description": "At what granularity working hours contribute to absence. \"time\" (default): all time outside a user's working windows counts as absent. \"day\": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals.", + "default": "time" + } } ], "responses": { @@ -763,6 +795,134 @@ } } }, + "/v1/calendar/working-hours/users/{user_id}": { + "get": { + "operationId": "getWorkingHours", + "summary": "getWorkingHours", + "description": "Get the recurring weekly working hours of a user. 404 means no record exists and the user is treated as always available.", + "tags": [ + "Working Hours" + ], + "parameters": [ + { + "in": "path", + "name": "user_id", + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true + } + ], + "responses": { + "200": { + "description": "Working hours", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkingHours" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "put": { + "operationId": "putWorkingHours", + "summary": "putWorkingHours", + "description": "Create or fully replace the working hours of a user in the caller organization. This is a full replace, not a merge.", + "tags": [ + "Working Hours" + ], + "parameters": [ + { + "in": "path", + "name": "user_id", + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpsertWorkingHoursBody" + } + } + } + }, + "responses": { + "200": { + "description": "Stored working hours", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkingHours" + } + } + } + }, + "400": { + "description": "Invalid request body", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "operationId": "deleteWorkingHours", + "summary": "deleteWorkingHours", + "description": "Delete the working hours of a user. The user is then treated as always available again.", + "tags": [ + "Working Hours" + ], + "parameters": [ + { + "in": "path", + "name": "user_id", + "schema": { + "type": "string", + "minLength": 1 + }, + "required": true + } + ], + "responses": { + "204": { + "description": "Working hours deleted" + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, "/v1/calendar": { "get": { "operationId": "listCalendars", @@ -1004,7 +1164,7 @@ "post": { "operationId": "outlookWebhook", "summary": "outlookWebhook", - "description": "Public Microsoft Graph webhook receiver for per-user Outlook calendar\nsubscriptions. Unauthenticated by design (API Gateway `Authorizer: NONE`):\nGraph calls it with no epilot token.\n\nHandles both the subscription-validation handshake (echoes the\n`validationToken` query param as `text/plain`) and change notifications.\nEach notification is trusted only after its HMAC-signed `clientState` is\nverified and matched against the stored subscription — identity is never\nread from the request body.", + "description": "Public Microsoft Graph webhook receiver for per-user Outlook calendar\nsubscriptions. Unauthenticated by design (API Gateway `Authorizer: NONE`):\nGraph calls it with no epilot token.\n\nHandles both the subscription-validation handshake (echoes the\n`validationToken` query param as `text/plain`) and change notifications.\nEach notification is trusted only after its HMAC-signed `clientState` is\nverified and matched against the stored subscription – identity is never\nread from the request body.", "tags": [ "Calendars" ], @@ -1693,7 +1853,8 @@ "type": "string", "enum": [ "calendar_event", - "absence_adjustment" + "absence_adjustment", + "working_hours" ] }, "Error": { @@ -1746,6 +1907,15 @@ "type": "boolean", "description": "Include busy inputs in addition to out-of-office absence. Defaults to false.", "default": false + }, + "working_hours_granularity": { + "type": "string", + "enum": [ + "time", + "day" + ], + "description": "At what granularity working hours contribute to absence. \"time\" (default): all time outside a user's working windows counts as absent. \"day\": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals.", + "default": "time" } }, "required": [ @@ -1771,6 +1941,15 @@ "type": "boolean", "description": "Include busy inputs in addition to out-of-office absence. Defaults to false.", "default": false + }, + "working_hours_granularity": { + "type": "string", + "enum": [ + "time", + "day" + ], + "description": "At what granularity working hours contribute to absence. \"time\" (default): all time outside a user's working windows counts as absent. \"day\": only full days with no working windows count as absent (whole-day granularity, used by thread assignment). Users without a working-hours record never produce working-hours intervals.", + "default": "time" } }, "required": [ @@ -1838,8 +2017,8 @@ "AbsenceStatus": { "type": "string", "enum": [ - "busy", - "oof" + "oof", + "busy" ], "description": "Calendar status targeted by the absence adjustment." }, @@ -1938,6 +2117,196 @@ "last_synced_at" ] }, + "WorkingHours": { + "type": "object", + "properties": { + "monday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "tuesday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "wednesday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "thursday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "friday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "saturday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "sunday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "timezone": { + "type": "string", + "description": "IANA timezone the working windows are expressed in." + }, + "user_id": { + "type": "string" + }, + "updated_by": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday", + "timezone", + "user_id", + "updated_by", + "created_at", + "updated_at" + ], + "description": "Recurring weekly working hours of a user. The absence of a record means the user is treated as always available." + }, + "UpsertWorkingHoursBody": { + "type": "object", + "properties": { + "monday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "tuesday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "wednesday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "thursday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "friday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "saturday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "sunday": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimeWindow" + }, + "maxItems": 6, + "description": "Working windows for a day, sorted and non-overlapping. An empty array means a day off." + }, + "timezone": { + "type": "string", + "description": "IANA timezone the working windows are expressed in. Defaults to Europe/Berlin.", + "default": "Europe/Berlin" + } + }, + "required": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday" + ], + "description": "Full replacement of the working-hours record. All weekdays are required; an empty array means a day off. Users without a working-hours record are treated as always available." + }, + "TimeWindow": { + "type": "object", + "properties": { + "start": { + "type": "string", + "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$", + "description": "24h wall-clock time (\"HH:mm\") in the timezone of the working-hours record." + }, + "end": { + "type": "string", + "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$", + "description": "24h wall-clock time (\"HH:mm\") in the timezone of the working-hours record." + } + }, + "required": [ + "start", + "end" + ], + "description": "A wall-clock working window within a single day." + }, "Calendar": { "type": "object", "properties": {