openapi: 3.0.3 info: title: Vinta Schedule API version: 1.0.0 description: API for vinta-schedule-api project paths: /available-times/: get: operationId: available_times_list description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - available-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedAvailableTimeList' description: '' post: operationId: available_times_create description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTime' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' /available-times{format}: get: operationId: available_times_formatted_list description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - available-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedAvailableTimeList' description: '' post: operationId: available_times_formatted_create description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTime' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' /available-times/{id}/: get: operationId: available_times_retrieve description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - available-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' put: operationId: available_times_update description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTime' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' patch: operationId: available_times_partial_update description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedAvailableTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedAvailableTime' multipart/form-data: schema: $ref: '#/components/schemas/PatchedAvailableTime' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' delete: operationId: available_times_destroy description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - available-times security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /available-times/{id}{format}: get: operationId: available_times_formatted_retrieve description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - available-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' put: operationId: available_times_formatted_update description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTime' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' patch: operationId: available_times_formatted_partial_update description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedAvailableTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedAvailableTime' multipart/form-data: schema: $ref: '#/components/schemas/PatchedAvailableTime' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' delete: operationId: available_times_formatted_destroy description: ViewSet for managing available times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - available-times security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /available-times/{id}/bulk-modify/: post: operationId: available_times_bulk_modify_create description: ViewSet for managing available times with recurring support. summary: Bulk modify or cancel recurring available time from a date parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTimeBulkModification' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTimeBulkModification' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTimeBulkModification' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' '204': description: No response body /available-times/{id}/bulk-modify{format}: post: operationId: available_times_bulk_modify_formatted_create description: ViewSet for managing available times with recurring support. summary: Bulk modify or cancel recurring available time from a date parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTimeBulkModification' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTimeBulkModification' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTimeBulkModification' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' '204': description: No response body /available-times/{id}/create-exception/: post: operationId: available_times_create_exception_create description: Create an exception for a recurring available time (either cancelled or modified). summary: Create recurring available time exception parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTimeRecurringException' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTimeRecurringException' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTimeRecurringException' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' '204': description: No response body /available-times/{id}/create-exception{format}: post: operationId: available_times_create_exception_formatted_create description: Create an exception for a recurring available time (either cancelled or modified). summary: Create recurring available time exception parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTimeRecurringException' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTimeRecurringException' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTimeRecurringException' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/AvailableTime' description: '' '204': description: No response body /available-times/batch/: post: operationId: available_times_batch_create description: Apply a list of create/update/delete operations to a single calendar's available times in one transaction (all-or-nothing). The calendar defaults to the user's default calendar when omitted. Returns the calendar's available times after the batch. summary: Batch create/update/delete available times parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTimeBatch' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTimeBatch' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTimeBatch' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedAvailableTimeList' description: '' /available-times/batch{format}: post: operationId: available_times_batch_formatted_create description: Apply a list of create/update/delete operations to a single calendar's available times in one transaction (all-or-nothing). The calendar defaults to the user's default calendar when omitted. Returns the calendar's available times after the batch. summary: Batch create/update/delete available times parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - available-times requestBody: content: application/json: schema: $ref: '#/components/schemas/AvailableTimeBatch' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AvailableTimeBatch' multipart/form-data: schema: $ref: '#/components/schemas/AvailableTimeBatch' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedAvailableTimeList' description: '' /available-times/expanded/: get: operationId: available_times_expanded_list description: Get expanded available times including recurring instances. summary: Get expanded available times parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_id schema: type: integer description: Calendar ID to get available times for required: true - in: query name: end_datetime schema: type: string description: End datetime for the range (ISO format) required: true - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_datetime schema: type: string description: Start datetime for the range (ISO format) required: true - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - available-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedAvailableTimeList' description: '' /available-times/expanded{format}: get: operationId: available_times_expanded_formatted_list description: Get expanded available times including recurring instances. summary: Get expanded available times parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_id schema: type: integer description: Calendar ID to get available times for required: true - in: query name: end_datetime schema: type: string description: End datetime for the range (ISO format) required: true - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_datetime schema: type: string description: Start datetime for the range (ISO format) required: true - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - available-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedAvailableTimeList' description: '' /billing-profile/create_billing_profile/: post: operationId: billing_profile_create_billing_profile_create description: Create a new billing profile for the active organization. summary: Create billing profile parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - billing-profile requestBody: content: application/json: schema: $ref: '#/components/schemas/BillingProfile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BillingProfile' multipart/form-data: schema: $ref: '#/components/schemas/BillingProfile' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/BillingProfile' description: '' /billing-profile/create_billing_profile{format}: post: operationId: billing_profile_create_billing_profile_formatted_create description: Create a new billing profile for the active organization. summary: Create billing profile parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - billing-profile requestBody: content: application/json: schema: $ref: '#/components/schemas/BillingProfile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BillingProfile' multipart/form-data: schema: $ref: '#/components/schemas/BillingProfile' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/BillingProfile' description: '' /billing-profile/partial_update_billing_profile/: patch: operationId: billing_profile_partial_update_billing_profile_partial_update description: Partially update the billing profile of the active organization. summary: Partially update billing profile parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - billing-profile requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedBillingProfile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedBillingProfile' multipart/form-data: schema: $ref: '#/components/schemas/PatchedBillingProfile' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BillingProfile' description: '' /billing-profile/partial_update_billing_profile{format}: patch: operationId: billing_profile_partial_update_billing_profile_formatted_partial_update description: Partially update the billing profile of the active organization. summary: Partially update billing profile parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - billing-profile requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedBillingProfile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedBillingProfile' multipart/form-data: schema: $ref: '#/components/schemas/PatchedBillingProfile' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BillingProfile' description: '' /billing-profile/retrieve_billing_profile/: get: operationId: billing_profile_retrieve_billing_profile_retrieve description: Retrieve the billing profile of the active organization. summary: Retrieve billing profile parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - billing-profile security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BillingProfile' description: '' /billing-profile/retrieve_billing_profile{format}: get: operationId: billing_profile_retrieve_billing_profile_formatted_retrieve description: Retrieve the billing profile of the active organization. summary: Retrieve billing profile parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - billing-profile security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BillingProfile' description: '' /billing-profile/update_billing_profile/: put: operationId: billing_profile_update_billing_profile_update description: Update the billing profile of the active organization. summary: Update billing profile parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - billing-profile requestBody: content: application/json: schema: $ref: '#/components/schemas/BillingProfile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BillingProfile' multipart/form-data: schema: $ref: '#/components/schemas/BillingProfile' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BillingProfile' description: '' /billing-profile/update_billing_profile{format}: put: operationId: billing_profile_update_billing_profile_formatted_update description: Update the billing profile of the active organization. summary: Update billing profile parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - billing-profile requestBody: content: application/json: schema: $ref: '#/components/schemas/BillingProfile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BillingProfile' multipart/form-data: schema: $ref: '#/components/schemas/BillingProfile' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BillingProfile' description: '' /billing/add-ons/: post: operationId: billing_add_ons_create description: |- ``POST /billing/add-ons/`` (purchase capacity), ``DELETE /billing/add-ons/{id}/`` (stop a recurring add-on from renewing). ``SubscriptionAddOnSerializer`` is a plain ``ModelSerializer`` -- no nested relation heavy enough to warrant a virtual model (see ``payments/virtual_models.py``) -- so this does not mix in ``GenericVirtualModelViewMixin``, unlike ``SubscriptionViewSet``. summary: Purchase additional capacity parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - billing requestBody: content: application/json: schema: $ref: '#/components/schemas/AddOnPurchaseRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AddOnPurchaseRequest' multipart/form-data: schema: $ref: '#/components/schemas/AddOnPurchaseRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/SubscriptionAddOn' description: '' /billing/add-ons{format}: post: operationId: billing_add_ons_formatted_create description: |- ``POST /billing/add-ons/`` (purchase capacity), ``DELETE /billing/add-ons/{id}/`` (stop a recurring add-on from renewing). ``SubscriptionAddOnSerializer`` is a plain ``ModelSerializer`` -- no nested relation heavy enough to warrant a virtual model (see ``payments/virtual_models.py``) -- so this does not mix in ``GenericVirtualModelViewMixin``, unlike ``SubscriptionViewSet``. summary: Purchase additional capacity parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - billing requestBody: content: application/json: schema: $ref: '#/components/schemas/AddOnPurchaseRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AddOnPurchaseRequest' multipart/form-data: schema: $ref: '#/components/schemas/AddOnPurchaseRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/SubscriptionAddOn' description: '' /billing/add-ons/{id}/: delete: operationId: billing_add_ons_destroy description: |- ``POST /billing/add-ons/`` (purchase capacity), ``DELETE /billing/add-ons/{id}/`` (stop a recurring add-on from renewing). ``SubscriptionAddOnSerializer`` is a plain ``ModelSerializer`` -- no nested relation heavy enough to warrant a virtual model (see ``payments/virtual_models.py``) -- so this does not mix in ``GenericVirtualModelViewMixin``, unlike ``SubscriptionViewSet``. summary: Cancel a recurring add-on at period end parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SubscriptionAddOn' description: '' /billing/add-ons/{id}{format}: delete: operationId: billing_add_ons_formatted_destroy description: |- ``POST /billing/add-ons/`` (purchase capacity), ``DELETE /billing/add-ons/{id}/`` (stop a recurring add-on from renewing). ``SubscriptionAddOnSerializer`` is a plain ``ModelSerializer`` -- no nested relation heavy enough to warrant a virtual model (see ``payments/virtual_models.py``) -- so this does not mix in ``GenericVirtualModelViewMixin``, unlike ``SubscriptionViewSet``. summary: Cancel a recurring add-on at period end parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SubscriptionAddOn' description: '' /billing/plans/: get: operationId: billing_plans_list description: |- ``GET /billing/plans/`` -- the active catalog, with limits and entitlements, so a client can render an upgrade picker in one round trip. summary: List active billing plans parameters: - in: query name: currency schema: type: string - in: query name: is_active schema: type: boolean - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBillingPlanList' description: '' /billing/plans{format}: get: operationId: billing_plans_formatted_list description: |- ``GET /billing/plans/`` -- the active catalog, with limits and entitlements, so a client can render an upgrade picker in one round trip. summary: List active billing plans parameters: - in: query name: currency schema: type: string - in: path name: format schema: type: string enum: - .json required: true - in: query name: is_active schema: type: boolean - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBillingPlanList' description: '' /billing/subscription/cancel/: post: operationId: billing_subscription_cancel_create description: |- ``GET /billing/subscription/``, ``POST .../change-plan/``, ``POST .../cancel/``. summary: Cancel the org's subscription parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Subscription' description: '' /billing/subscription/cancel{format}: post: operationId: billing_subscription_cancel_formatted_create description: |- ``GET /billing/subscription/``, ``POST .../change-plan/``, ``POST .../cancel/``. summary: Cancel the org's subscription parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Subscription' description: '' /billing/subscription/change-plan/: post: operationId: billing_subscription_change_plan_create description: |- ``GET /billing/subscription/``, ``POST .../change-plan/``, ``POST .../cancel/``. summary: Upgrade or downgrade the org's plan parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - billing requestBody: content: application/json: schema: $ref: '#/components/schemas/ChangePlanRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ChangePlanRequest' multipart/form-data: schema: $ref: '#/components/schemas/ChangePlanRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Subscription' description: '' /billing/subscription/change-plan{format}: post: operationId: billing_subscription_change_plan_formatted_create description: |- ``GET /billing/subscription/``, ``POST .../change-plan/``, ``POST .../cancel/``. summary: Upgrade or downgrade the org's plan parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - billing requestBody: content: application/json: schema: $ref: '#/components/schemas/ChangePlanRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ChangePlanRequest' multipart/form-data: schema: $ref: '#/components/schemas/ChangePlanRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Subscription' description: '' /billing/subscription/retrieve_subscription/: get: operationId: billing_subscription_retrieve_subscription_retrieve description: |- ``GET /billing/subscription/``, ``POST .../change-plan/``, ``POST .../cancel/``. summary: Retrieve the org's subscription parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Subscription' description: '' /billing/subscription/retrieve_subscription{format}: get: operationId: billing_subscription_retrieve_subscription_formatted_retrieve description: |- ``GET /billing/subscription/``, ``POST .../change-plan/``, ``POST .../cancel/``. summary: Retrieve the org's subscription parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Subscription' description: '' /billing/usage/retrieve_usage/: get: operationId: billing_usage_retrieve_usage_retrieve description: |- ``GET /billing/usage/`` -- current usage against effective limits, per resource, plus ``billing_state``. Resolved at the billing root, same as every other read in this app. The "pull" half of "an organization can see where it stands". It reads usage through the identical ``EntitlementService.get_effective_limit`` / ``get_current_usage`` methods ``check_limit`` / ``check_postpaid_allowance`` count against, and that ``payments.services.usage_warning_service.UsageWarningService`` (the "push" half -- proactive approaching-limit notifications) also reads its ceiling from -- so this endpoint, the enforcement checks, and the beat warning can never disagree about a number. No permission beyond ``IsAuthenticated``, deliberately -- a read never blocks, including for a ``RESTRICTED`` organization (a RESTRICTED organization has its writes blocked and sync paused, never its reads; an organization must be able to see exactly what it needs to resolve before it can act on it). summary: Get current usage against effective limits parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/UsageResponse' description: '' /billing/usage/retrieve_usage{format}: get: operationId: billing_usage_retrieve_usage_formatted_retrieve description: |- ``GET /billing/usage/`` -- current usage against effective limits, per resource, plus ``billing_state``. Resolved at the billing root, same as every other read in this app. The "pull" half of "an organization can see where it stands". It reads usage through the identical ``EntitlementService.get_effective_limit`` / ``get_current_usage`` methods ``check_limit`` / ``check_postpaid_allowance`` count against, and that ``payments.services.usage_warning_service.UsageWarningService`` (the "push" half -- proactive approaching-limit notifications) also reads its ceiling from -- so this endpoint, the enforcement checks, and the beat warning can never disagree about a number. No permission beyond ``IsAuthenticated``, deliberately -- a read never blocks, including for a ``RESTRICTED`` organization (a RESTRICTED organization has its writes blocked and sync paused, never its reads; an organization must be able to see exactly what it needs to resolve before it can act on it). summary: Get current usage against effective limits parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - billing security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/UsageResponse' description: '' /blocked-times/: get: operationId: blocked_times_list description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: reason schema: type: string description: Filter by partial title match - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - blocked-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBlockedTimeList' description: '' post: operationId: blocked_times_create description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockedTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BlockedTime' multipart/form-data: schema: $ref: '#/components/schemas/BlockedTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' /blocked-times{format}: get: operationId: blocked_times_formatted_list description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: reason schema: type: string description: Filter by partial title match - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - blocked-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBlockedTimeList' description: '' post: operationId: blocked_times_formatted_create description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockedTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BlockedTime' multipart/form-data: schema: $ref: '#/components/schemas/BlockedTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' /blocked-times/{id}/: get: operationId: blocked_times_retrieve description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - blocked-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' put: operationId: blocked_times_update description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockedTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BlockedTime' multipart/form-data: schema: $ref: '#/components/schemas/BlockedTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' patch: operationId: blocked_times_partial_update description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedBlockedTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedBlockedTime' multipart/form-data: schema: $ref: '#/components/schemas/PatchedBlockedTime' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' delete: operationId: blocked_times_destroy description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - blocked-times security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /blocked-times/{id}{format}: get: operationId: blocked_times_formatted_retrieve description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - blocked-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' put: operationId: blocked_times_formatted_update description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockedTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BlockedTime' multipart/form-data: schema: $ref: '#/components/schemas/BlockedTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' patch: operationId: blocked_times_formatted_partial_update description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedBlockedTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedBlockedTime' multipart/form-data: schema: $ref: '#/components/schemas/PatchedBlockedTime' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' delete: operationId: blocked_times_formatted_destroy description: ViewSet for managing blocked times with recurring support. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - blocked-times security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /blocked-times/{id}/bulk-modify/: post: operationId: blocked_times_bulk_modify_create description: ViewSet for managing blocked times with recurring support. summary: Bulk modify or cancel recurring blocked time from a date parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockedTimeBulkModification' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BlockedTimeBulkModification' multipart/form-data: schema: $ref: '#/components/schemas/BlockedTimeBulkModification' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' '204': description: No response body /blocked-times/{id}/bulk-modify{format}: post: operationId: blocked_times_bulk_modify_formatted_create description: ViewSet for managing blocked times with recurring support. summary: Bulk modify or cancel recurring blocked time from a date parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockedTimeBulkModification' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BlockedTimeBulkModification' multipart/form-data: schema: $ref: '#/components/schemas/BlockedTimeBulkModification' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' '204': description: No response body /blocked-times/{id}/create-exception/: post: operationId: blocked_times_create_exception_create description: Create an exception for a recurring blocked time (either cancelled or modified). summary: Create recurring blocked time exception parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockedTimeRecurringException' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BlockedTimeRecurringException' multipart/form-data: schema: $ref: '#/components/schemas/BlockedTimeRecurringException' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' '204': description: No response body /blocked-times/{id}/create-exception{format}: post: operationId: blocked_times_create_exception_formatted_create description: Create an exception for a recurring blocked time (either cancelled or modified). summary: Create recurring blocked time exception parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BlockedTimeRecurringException' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BlockedTimeRecurringException' multipart/form-data: schema: $ref: '#/components/schemas/BlockedTimeRecurringException' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/BlockedTime' description: '' '204': description: No response body /blocked-times/bulk-create/: post: operationId: blocked_times_bulk_create_create description: Create multiple blocked times. summary: Create bulk blocked times parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: reason schema: type: string description: Filter by partial title match - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BulkBlockedTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BulkBlockedTime' multipart/form-data: schema: $ref: '#/components/schemas/BulkBlockedTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/PaginatedBlockedTimeList' description: '' /blocked-times/bulk-create{format}: post: operationId: blocked_times_bulk_create_formatted_create description: Create multiple blocked times. summary: Create bulk blocked times parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: reason schema: type: string description: Filter by partial title match - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - blocked-times requestBody: content: application/json: schema: $ref: '#/components/schemas/BulkBlockedTime' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BulkBlockedTime' multipart/form-data: schema: $ref: '#/components/schemas/BulkBlockedTime' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/PaginatedBlockedTimeList' description: '' /blocked-times/expanded/: get: operationId: blocked_times_expanded_list description: Get expanded blocked times including recurring instances. summary: Get expanded blocked times parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_id schema: type: integer description: Calendar ID to get blocked times for required: true - in: query name: end_datetime schema: type: string description: End datetime for the range (ISO format) required: true - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: reason schema: type: string description: Filter by partial title match - in: query name: start_datetime schema: type: string description: Start datetime for the range (ISO format) required: true - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - blocked-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBlockedTimeList' description: '' /blocked-times/expanded{format}: get: operationId: blocked_times_expanded_formatted_list description: Get expanded blocked times including recurring instances. summary: Get expanded blocked times parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_id schema: type: integer description: Calendar ID to get blocked times for required: true - in: query name: end_datetime schema: type: string description: End datetime for the range (ISO format) required: true - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: reason schema: type: string description: Filter by partial title match - in: query name: start_datetime schema: type: string description: Start datetime for the range (ISO format) required: true - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) tags: - blocked-times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBlockedTimeList' description: '' /booking-policies/: get: operationId: booking_policies_list description: |- ViewSet for managing ``BookingPolicy`` objects. Provides full CRUD for booking policies scoped to the authenticated user's organization. Write operations (create, update, delete) delegate to ``BookingPolicyService`` so validation, uniqueness checking, and audit emission live in a single place. **Exactly-one-target rule:** create requests must supply exactly one of ``calendar``, ``membership_user_id``, ``calendar_group``, or ``is_organization_default=true``. Any other combination returns 400. **Duplicate target → 400:** creating a second policy for the same target is a validation error (not a 409) so the serializer can name the conflict. **Idempotent destroy:** ``DELETE /booking-policies/{id}/`` returns 204 even when the policy does not exist for the bound organization. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - Booking Policies security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBookingPolicyList' description: '' post: operationId: booking_policies_create description: Create a new booking policy for the organization. Exactly one of 'calendar', 'membership_user_id', 'calendar_group', or 'is_organization_default' must be set. Returns 400 when a policy for the target already exists. summary: Create a booking policy parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - Booking Policies requestBody: content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BookingPolicy' multipart/form-data: schema: $ref: '#/components/schemas/BookingPolicy' security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' description: '' /booking-policies{format}: get: operationId: booking_policies_formatted_list description: |- ViewSet for managing ``BookingPolicy`` objects. Provides full CRUD for booking policies scoped to the authenticated user's organization. Write operations (create, update, delete) delegate to ``BookingPolicyService`` so validation, uniqueness checking, and audit emission live in a single place. **Exactly-one-target rule:** create requests must supply exactly one of ``calendar``, ``membership_user_id``, ``calendar_group``, or ``is_organization_default=true``. Any other combination returns 400. **Duplicate target → 400:** creating a second policy for the same target is a validation error (not a 409) so the serializer can name the conflict. **Idempotent destroy:** ``DELETE /booking-policies/{id}/`` returns 204 even when the policy does not exist for the bound organization. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - Booking Policies security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBookingPolicyList' description: '' post: operationId: booking_policies_formatted_create description: Create a new booking policy for the organization. Exactly one of 'calendar', 'membership_user_id', 'calendar_group', or 'is_organization_default' must be set. Returns 400 when a policy for the target already exists. summary: Create a booking policy parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - Booking Policies requestBody: content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BookingPolicy' multipart/form-data: schema: $ref: '#/components/schemas/BookingPolicy' security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' description: '' /booking-policies/{id}/: get: operationId: booking_policies_retrieve description: |- ViewSet for managing ``BookingPolicy`` objects. Provides full CRUD for booking policies scoped to the authenticated user's organization. Write operations (create, update, delete) delegate to ``BookingPolicyService`` so validation, uniqueness checking, and audit emission live in a single place. **Exactly-one-target rule:** create requests must supply exactly one of ``calendar``, ``membership_user_id``, ``calendar_group``, or ``is_organization_default=true``. Any other combination returns 400. **Duplicate target → 400:** creating a second policy for the same target is a validation error (not a 409) so the serializer can name the conflict. **Idempotent destroy:** ``DELETE /booking-policies/{id}/`` returns 204 even when the policy does not exist for the bound organization. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - Booking Policies security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' description: '' put: operationId: booking_policies_update description: Update the rule fields (lead_time_seconds, max_horizon_seconds, buffer_before_seconds, buffer_after_seconds) of an existing booking policy. Target fields (calendar, membership_user_id, calendar_group, is_organization_default) are immutable after creation. summary: Update a booking policy parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - Booking Policies requestBody: content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BookingPolicy' multipart/form-data: schema: $ref: '#/components/schemas/BookingPolicy' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' description: '' patch: operationId: booking_policies_partial_update description: |- ViewSet for managing ``BookingPolicy`` objects. Provides full CRUD for booking policies scoped to the authenticated user's organization. Write operations (create, update, delete) delegate to ``BookingPolicyService`` so validation, uniqueness checking, and audit emission live in a single place. **Exactly-one-target rule:** create requests must supply exactly one of ``calendar``, ``membership_user_id``, ``calendar_group``, or ``is_organization_default=true``. Any other combination returns 400. **Duplicate target → 400:** creating a second policy for the same target is a validation error (not a 409) so the serializer can name the conflict. **Idempotent destroy:** ``DELETE /booking-policies/{id}/`` returns 204 even when the policy does not exist for the bound organization. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - Booking Policies requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedBookingPolicy' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedBookingPolicy' multipart/form-data: schema: $ref: '#/components/schemas/PatchedBookingPolicy' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' description: '' delete: operationId: booking_policies_destroy description: Delete a booking policy by id. Returns 204 even when the policy does not exist (idempotent no-op). summary: Delete a booking policy parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - Booking Policies security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /booking-policies/{id}{format}: get: operationId: booking_policies_formatted_retrieve description: |- ViewSet for managing ``BookingPolicy`` objects. Provides full CRUD for booking policies scoped to the authenticated user's organization. Write operations (create, update, delete) delegate to ``BookingPolicyService`` so validation, uniqueness checking, and audit emission live in a single place. **Exactly-one-target rule:** create requests must supply exactly one of ``calendar``, ``membership_user_id``, ``calendar_group``, or ``is_organization_default=true``. Any other combination returns 400. **Duplicate target → 400:** creating a second policy for the same target is a validation error (not a 409) so the serializer can name the conflict. **Idempotent destroy:** ``DELETE /booking-policies/{id}/`` returns 204 even when the policy does not exist for the bound organization. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - Booking Policies security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' description: '' put: operationId: booking_policies_formatted_update description: Update the rule fields (lead_time_seconds, max_horizon_seconds, buffer_before_seconds, buffer_after_seconds) of an existing booking policy. Target fields (calendar, membership_user_id, calendar_group, is_organization_default) are immutable after creation. summary: Update a booking policy parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - Booking Policies requestBody: content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BookingPolicy' multipart/form-data: schema: $ref: '#/components/schemas/BookingPolicy' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' description: '' patch: operationId: booking_policies_formatted_partial_update description: |- ViewSet for managing ``BookingPolicy`` objects. Provides full CRUD for booking policies scoped to the authenticated user's organization. Write operations (create, update, delete) delegate to ``BookingPolicyService`` so validation, uniqueness checking, and audit emission live in a single place. **Exactly-one-target rule:** create requests must supply exactly one of ``calendar``, ``membership_user_id``, ``calendar_group``, or ``is_organization_default=true``. Any other combination returns 400. **Duplicate target → 400:** creating a second policy for the same target is a validation error (not a 409) so the serializer can name the conflict. **Idempotent destroy:** ``DELETE /booking-policies/{id}/`` returns 204 even when the policy does not exist for the bound organization. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - Booking Policies requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedBookingPolicy' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedBookingPolicy' multipart/form-data: schema: $ref: '#/components/schemas/PatchedBookingPolicy' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/BookingPolicy' description: '' delete: operationId: booking_policies_formatted_destroy description: Delete a booking policy by id. Returns 204 even when the policy does not exist (idempotent no-op). summary: Delete a booking policy parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - Booking Policies security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /branding/: get: operationId: branding_retrieve description: |- GET /branding/ — retrieve the acting org's branding. Uses the two-condition eligibility gate, not the full write gate: a slug-less-but-otherwise-eligible org is admitted here (and falls through to the normal 404-no-row-yet / 200-with-a-row branch below) -- see ``_check_branding_read_gate``. summary: Retrieve the acting organization's branding parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - Branding security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationBranding' description: '' '403': description: Organization has a parent or lacks the entitlement; or not an admin. A slug-less-but-otherwise-eligible org is NOT refused here -- see 404. '404': description: Branding not yet configured put: operationId: branding_update description: |- PUT /branding/ — create or replace the acting org's branding. Audited (Organization Auth-Area Branding plan, Phase 4): a refused write (gate failure or serializer validation error) raises before this method reaches the upsert, so nothing is ever recorded for a refused write. A first-time upsert records a CREATE with no diff; an upsert that replaces an existing row records an UPDATE with a diff naming only the fields that actually changed, using the before-state captured BEFORE the write. summary: Create or replace the acting organization's branding parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - Branding requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationBranding' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationBranding' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationBranding' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/OrganizationBranding' description: '' '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationBranding' description: '' '400': description: Invalid input (color format, URL validation) '403': description: Organization has a parent, lacks the entitlement, or has no slug; or not an admin patch: operationId: branding_partial_update description: |- PATCH /branding/ — update the acting org's branding (partial). Audited (Organization Auth-Area Branding plan, Phase 4): a refused write (gate failure, 404-not-configured, or serializer validation error) raises before this method reaches ``serializer.save()``, so nothing is ever recorded for a refused write. Always an UPDATE (PATCH never creates — see ``_get_branding_or_404``); the before-state is captured BEFORE ``serializer.save()`` mutates ``instance`` in place. summary: Update the acting organization's branding (partial) parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - Branding requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedOrganizationBranding' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedOrganizationBranding' multipart/form-data: schema: $ref: '#/components/schemas/PatchedOrganizationBranding' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationBranding' description: '' '400': description: Invalid input (color format, URL validation) '403': description: Organization has a parent, lacks the entitlement, or has no slug; or not an admin '404': description: Branding not yet configured /branding/logo-upload-params/: post: operationId: branding_logo_upload_params_create description: |- Signs a ``branding_logos`` S3 upload for the acting organization's admin. The shipped ``django-s3direct`` signing view (``POST /s3direct/get_upload_params/``) is a plain Django view authenticated only by session cookie -- it never reaches DRF's ``JWTAuthentication``, so the JWT-only frontend SPA gets ``AnonymousUser`` there and is refused unconditionally. This view is the REST sibling of the GraphQL ``create_branding_logo_upload`` mutation (``public_api.mutations.Mutation``), reusing the same ``sign_branding_logo_upload`` signing helper so the S3 key/credential logic has one implementation. Gated on the two-condition branding **eligibility** check (``organizations.permissions.check_branding_read_eligibility`` -- parentless AND entitled), not the three-condition write gate: the frontend uploads a logo on file-picker change, before the slug/branding PUT on form submit, so requiring a slug here would refuse an upload the write gate itself never sees. Matches ``OrganizationBrandingView.get``'s read gate. summary: Sign a branding logo upload parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - Branding requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationBrandingLogoUploadParamsRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationBrandingLogoUploadParamsRequest' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationBrandingLogoUploadParamsRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationBrandingLogoUploadParams' description: '' '400': description: Disallowed content type or file size '403': description: Organization has a parent or lacks the entitlement; or not an admin /branding/logo/{org_slug}/: get: operationId: branding_logo_retrieve description: GET /branding/logo// — stream the resolved logo or the default. summary: Deliver an organization's branding logo (or our default) parameters: - in: path name: org_slug schema: type: string required: true tags: - Branding security: - {} responses: '200': content: image/*: schema: type: string format: binary description: '' /calendar/: get: operationId: calendar_list description: ViewSet for managing calendars. summary: List calendars parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_type schema: type: string enum: - bundle - personal - resource - virtual description: |- Filter by calendar type (e.g. resource) * `personal` - Personal Calendar * `resource` - Resource Calendar * `virtual` - Virtual Calendar * `bundle` - Bundle Calendar - in: query name: include_inactive schema: type: boolean description: When true, include inactive (soft-deleted) calendars (visibility=inactive). Defaults to false. - in: query name: include_unlisted schema: type: boolean description: When true, include unlisted calendars (visibility=unlisted) in the response. Unlisted calendars are hidden from booking queries but still synced. Defaults to false. - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: owner schema: type: string description: Scope the listing to a calendar owner. Pass 'me' to return only the authenticated user's own calendars. Pass a numeric user id to return that user's calendars — allowed for organization admins only; non-admins receive 403. When omitted, admins see all organization calendars while non-admins are restricted to their own. - in: query name: provider schema: type: string enum: - apple - google - ics - internal - microsoft description: |- Filter by provider (internal = manual, others = synced) * `internal` - Internal Calendar * `google` - Google Calendar * `microsoft` - Microsoft Outlook Calendar * `apple` - Apple Calendar * `ics` - ICS - in: query name: sync_enabled schema: type: boolean description: Filter by whether provider sync is enabled tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarList' description: '' post: operationId: calendar_create description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/Calendar' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Calendar' multipart/form-data: schema: $ref: '#/components/schemas/Calendar' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' /calendar{format}: get: operationId: calendar_formatted_list description: ViewSet for managing calendars. summary: List calendars parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_type schema: type: string enum: - bundle - personal - resource - virtual description: |- Filter by calendar type (e.g. resource) * `personal` - Personal Calendar * `resource` - Resource Calendar * `virtual` - Virtual Calendar * `bundle` - Bundle Calendar - in: path name: format schema: type: string enum: - .json required: true - in: query name: include_inactive schema: type: boolean description: When true, include inactive (soft-deleted) calendars (visibility=inactive). Defaults to false. - in: query name: include_unlisted schema: type: boolean description: When true, include unlisted calendars (visibility=unlisted) in the response. Unlisted calendars are hidden from booking queries but still synced. Defaults to false. - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: owner schema: type: string description: Scope the listing to a calendar owner. Pass 'me' to return only the authenticated user's own calendars. Pass a numeric user id to return that user's calendars — allowed for organization admins only; non-admins receive 403. When omitted, admins see all organization calendars while non-admins are restricted to their own. - in: query name: provider schema: type: string enum: - apple - google - ics - internal - microsoft description: |- Filter by provider (internal = manual, others = synced) * `internal` - Internal Calendar * `google` - Google Calendar * `microsoft` - Microsoft Outlook Calendar * `apple` - Apple Calendar * `ics` - ICS - in: query name: sync_enabled schema: type: boolean description: Filter by whether provider sync is enabled tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarList' description: '' post: operationId: calendar_formatted_create description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/Calendar' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Calendar' multipart/form-data: schema: $ref: '#/components/schemas/Calendar' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' /calendar-events/: get: operationId: calendar_events_list description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar schema: type: number description: Filter by calendar ID - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - in: query name: end_time_range_after schema: type: string format: date-time description: End time range - in: query name: end_time_range_before schema: type: string format: date-time description: End time range - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) - in: query name: start_time_range_after schema: type: string format: date-time description: Start time range - in: query name: start_time_range_before schema: type: string format: date-time description: Start time range - in: query name: title schema: type: string description: Filter by partial title match tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarEventList' description: '' post: operationId: calendar_events_create description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarEvent' multipart/form-data: schema: $ref: '#/components/schemas/CalendarEvent' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' /calendar-events{format}: get: operationId: calendar_events_formatted_list description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar schema: type: number description: Filter by calendar ID - in: query name: end_time schema: type: string format: date-time description: End time (less than or equal to) - in: query name: end_time_range_after schema: type: string format: date-time description: End time range - in: query name: end_time_range_before schema: type: string format: date-time description: End time range - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_time schema: type: string format: date-time description: Start time (greater than or equal to) - in: query name: start_time_range_after schema: type: string format: date-time description: Start time range - in: query name: start_time_range_before schema: type: string format: date-time description: Start time range - in: query name: title schema: type: string description: Filter by partial title match tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarEventList' description: '' post: operationId: calendar_events_formatted_create description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarEvent' multipart/form-data: schema: $ref: '#/components/schemas/CalendarEvent' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' /calendar-events/{id}/: get: operationId: calendar_events_retrieve description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' put: operationId: calendar_events_update description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarEvent' multipart/form-data: schema: $ref: '#/components/schemas/CalendarEvent' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' patch: operationId: calendar_events_partial_update description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedCalendarEvent' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedCalendarEvent' multipart/form-data: schema: $ref: '#/components/schemas/PatchedCalendarEvent' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' delete: operationId: calendar_events_destroy description: Delete a calendar event. summary: Delete calendar event parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-events/{id}{format}: get: operationId: calendar_events_formatted_retrieve description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' put: operationId: calendar_events_formatted_update description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarEvent' multipart/form-data: schema: $ref: '#/components/schemas/CalendarEvent' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' patch: operationId: calendar_events_formatted_partial_update description: ViewSet for managing calendar events. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedCalendarEvent' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedCalendarEvent' multipart/form-data: schema: $ref: '#/components/schemas/PatchedCalendarEvent' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' delete: operationId: calendar_events_formatted_destroy description: Delete a calendar event. summary: Delete calendar event parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-events/{id}/bulk-modify/: post: operationId: calendar_events_bulk_modify_create description: ViewSet for managing calendar events. summary: Bulk modify or cancel recurring event from a date parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/EventBulkModification' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/EventBulkModification' multipart/form-data: schema: $ref: '#/components/schemas/EventBulkModification' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' '204': description: No response body /calendar-events/{id}/bulk-modify{format}: post: operationId: calendar_events_bulk_modify_formatted_create description: ViewSet for managing calendar events. summary: Bulk modify or cancel recurring event from a date parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/EventBulkModification' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/EventBulkModification' multipart/form-data: schema: $ref: '#/components/schemas/EventBulkModification' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' '204': description: No response body /calendar-events/{id}/create-exception/: post: operationId: calendar_events_create_exception_create description: Create an exception for a recurring event (either cancelled or modified). summary: Create recurring event exception parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/EventRecurringException' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/EventRecurringException' multipart/form-data: schema: $ref: '#/components/schemas/EventRecurringException' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' '204': description: No response body /calendar-events/{id}/create-exception{format}: post: operationId: calendar_events_create_exception_formatted_create description: Create an exception for a recurring event (either cancelled or modified). summary: Create recurring event exception parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/EventRecurringException' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/EventRecurringException' multipart/form-data: schema: $ref: '#/components/schemas/EventRecurringException' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' '204': description: No response body /calendar-events/{id}/ics/: get: operationId: calendar_events_ics_retrieve description: |- Download a calendar event as an iCalendar (.ics) file. Returns the event in RFC 5545 format with proper timezone handling, recurrence rules (if applicable), and attendee information. summary: Download calendar event ICS parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: text/calendar: schema: type: string format: binary description: '' /calendar-events/{id}/ics{format}: get: operationId: calendar_events_ics_formatted_retrieve description: |- Download a calendar event as an iCalendar (.ics) file. Returns the event in RFC 5545 format with proper timezone handling, recurrence rules (if applicable), and attendee information. summary: Download calendar event ICS parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: text/calendar: schema: type: string format: binary description: '' /calendar-events/{id}/transfer/: post: operationId: calendar_events_transfer_create description: Move an event from its current calendar to a target calendar within the same organization. The service authenticates with the SOURCE calendar owner's credentials to read and delete the event from the provider. Admin only. summary: Transfer event to another calendar (admin) parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarEventTransfer' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarEventTransfer' multipart/form-data: schema: $ref: '#/components/schemas/CalendarEventTransfer' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' /calendar-events/{id}/transfer{format}: post: operationId: calendar_events_transfer_formatted_create description: Move an event from its current calendar to a target calendar within the same organization. The service authenticates with the SOURCE calendar owner's credentials to read and delete the event from the provider. Admin only. summary: Transfer event to another calendar (admin) parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-events requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarEventTransfer' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarEventTransfer' multipart/form-data: schema: $ref: '#/components/schemas/CalendarEventTransfer' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' /calendar-events/expanded/: get: operationId: calendar_events_expanded_list description: Get expanded calendar events including materialized recurring instances. summary: Get expanded calendar events parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar schema: type: number description: Filter by calendar ID - in: query name: calendar_id schema: type: integer description: Calendar ID to get events for required: true - in: query name: end_time schema: type: string description: End datetime for the range (ISO format) required: true - in: query name: end_time_range_after schema: type: string format: date-time description: End time range - in: query name: end_time_range_before schema: type: string format: date-time description: End time range - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_time schema: type: string description: Start datetime for the range (ISO format) required: true - in: query name: start_time_range_after schema: type: string format: date-time description: Start time range - in: query name: start_time_range_before schema: type: string format: date-time description: Start time range - in: query name: title schema: type: string description: Filter by partial title match tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarEventList' description: '' /calendar-events/expanded{format}: get: operationId: calendar_events_expanded_formatted_list description: Get expanded calendar events including materialized recurring instances. summary: Get expanded calendar events parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar schema: type: number description: Filter by calendar ID - in: query name: calendar_id schema: type: integer description: Calendar ID to get events for required: true - in: query name: end_time schema: type: string description: End datetime for the range (ISO format) required: true - in: query name: end_time_range_after schema: type: string format: date-time description: End time range - in: query name: end_time_range_before schema: type: string format: date-time description: End time range - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_time schema: type: string description: Start datetime for the range (ISO format) required: true - in: query name: start_time_range_after schema: type: string format: date-time description: Start time range - in: query name: start_time_range_before schema: type: string format: date-time description: Start time range - in: query name: title schema: type: string description: Filter by partial title match tags: - calendar-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarEventList' description: '' /calendar-groups/: get: operationId: calendar_groups_list description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - in: query name: name schema: type: string description: Filter by partial name match - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarGroupList' description: '' post: operationId: calendar_groups_create description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarGroup' multipart/form-data: schema: $ref: '#/components/schemas/CalendarGroup' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' description: '' /calendar-groups{format}: get: operationId: calendar_groups_formatted_list description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - in: query name: name schema: type: string description: Filter by partial name match - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarGroupList' description: '' post: operationId: calendar_groups_formatted_create description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarGroup' multipart/form-data: schema: $ref: '#/components/schemas/CalendarGroup' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' description: '' /calendar-groups/{group_id}/slots/{slot_id}/availability-windows/: get: operationId: calendar_groups_slots_availability_windows_list description: |- Nested under a group's slot: manage group-scoped availability windows for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 1c). Reads go through ``AvailableTime.objects.for_group_slot(...)``. Every write delegates to ``CalendarGroupService`` (Phase 1a) -- this view holds no business logic of its own, only request/response translation. Route visibility is gated by ``GroupScopedAvailabilityWindowPermission``; the per-calendar write authorization is re-checked by the service and its ``CalendarGroupSlotConfigNotFoundError`` is translated to a 404 here so a denied write and a genuinely missing window are indistinguishable. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedGroupScopedAvailabilityWindowList' description: '' post: operationId: calendar_groups_slots_availability_windows_create description: Creates a group-scoped availability window for a calendar within a group slot's roster. If this is the calendar's FIRST group-scoped window (i.e. the write narrows it from base availability), confirmed future bookings that now fall outside it are returned in `orphaned_bookings`; nothing about them is modified. summary: Create a group-scoped availability window parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows requestBody: content: application/json: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWindowCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWindowCreate' multipart/form-data: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWindowCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWriteResult' description: '' /calendar-groups/{group_id}/slots/{slot_id}/availability-windows{format}: get: operationId: calendar_groups_slots_availability_windows_formatted_list description: |- Nested under a group's slot: manage group-scoped availability windows for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 1c). Reads go through ``AvailableTime.objects.for_group_slot(...)``. Every write delegates to ``CalendarGroupService`` (Phase 1a) -- this view holds no business logic of its own, only request/response translation. Route visibility is gated by ``GroupScopedAvailabilityWindowPermission``; the per-calendar write authorization is re-checked by the service and its ``CalendarGroupSlotConfigNotFoundError`` is translated to a 404 here so a denied write and a genuinely missing window are indistinguishable. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedGroupScopedAvailabilityWindowList' description: '' post: operationId: calendar_groups_slots_availability_windows_formatted_create description: Creates a group-scoped availability window for a calendar within a group slot's roster. If this is the calendar's FIRST group-scoped window (i.e. the write narrows it from base availability), confirmed future bookings that now fall outside it are returned in `orphaned_bookings`; nothing about them is modified. summary: Create a group-scoped availability window parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows requestBody: content: application/json: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWindowCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWindowCreate' multipart/form-data: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWindowCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWriteResult' description: '' /calendar-groups/{group_id}/slots/{slot_id}/availability-windows/{id}/: get: operationId: calendar_groups_slots_availability_windows_retrieve description: |- Nested under a group's slot: manage group-scoped availability windows for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 1c). Reads go through ``AvailableTime.objects.for_group_slot(...)``. Every write delegates to ``CalendarGroupService`` (Phase 1a) -- this view holds no business logic of its own, only request/response translation. Route visibility is gated by ``GroupScopedAvailabilityWindowPermission``; the per-calendar write authorization is re-checked by the service and its ``CalendarGroupSlotConfigNotFoundError`` is translated to a 404 here so a denied write and a genuinely missing window are indistinguishable. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWindow' description: '' patch: operationId: calendar_groups_slots_availability_windows_partial_update description: Partial update -- only provided fields change. If the change narrows the window, confirmed future bookings that now fall outside it are returned in `orphaned_bookings`; nothing about them is modified. summary: Update a group-scoped availability window parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedGroupScopedAvailabilityWindowUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedGroupScopedAvailabilityWindowUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedGroupScopedAvailabilityWindowUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWriteResult' description: '' delete: operationId: calendar_groups_slots_availability_windows_destroy description: Deletes the window (a recurring window is one row -- deletes the whole series). summary: Delete a group-scoped availability window parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-groups/{group_id}/slots/{slot_id}/availability-windows/{id}{format}: get: operationId: calendar_groups_slots_availability_windows_formatted_retrieve description: |- Nested under a group's slot: manage group-scoped availability windows for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 1c). Reads go through ``AvailableTime.objects.for_group_slot(...)``. Every write delegates to ``CalendarGroupService`` (Phase 1a) -- this view holds no business logic of its own, only request/response translation. Route visibility is gated by ``GroupScopedAvailabilityWindowPermission``; the per-calendar write authorization is re-checked by the service and its ``CalendarGroupSlotConfigNotFoundError`` is translated to a 404 here so a denied write and a genuinely missing window are indistinguishable. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWindow' description: '' patch: operationId: calendar_groups_slots_availability_windows_formatted_partial_update description: Partial update -- only provided fields change. If the change narrows the window, confirmed future bookings that now fall outside it are returned in `orphaned_bookings`; nothing about them is modified. summary: Update a group-scoped availability window parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedGroupScopedAvailabilityWindowUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedGroupScopedAvailabilityWindowUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedGroupScopedAvailabilityWindowUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedAvailabilityWriteResult' description: '' delete: operationId: calendar_groups_slots_availability_windows_formatted_destroy description: Deletes the window (a recurring window is one row -- deletes the whole series). summary: Delete a group-scoped availability window parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Availability Windows security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-groups/{group_id}/slots/{slot_id}/blocked-times/: get: operationId: calendar_groups_slots_blocked_times_list description: |- Nested under a group's slot: manage group-scoped blocked times for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 2b). Direct mirror of ``GroupScopedAvailabilityWindowViewSet`` -- reads go through ``BlockedTime.objects.for_group_slot(...)``, every write delegates to the Phase 2a ``CalendarGroupService`` block-write methods, and route visibility is gated by ``GroupScopedBlockedTimePermission``. See that viewset's docstring for the full rationale; only the resource it manages differs (blocks instead of windows). parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedGroupScopedBlockedTimeList' description: '' post: operationId: calendar_groups_slots_blocked_times_create description: Creates a group-scoped blocked time for a calendar within a group slot's roster. Confirmed future bookings in that group for that calendar that now fall INSIDE the block are returned in `orphaned_bookings`; nothing about them is modified. summary: Create a group-scoped blocked time parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times requestBody: content: application/json: schema: $ref: '#/components/schemas/GroupScopedBlockedTimeCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/GroupScopedBlockedTimeCreate' multipart/form-data: schema: $ref: '#/components/schemas/GroupScopedBlockedTimeCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/GroupScopedBlockWriteResult' description: '' /calendar-groups/{group_id}/slots/{slot_id}/blocked-times{format}: get: operationId: calendar_groups_slots_blocked_times_formatted_list description: |- Nested under a group's slot: manage group-scoped blocked times for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 2b). Direct mirror of ``GroupScopedAvailabilityWindowViewSet`` -- reads go through ``BlockedTime.objects.for_group_slot(...)``, every write delegates to the Phase 2a ``CalendarGroupService`` block-write methods, and route visibility is gated by ``GroupScopedBlockedTimePermission``. See that viewset's docstring for the full rationale; only the resource it manages differs (blocks instead of windows). parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedGroupScopedBlockedTimeList' description: '' post: operationId: calendar_groups_slots_blocked_times_formatted_create description: Creates a group-scoped blocked time for a calendar within a group slot's roster. Confirmed future bookings in that group for that calendar that now fall INSIDE the block are returned in `orphaned_bookings`; nothing about them is modified. summary: Create a group-scoped blocked time parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times requestBody: content: application/json: schema: $ref: '#/components/schemas/GroupScopedBlockedTimeCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/GroupScopedBlockedTimeCreate' multipart/form-data: schema: $ref: '#/components/schemas/GroupScopedBlockedTimeCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/GroupScopedBlockWriteResult' description: '' /calendar-groups/{group_id}/slots/{slot_id}/blocked-times/{id}/: get: operationId: calendar_groups_slots_blocked_times_retrieve description: |- Nested under a group's slot: manage group-scoped blocked times for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 2b). Direct mirror of ``GroupScopedAvailabilityWindowViewSet`` -- reads go through ``BlockedTime.objects.for_group_slot(...)``, every write delegates to the Phase 2a ``CalendarGroupService`` block-write methods, and route visibility is gated by ``GroupScopedBlockedTimePermission``. See that viewset's docstring for the full rationale; only the resource it manages differs (blocks instead of windows). parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedBlockedTime' description: '' patch: operationId: calendar_groups_slots_blocked_times_partial_update description: Partial update -- only provided fields change. Confirmed future bookings that now fall inside the block are returned in `orphaned_bookings`; nothing about them is modified. summary: Update a group-scoped blocked time parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedGroupScopedBlockedTimeUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedGroupScopedBlockedTimeUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedGroupScopedBlockedTimeUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedBlockWriteResult' description: '' delete: operationId: calendar_groups_slots_blocked_times_destroy description: Deletes the block (a recurring block is one row -- deletes the whole series). summary: Delete a group-scoped blocked time parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-groups/{group_id}/slots/{slot_id}/blocked-times/{id}{format}: get: operationId: calendar_groups_slots_blocked_times_formatted_retrieve description: |- Nested under a group's slot: manage group-scoped blocked times for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 2b). Direct mirror of ``GroupScopedAvailabilityWindowViewSet`` -- reads go through ``BlockedTime.objects.for_group_slot(...)``, every write delegates to the Phase 2a ``CalendarGroupService`` block-write methods, and route visibility is gated by ``GroupScopedBlockedTimePermission``. See that viewset's docstring for the full rationale; only the resource it manages differs (blocks instead of windows). parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedBlockedTime' description: '' patch: operationId: calendar_groups_slots_blocked_times_formatted_partial_update description: Partial update -- only provided fields change. Confirmed future bookings that now fall inside the block are returned in `orphaned_bookings`; nothing about them is modified. summary: Update a group-scoped blocked time parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedGroupScopedBlockedTimeUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedGroupScopedBlockedTimeUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedGroupScopedBlockedTimeUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedBlockWriteResult' description: '' delete: operationId: calendar_groups_slots_blocked_times_formatted_destroy description: Deletes the block (a recurring block is one row -- deletes the whole series). summary: Delete a group-scoped blocked time parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Blocked Times security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-groups/{group_id}/slots/{slot_id}/quota-rules/: get: operationId: calendar_groups_slots_quota_rules_list description: |- Nested under a group's slot: manage group-scoped quota rules for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 3c). Mirrors ``GroupScopedAvailabilityWindowViewSet``/``GroupScopedBlockedTimeViewSet`` exactly -- reads go through ``CalendarGroupSlotQuotaRule.objects.for_group_slot(...)``, every write delegates to the Phase 3c ``CalendarGroupService`` quota-write methods, and route visibility is gated by ``GroupScopedQuotaRulePermission``. The resource is simpler than windows/blocks: quota rules are non-recurring (no ``rrule_string``/``timezone``/time range) and unmetered (no entitlement ``check_limit`` gates their creation -- only ``check_not_restricted``, like blocks). There is also no orphaned-booking report: a quota rule caps FUTURE bookings and never narrows already-confirmed ones, so the create/update responses return the saved rule directly rather than a write-result wrapper. The uniqueness constraint on (calendar, slot, period) is surfaced here as a 400 validation error (``CalendarGroupValidationError`` -> DRF ``ValidationError``), never an unhandled ``IntegrityError``/500. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedGroupScopedQuotaRuleList' description: '' post: operationId: calendar_groups_slots_quota_rules_create description: Creates a group-scoped quota rule capping a calendar's live bookings made through a group slot within a fixed period. Not metered -- no entitlement limit gates this write. summary: Create a group-scoped quota rule parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules requestBody: content: application/json: schema: $ref: '#/components/schemas/GroupScopedQuotaRuleCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/GroupScopedQuotaRuleCreate' multipart/form-data: schema: $ref: '#/components/schemas/GroupScopedQuotaRuleCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/GroupScopedQuotaRule' description: '' /calendar-groups/{group_id}/slots/{slot_id}/quota-rules{format}: get: operationId: calendar_groups_slots_quota_rules_formatted_list description: |- Nested under a group's slot: manage group-scoped quota rules for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 3c). Mirrors ``GroupScopedAvailabilityWindowViewSet``/``GroupScopedBlockedTimeViewSet`` exactly -- reads go through ``CalendarGroupSlotQuotaRule.objects.for_group_slot(...)``, every write delegates to the Phase 3c ``CalendarGroupService`` quota-write methods, and route visibility is gated by ``GroupScopedQuotaRulePermission``. The resource is simpler than windows/blocks: quota rules are non-recurring (no ``rrule_string``/``timezone``/time range) and unmetered (no entitlement ``check_limit`` gates their creation -- only ``check_not_restricted``, like blocks). There is also no orphaned-booking report: a quota rule caps FUTURE bookings and never narrows already-confirmed ones, so the create/update responses return the saved rule directly rather than a write-result wrapper. The uniqueness constraint on (calendar, slot, period) is surfaced here as a 400 validation error (``CalendarGroupValidationError`` -> DRF ``ValidationError``), never an unhandled ``IntegrityError``/500. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedGroupScopedQuotaRuleList' description: '' post: operationId: calendar_groups_slots_quota_rules_formatted_create description: Creates a group-scoped quota rule capping a calendar's live bookings made through a group slot within a fixed period. Not metered -- no entitlement limit gates this write. summary: Create a group-scoped quota rule parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules requestBody: content: application/json: schema: $ref: '#/components/schemas/GroupScopedQuotaRuleCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/GroupScopedQuotaRuleCreate' multipart/form-data: schema: $ref: '#/components/schemas/GroupScopedQuotaRuleCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/GroupScopedQuotaRule' description: '' /calendar-groups/{group_id}/slots/{slot_id}/quota-rules/{id}/: get: operationId: calendar_groups_slots_quota_rules_retrieve description: |- Nested under a group's slot: manage group-scoped quota rules for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 3c). Mirrors ``GroupScopedAvailabilityWindowViewSet``/``GroupScopedBlockedTimeViewSet`` exactly -- reads go through ``CalendarGroupSlotQuotaRule.objects.for_group_slot(...)``, every write delegates to the Phase 3c ``CalendarGroupService`` quota-write methods, and route visibility is gated by ``GroupScopedQuotaRulePermission``. The resource is simpler than windows/blocks: quota rules are non-recurring (no ``rrule_string``/``timezone``/time range) and unmetered (no entitlement ``check_limit`` gates their creation -- only ``check_not_restricted``, like blocks). There is also no orphaned-booking report: a quota rule caps FUTURE bookings and never narrows already-confirmed ones, so the create/update responses return the saved rule directly rather than a write-result wrapper. The uniqueness constraint on (calendar, slot, period) is surfaced here as a 400 validation error (``CalendarGroupValidationError`` -> DRF ``ValidationError``), never an unhandled ``IntegrityError``/500. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedQuotaRule' description: '' patch: operationId: calendar_groups_slots_quota_rules_partial_update description: Partial update -- only provided fields change. summary: Update a group-scoped quota rule parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedGroupScopedQuotaRuleUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedGroupScopedQuotaRuleUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedGroupScopedQuotaRuleUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedQuotaRule' description: '' delete: operationId: calendar_groups_slots_quota_rules_destroy description: |- Nested under a group's slot: manage group-scoped quota rules for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 3c). Mirrors ``GroupScopedAvailabilityWindowViewSet``/``GroupScopedBlockedTimeViewSet`` exactly -- reads go through ``CalendarGroupSlotQuotaRule.objects.for_group_slot(...)``, every write delegates to the Phase 3c ``CalendarGroupService`` quota-write methods, and route visibility is gated by ``GroupScopedQuotaRulePermission``. The resource is simpler than windows/blocks: quota rules are non-recurring (no ``rrule_string``/``timezone``/time range) and unmetered (no entitlement ``check_limit`` gates their creation -- only ``check_not_restricted``, like blocks). There is also no orphaned-booking report: a quota rule caps FUTURE bookings and never narrows already-confirmed ones, so the create/update responses return the saved rule directly rather than a write-result wrapper. The uniqueness constraint on (calendar, slot, period) is surfaced here as a 400 validation error (``CalendarGroupValidationError`` -> DRF ``ValidationError``), never an unhandled ``IntegrityError``/500. summary: Delete a group-scoped quota rule parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-groups/{group_id}/slots/{slot_id}/quota-rules/{id}{format}: get: operationId: calendar_groups_slots_quota_rules_formatted_retrieve description: |- Nested under a group's slot: manage group-scoped quota rules for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 3c). Mirrors ``GroupScopedAvailabilityWindowViewSet``/``GroupScopedBlockedTimeViewSet`` exactly -- reads go through ``CalendarGroupSlotQuotaRule.objects.for_group_slot(...)``, every write delegates to the Phase 3c ``CalendarGroupService`` quota-write methods, and route visibility is gated by ``GroupScopedQuotaRulePermission``. The resource is simpler than windows/blocks: quota rules are non-recurring (no ``rrule_string``/``timezone``/time range) and unmetered (no entitlement ``check_limit`` gates their creation -- only ``check_not_restricted``, like blocks). There is also no orphaned-booking report: a quota rule caps FUTURE bookings and never narrows already-confirmed ones, so the create/update responses return the saved rule directly rather than a write-result wrapper. The uniqueness constraint on (calendar, slot, period) is surfaced here as a 400 validation error (``CalendarGroupValidationError`` -> DRF ``ValidationError``), never an unhandled ``IntegrityError``/500. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedQuotaRule' description: '' patch: operationId: calendar_groups_slots_quota_rules_formatted_partial_update description: Partial update -- only provided fields change. summary: Update a group-scoped quota rule parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedGroupScopedQuotaRuleUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedGroupScopedQuotaRuleUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedGroupScopedQuotaRuleUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/GroupScopedQuotaRule' description: '' delete: operationId: calendar_groups_slots_quota_rules_formatted_destroy description: |- Nested under a group's slot: manage group-scoped quota rules for calendars in that slot's roster (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 3c). Mirrors ``GroupScopedAvailabilityWindowViewSet``/``GroupScopedBlockedTimeViewSet`` exactly -- reads go through ``CalendarGroupSlotQuotaRule.objects.for_group_slot(...)``, every write delegates to the Phase 3c ``CalendarGroupService`` quota-write methods, and route visibility is gated by ``GroupScopedQuotaRulePermission``. The resource is simpler than windows/blocks: quota rules are non-recurring (no ``rrule_string``/``timezone``/time range) and unmetered (no entitlement ``check_limit`` gates their creation -- only ``check_not_restricted``, like blocks). There is also no orphaned-booking report: a quota rule caps FUTURE bookings and never narrows already-confirmed ones, so the create/update responses return the saved rule directly rather than a write-result wrapper. The uniqueness constraint on (calendar, slot, period) is surfaced here as a 400 validation error (``CalendarGroupValidationError`` -> DRF ``ValidationError``), never an unhandled ``IntegrityError``/500. summary: Delete a group-scoped quota rule parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: group_id schema: type: integer required: true - in: path name: id schema: type: string required: true - in: path name: slot_id schema: type: integer required: true tags: - Calendar Group Scoped Quota Rules security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-groups/{id}/: get: operationId: calendar_groups_retrieve description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' description: '' put: operationId: calendar_groups_update description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarGroup' multipart/form-data: schema: $ref: '#/components/schemas/CalendarGroup' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' description: '' patch: operationId: calendar_groups_partial_update description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedCalendarGroup' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedCalendarGroup' multipart/form-data: schema: $ref: '#/components/schemas/PatchedCalendarGroup' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' description: '' delete: operationId: calendar_groups_destroy description: Delete a CalendarGroup. Fails with 400 if the group has any bookings. summary: Delete calendar group parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-groups/{id}{format}: get: operationId: calendar_groups_formatted_retrieve description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' description: '' put: operationId: calendar_groups_formatted_update description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarGroup' multipart/form-data: schema: $ref: '#/components/schemas/CalendarGroup' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' description: '' patch: operationId: calendar_groups_formatted_partial_update description: ViewSet for CalendarGroup CRUD and grouped event actions. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedCalendarGroup' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedCalendarGroup' multipart/form-data: schema: $ref: '#/components/schemas/PatchedCalendarGroup' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarGroup' description: '' delete: operationId: calendar_groups_formatted_destroy description: Delete a CalendarGroup. Fails with 400 if the group has any bookings. summary: Delete calendar group parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar-groups/{id}/availability/: post: operationId: calendar_groups_availability_create description: ViewSet for CalendarGroup CRUD and grouped event actions. summary: Per-slot availability for requested ranges parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - in: query name: name schema: type: string description: Filter by partial name match - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarGroupAvailabilityQuery' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarGroupAvailabilityQuery' multipart/form-data: schema: $ref: '#/components/schemas/CalendarGroupAvailabilityQuery' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarGroupRangeAvailabilityList' description: '' /calendar-groups/{id}/availability{format}: post: operationId: calendar_groups_availability_formatted_create description: ViewSet for CalendarGroup CRUD and grouped event actions. summary: Per-slot availability for requested ranges parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - in: query name: name schema: type: string description: Filter by partial name match - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarGroupAvailabilityQuery' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarGroupAvailabilityQuery' multipart/form-data: schema: $ref: '#/components/schemas/CalendarGroupAvailabilityQuery' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarGroupRangeAvailabilityList' description: '' /calendar-groups/{id}/bookable-slots/: get: operationId: calendar_groups_bookable_slots_list description: ViewSet for CalendarGroup CRUD and grouped event actions. summary: Bookable slot proposals for the group within a search window parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: duration_seconds schema: type: integer description: Desired event duration, in seconds required: true - in: path name: id schema: type: string required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - in: query name: name schema: type: string description: Filter by partial name match - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: search_window_end schema: type: string description: End of the search window (ISO 8601) required: true - in: query name: search_window_start schema: type: string description: Start of the search window (ISO 8601) required: true - in: query name: slot_step_seconds schema: type: integer description: Search step, in seconds (default 900 = 15min) tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBookableSlotProposalList' description: '' /calendar-groups/{id}/bookable-slots{format}: get: operationId: calendar_groups_bookable_slots_formatted_list description: ViewSet for CalendarGroup CRUD and grouped event actions. summary: Bookable slot proposals for the group within a search window parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: duration_seconds schema: type: integer description: Desired event duration, in seconds required: true - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - in: query name: name schema: type: string description: Filter by partial name match - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: search_window_end schema: type: string description: End of the search window (ISO 8601) required: true - in: query name: search_window_start schema: type: string description: Start of the search window (ISO 8601) required: true - in: query name: slot_step_seconds schema: type: integer description: Search step, in seconds (default 900 = 15min) tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedBookableSlotProposalList' description: '' /calendar-groups/{id}/booked-events/: get: operationId: calendar_groups_booked_events_list description: ViewSet for CalendarGroup CRUD and grouped event actions. summary: List events booked under this group parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_datetime schema: type: string description: End datetime in ISO format required: true - in: path name: id schema: type: string required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - in: query name: name schema: type: string description: Filter by partial name match - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_datetime schema: type: string description: Start datetime in ISO format required: true tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarEventList' description: '' /calendar-groups/{id}/booked-events{format}: get: operationId: calendar_groups_booked_events_formatted_list description: ViewSet for CalendarGroup CRUD and grouped event actions. summary: List events booked under this group parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: end_datetime schema: type: string description: End datetime in ISO format required: true - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - in: query name: name schema: type: string description: Filter by partial name match - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: start_datetime schema: type: string description: Start datetime in ISO format required: true tags: - calendar-groups security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarEventList' description: '' /calendar-groups/{id}/events/: post: operationId: calendar_groups_events_create description: ViewSet for CalendarGroup CRUD and grouped event actions. summary: Create grouped event parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarGroupEventCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarGroupEventCreate' multipart/form-data: schema: $ref: '#/components/schemas/CalendarGroupEventCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' /calendar-groups/{id}/events{format}: post: operationId: calendar_groups_events_formatted_create description: ViewSet for CalendarGroup CRUD and grouped event actions. summary: Create grouped event parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar-groups requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarGroupEventCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarGroupEventCreate' multipart/form-data: schema: $ref: '#/components/schemas/CalendarGroupEventCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' /calendar/{id}/: get: operationId: calendar_retrieve description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' put: operationId: calendar_update description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/Calendar' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Calendar' multipart/form-data: schema: $ref: '#/components/schemas/Calendar' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' patch: operationId: calendar_partial_update description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedCalendar' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedCalendar' multipart/form-data: schema: $ref: '#/components/schemas/PatchedCalendar' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' delete: operationId: calendar_destroy description: "Disables a calendar by setting visibility=inactive instead of deleting the row. The row persists and is hidden from default list/detail queries. \n\n**Authorization rules (enforced after org-scoping):**\n- BUNDLE calendar: caller must be an org admin. Non-admin members receive 403.\n- Non-bundle calendar (PERSONAL/RESOURCE/VIRTUAL): caller must own the calendar (CalendarOwnership) or be an org admin. Non-owner non-admins receive 403.\n\n\n**Bundle semantics:** disabling a bundle sets only the bundle calendar inactive. Child calendars, bundle events, and their representation BlockedTimes/events are deliberately left untouched (event cancellation is out of scope)." summary: Soft-disable a calendar parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar/{id}{format}: get: operationId: calendar_formatted_retrieve description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' put: operationId: calendar_formatted_update description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/Calendar' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Calendar' multipart/form-data: schema: $ref: '#/components/schemas/Calendar' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' patch: operationId: calendar_formatted_partial_update description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedCalendar' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedCalendar' multipart/form-data: schema: $ref: '#/components/schemas/PatchedCalendar' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' delete: operationId: calendar_formatted_destroy description: "Disables a calendar by setting visibility=inactive instead of deleting the row. The row persists and is hidden from default list/detail queries. \n\n**Authorization rules (enforced after org-scoping):**\n- BUNDLE calendar: caller must be an org admin. Non-admin members receive 403.\n- Non-bundle calendar (PERSONAL/RESOURCE/VIRTUAL): caller must own the calendar (CalendarOwnership) or be an org admin. Non-owner non-admins receive 403.\n\n\n**Bundle semantics:** disabling a bundle sets only the bundle calendar inactive. Child calendars, bundle events, and their representation BlockedTimes/events are deliberately left untouched (event cancellation is out of scope)." summary: Soft-disable a calendar parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /calendar/{id}/admin-sync/: post: operationId: calendar_admin_sync_create description: Admin syncs any calendar in the organization over a date range. summary: Admin syncs another user's calendar parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarSyncRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarSyncRequest' multipart/form-data: schema: $ref: '#/components/schemas/CalendarSyncRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': content: application/json: schema: $ref: '#/components/schemas/CalendarSync' description: '' '409': description: Sync is disabled for this calendar. /calendar/{id}/admin-sync{format}: post: operationId: calendar_admin_sync_formatted_create description: Admin syncs any calendar in the organization over a date range. summary: Admin syncs another user's calendar parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarSyncRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarSyncRequest' multipart/form-data: schema: $ref: '#/components/schemas/CalendarSyncRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': content: application/json: schema: $ref: '#/components/schemas/CalendarSync' description: '' '409': description: Sync is disabled for this calendar. /calendar/{id}/available-windows/: get: operationId: calendar_available_windows_list description: Get available time windows for a calendar within a specified date range. summary: Get available time windows parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_type schema: type: string enum: - bundle - personal - resource - virtual description: |- Filter by calendar type (e.g. resource) * `personal` - Personal Calendar * `resource` - Resource Calendar * `virtual` - Virtual Calendar * `bundle` - Bundle Calendar - in: query name: end_datetime schema: type: string description: End datetime in ISO format (YYYY-MM-DDTHH:MM:SS) required: true - in: path name: id schema: type: string required: true - in: query name: provider schema: type: string enum: - apple - google - ics - internal - microsoft description: |- Filter by provider (internal = manual, others = synced) * `internal` - Internal Calendar * `google` - Google Calendar * `microsoft` - Microsoft Outlook Calendar * `apple` - Apple Calendar * `ics` - ICS - in: query name: start_datetime schema: type: string description: Start datetime in ISO format (YYYY-MM-DDTHH:MM:SS) required: true - in: query name: sync_enabled schema: type: boolean description: Filter by whether provider sync is enabled tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/AvailableTimeWindow' description: '' /calendar/{id}/available-windows{format}: get: operationId: calendar_available_windows_formatted_list description: Get available time windows for a calendar within a specified date range. summary: Get available time windows parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_type schema: type: string enum: - bundle - personal - resource - virtual description: |- Filter by calendar type (e.g. resource) * `personal` - Personal Calendar * `resource` - Resource Calendar * `virtual` - Virtual Calendar * `bundle` - Bundle Calendar - in: query name: end_datetime schema: type: string description: End datetime in ISO format (YYYY-MM-DDTHH:MM:SS) required: true - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true - in: query name: provider schema: type: string enum: - apple - google - ics - internal - microsoft description: |- Filter by provider (internal = manual, others = synced) * `internal` - Internal Calendar * `google` - Google Calendar * `microsoft` - Microsoft Outlook Calendar * `apple` - Apple Calendar * `ics` - ICS - in: query name: start_datetime schema: type: string description: Start datetime in ISO format (YYYY-MM-DDTHH:MM:SS) required: true - in: query name: sync_enabled schema: type: boolean description: Filter by whether provider sync is enabled tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/AvailableTimeWindow' description: '' /calendar/{id}/bundle/: patch: operationId: calendar_bundle_partial_update description: Reconcile the child calendars and primary designation for an existing bundle. Provide the full desired set of bundle_calendars; children not in the list will be removed and new ones will be added. Optionally specify primary_calendar (must be one of bundle_calendars). Admin only. Returns the updated bundle calendar. summary: Update a bundle calendar's children and primary parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedCalendarBundleUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedCalendarBundleUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedCalendarBundleUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' /calendar/{id}/bundle{format}: patch: operationId: calendar_bundle_formatted_partial_update description: Reconcile the child calendars and primary designation for an existing bundle. Provide the full desired set of bundle_calendars; children not in the list will be removed and new ones will be added. Optionally specify primary_calendar (must be one of bundle_calendars). Admin only. Returns the updated bundle calendar. summary: Update a bundle calendar's children and primary parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedCalendarBundleUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedCalendarBundleUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedCalendarBundleUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' /calendar/{id}/request-sync/: post: operationId: calendar_request_sync_create description: Request synchronization of an owned calendar over a date range. summary: Request calendar sync parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarSyncRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarSyncRequest' multipart/form-data: schema: $ref: '#/components/schemas/CalendarSyncRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': content: application/json: schema: $ref: '#/components/schemas/CalendarSync' description: '' '409': description: Sync is disabled for this calendar. /calendar/{id}/request-sync{format}: post: operationId: calendar_request_sync_formatted_create description: Request synchronization of an owned calendar over a date range. summary: Request calendar sync parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarSyncRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarSyncRequest' multipart/form-data: schema: $ref: '#/components/schemas/CalendarSyncRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': content: application/json: schema: $ref: '#/components/schemas/CalendarSync' description: '' '409': description: Sync is disabled for this calendar. /calendar/{id}/unavailable-windows/: get: operationId: calendar_unavailable_windows_list description: Get unavailable time windows for a calendar within a specified date range. summary: Get unavailable time windows parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_type schema: type: string enum: - bundle - personal - resource - virtual description: |- Filter by calendar type (e.g. resource) * `personal` - Personal Calendar * `resource` - Resource Calendar * `virtual` - Virtual Calendar * `bundle` - Bundle Calendar - in: query name: end_datetime schema: type: string description: End datetime in ISO format (YYYY-MM-DDTHH:MM:SS) required: true - in: path name: id schema: type: string required: true - in: query name: provider schema: type: string enum: - apple - google - ics - internal - microsoft description: |- Filter by provider (internal = manual, others = synced) * `internal` - Internal Calendar * `google` - Google Calendar * `microsoft` - Microsoft Outlook Calendar * `apple` - Apple Calendar * `ics` - ICS - in: query name: start_datetime schema: type: string description: Start datetime in ISO format (YYYY-MM-DDTHH:MM:SS) required: true - in: query name: sync_enabled schema: type: boolean description: Filter by whether provider sync is enabled tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/UnavailableTimeWindow' description: '' /calendar/{id}/unavailable-windows{format}: get: operationId: calendar_unavailable_windows_formatted_list description: Get unavailable time windows for a calendar within a specified date range. summary: Get unavailable time windows parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: calendar_type schema: type: string enum: - bundle - personal - resource - virtual description: |- Filter by calendar type (e.g. resource) * `personal` - Personal Calendar * `resource` - Resource Calendar * `virtual` - Virtual Calendar * `bundle` - Bundle Calendar - in: query name: end_datetime schema: type: string description: End datetime in ISO format (YYYY-MM-DDTHH:MM:SS) required: true - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true - in: query name: provider schema: type: string enum: - apple - google - ics - internal - microsoft description: |- Filter by provider (internal = manual, others = synced) * `internal` - Internal Calendar * `google` - Google Calendar * `microsoft` - Microsoft Outlook Calendar * `apple` - Apple Calendar * `ics` - ICS - in: query name: start_datetime schema: type: string description: Start datetime in ISO format (YYYY-MM-DDTHH:MM:SS) required: true - in: query name: sync_enabled schema: type: boolean description: Filter by whether provider sync is enabled tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/UnavailableTimeWindow' description: '' /calendar/bundle/: post: operationId: calendar_bundle_create description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarBundleCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarBundleCreate' multipart/form-data: schema: $ref: '#/components/schemas/CalendarBundleCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' /calendar/bundle{format}: post: operationId: calendar_bundle_formatted_create description: ViewSet for managing calendars. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarBundleCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarBundleCreate' multipart/form-data: schema: $ref: '#/components/schemas/CalendarBundleCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' /calendar/default/: get: operationId: calendar_default_retrieve description: Returns the authenticated user's default calendar in their organization (the active CalendarOwnership flagged is_default). 404 when the user has no default calendar (e.g. before importing any calendars). summary: Get the caller's default calendar parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' '404': description: No default calendar for this user /calendar/default{format}: get: operationId: calendar_default_formatted_retrieve description: Returns the authenticated user's default calendar in their organization (the active CalendarOwnership flagged is_default). 404 when the user has no default calendar (e.g. before importing any calendars). summary: Get the caller's default calendar parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - calendar security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' '404': description: No default calendar for this user /calendar/request-import/: post: operationId: calendar_request_import_create description: Request import of external calendars for the authenticated user. summary: Request calendar import parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/Calendar' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Calendar' multipart/form-data: schema: $ref: '#/components/schemas/Calendar' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': content: application/json: schema: type: object properties: detail: type: string description: '' /calendar/request-import{format}: post: operationId: calendar_request_import_formatted_create description: Request import of external calendars for the authenticated user. summary: Request calendar import parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/Calendar' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Calendar' multipart/form-data: schema: $ref: '#/components/schemas/Calendar' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': content: application/json: schema: type: object properties: detail: type: string description: '' /calendar/resource/: post: operationId: calendar_resource_create description: Org admins create an internal (manual) resource calendar — a shared bookable resource (room, equipment, etc.) owned by the organization rather than synced from an external provider. Sets provider=internal and calendar_type=resource. Admin only. Returns the created calendar. summary: Create a manual resource calendar parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/ResourceCalendarCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ResourceCalendarCreate' multipart/form-data: schema: $ref: '#/components/schemas/ResourceCalendarCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' /calendar/resource{format}: post: operationId: calendar_resource_formatted_create description: Org admins create an internal (manual) resource calendar — a shared bookable resource (room, equipment, etc.) owned by the organization rather than synced from an external provider. Sets provider=internal and calendar_type=resource. Admin only. Returns the created calendar. summary: Create a manual resource calendar parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - calendar requestBody: content: application/json: schema: $ref: '#/components/schemas/ResourceCalendarCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ResourceCalendarCreate' multipart/form-data: schema: $ref: '#/components/schemas/ResourceCalendarCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/Calendar' description: '' /change-requests/: get: operationId: change_requests_list description: |- List and act on external-event change requests (approve / reject). **Eligibility scoping (GET /change-requests/):** - **Admins** see all change requests in their organization. - **Members** see only requests whose target event they attend (``EventAttendance`` row for their membership). **Default filter:** ``status=PENDING``. Pass ``?status=approved`` (or any valid status) to retrieve historical / resolved requests. **Approve (POST /change-requests/{id}/approve/):** Apply the proposed change locally. Returns the updated request (200). **Reject (POST /change-requests/{id}/reject/):** Push the retained value back to the external provider (GCal) and mark the request ``REJECTED``. Returns the updated request (200). **Error responses:** - ``403`` when the caller is not eligible to resolve the specific request. - ``409`` when the request is no longer ``PENDING``. - ``401`` when the caller is not authenticated. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: event schema: type: number description: Filter by CalendarEvent ID - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: status schema: type: string enum: - approved - auto_undone - pending - rejected - stale description: |- Filter by request status (default: pending) * `pending` - Pending * `approved` - Approved * `rejected` - Rejected * `stale` - Stale * `auto_undone` - Auto-undone tags: - External Event Change Requests security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedExternalEventChangeRequestList' description: '' /change-requests{format}: get: operationId: change_requests_formatted_list description: |- List and act on external-event change requests (approve / reject). **Eligibility scoping (GET /change-requests/):** - **Admins** see all change requests in their organization. - **Members** see only requests whose target event they attend (``EventAttendance`` row for their membership). **Default filter:** ``status=PENDING``. Pass ``?status=approved`` (or any valid status) to retrieve historical / resolved requests. **Approve (POST /change-requests/{id}/approve/):** Apply the proposed change locally. Returns the updated request (200). **Reject (POST /change-requests/{id}/reject/):** Push the retained value back to the external provider (GCal) and mark the request ``REJECTED``. Returns the updated request (200). **Error responses:** - ``403`` when the caller is not eligible to resolve the specific request. - ``409`` when the request is no longer ``PENDING``. - ``401`` when the caller is not authenticated. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: event schema: type: number description: Filter by CalendarEvent ID - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: status schema: type: string enum: - approved - auto_undone - pending - rejected - stale description: |- Filter by request status (default: pending) * `pending` - Pending * `approved` - Approved * `rejected` - Rejected * `stale` - Stale * `auto_undone` - Auto-undone tags: - External Event Change Requests security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedExternalEventChangeRequestList' description: '' /change-requests/{id}/: get: operationId: change_requests_retrieve description: |- List and act on external-event change requests (approve / reject). **Eligibility scoping (GET /change-requests/):** - **Admins** see all change requests in their organization. - **Members** see only requests whose target event they attend (``EventAttendance`` row for their membership). **Default filter:** ``status=PENDING``. Pass ``?status=approved`` (or any valid status) to retrieve historical / resolved requests. **Approve (POST /change-requests/{id}/approve/):** Apply the proposed change locally. Returns the updated request (200). **Reject (POST /change-requests/{id}/reject/):** Push the retained value back to the external provider (GCal) and mark the request ``REJECTED``. Returns the updated request (200). **Error responses:** - ``403`` when the caller is not eligible to resolve the specific request. - ``409`` when the request is no longer ``PENDING``. - ``401`` when the caller is not authenticated. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - External Event Change Requests security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ExternalEventChangeRequest' description: '' /change-requests/{id}{format}: get: operationId: change_requests_formatted_retrieve description: |- List and act on external-event change requests (approve / reject). **Eligibility scoping (GET /change-requests/):** - **Admins** see all change requests in their organization. - **Members** see only requests whose target event they attend (``EventAttendance`` row for their membership). **Default filter:** ``status=PENDING``. Pass ``?status=approved`` (or any valid status) to retrieve historical / resolved requests. **Approve (POST /change-requests/{id}/approve/):** Apply the proposed change locally. Returns the updated request (200). **Reject (POST /change-requests/{id}/reject/):** Push the retained value back to the external provider (GCal) and mark the request ``REJECTED``. Returns the updated request (200). **Error responses:** - ``403`` when the caller is not eligible to resolve the specific request. - ``409`` when the request is no longer ``PENDING``. - ``401`` when the caller is not authenticated. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - External Event Change Requests security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ExternalEventChangeRequest' description: '' /change-requests/{id}/approve/: post: operationId: change_requests_approve_create description: Apply the proposed change locally and mark the request APPROVED. Returns 403 when the caller is not eligible to resolve this request; 409 when the request is no longer PENDING. summary: Approve a change request parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - External Event Change Requests security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ExternalEventChangeRequest' description: '' '403': description: Caller is not eligible to resolve this request. '409': description: Request is no longer PENDING. /change-requests/{id}/approve{format}: post: operationId: change_requests_approve_formatted_create description: Apply the proposed change locally and mark the request APPROVED. Returns 403 when the caller is not eligible to resolve this request; 409 when the request is no longer PENDING. summary: Approve a change request parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - External Event Change Requests security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ExternalEventChangeRequest' description: '' '403': description: Caller is not eligible to resolve this request. '409': description: Request is no longer PENDING. /change-requests/{id}/reject/: post: operationId: change_requests_reject_create description: Push the retained value back to the external provider and mark the request REJECTED. Requires a valid social account for the event's calendar provider. Returns 403 when the caller is not eligible to resolve this request; 409 when the request is no longer PENDING. summary: Reject a change request parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - External Event Change Requests security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ExternalEventChangeRequest' description: '' '400': description: No social account for the calendar's provider or no calendar owner. '403': description: Caller is not eligible to resolve this request. '409': description: Request is no longer PENDING. /change-requests/{id}/reject{format}: post: operationId: change_requests_reject_formatted_create description: Push the retained value back to the external provider and mark the request REJECTED. Requires a valid social account for the event's calendar provider. Returns 403 when the caller is not eligible to resolve this request; 409 when the request is no longer PENDING. summary: Reject a change request parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - External Event Change Requests security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ExternalEventChangeRequest' description: '' '400': description: No social account for the calendar's provider or no calendar owner. '403': description: Caller is not eligible to resolve this request. '409': description: Request is no longer PENDING. /consents/: post: operationId: consents_create description: |- POST /consents/ — record acceptance of `document_type` for the current user. Authenticated only. Captures client IP + User-Agent for audit-grade proof; delegates version resolution + persistence to `ConsentService`. summary: Record the authenticated user's consent to a policy document type tags: - Legal requestBody: content: application/json: schema: $ref: '#/components/schemas/ConsentCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ConsentCreate' multipart/form-data: schema: $ref: '#/components/schemas/ConsentCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/UserConsent' description: '' '400': description: Invalid document_type, or no published document of that type yet /consents{format}: post: operationId: consents_formatted_create description: |- POST /consents/ — record acceptance of `document_type` for the current user. Authenticated only. Captures client IP + User-Agent for audit-grade proof; delegates version resolution + persistence to `ConsentService`. summary: Record the authenticated user's consent to a policy document type parameters: - in: path name: format schema: type: string enum: - .json required: true tags: - Legal requestBody: content: application/json: schema: $ref: '#/components/schemas/ConsentCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ConsentCreate' multipart/form-data: schema: $ref: '#/components/schemas/ConsentCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/UserConsent' description: '' '400': description: Invalid document_type, or no published document of that type yet /invitations/: get: operationId: invitations_list description: A viewset for managing organization invitations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: email schema: type: string description: Filter by partial email match - in: query name: invited_by schema: type: number description: Filter by inviter user ID - in: query name: is_accepted schema: type: boolean description: Filter by acceptance status - in: query name: is_expired schema: type: boolean description: Filter by expiration status - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - invitations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedOrganizationInvitationList' description: '' post: operationId: invitations_create description: A viewset for managing organization invitations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - invitations requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationInvitation' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationInvitation' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' description: '' '402': description: Organization is at its seat limit /invitations{format}: get: operationId: invitations_formatted_list description: A viewset for managing organization invitations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: email schema: type: string description: Filter by partial email match - in: path name: format schema: type: string enum: - .json required: true - in: query name: invited_by schema: type: number description: Filter by inviter user ID - in: query name: is_accepted schema: type: boolean description: Filter by acceptance status - in: query name: is_expired schema: type: boolean description: Filter by expiration status - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - invitations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedOrganizationInvitationList' description: '' post: operationId: invitations_formatted_create description: A viewset for managing organization invitations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - invitations requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationInvitation' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationInvitation' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' description: '' '402': description: Organization is at its seat limit /invitations/{id}/: get: operationId: invitations_retrieve description: A viewset for managing organization invitations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - invitations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' description: '' delete: operationId: invitations_destroy description: A viewset for managing organization invitations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - invitations security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /invitations/{id}{format}: get: operationId: invitations_formatted_retrieve description: A viewset for managing organization invitations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - invitations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' description: '' delete: operationId: invitations_formatted_destroy description: A viewset for managing organization invitations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - invitations security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /invitations/{id}/resend/: post: operationId: invitations_resend_create description: |- POST /invitations/{id}/resend/ — regenerate token and re-send a pending invitation. Guards: - Invitation must not be accepted (accepted_at is None). - User must be an active member of the invitation's organization. Returns the re-serialized invitation with the new token_hash and extended expires_at. summary: Resend a pending organization invitation parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - invitations requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationInvitation' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationInvitation' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' description: '' '400': description: Invitation already accepted or service error '402': description: Organization is at its seat limit '403': description: Not an active member '404': description: Invitation not found or cross-org /invitations/{id}/resend{format}: post: operationId: invitations_resend_formatted_create description: |- POST /invitations/{id}/resend/ — regenerate token and re-send a pending invitation. Guards: - Invitation must not be accepted (accepted_at is None). - User must be an active member of the invitation's organization. Returns the re-serialized invitation with the new token_hash and extended expires_at. summary: Resend a pending organization invitation parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - invitations requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationInvitation' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationInvitation' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationInvitation' description: '' '400': description: Invitation already accepted or service error '402': description: Organization is at its seat limit '403': description: Not an active member '404': description: Invitation not found or cross-org /invitations/accept: post: operationId: invitations_accept_create description: Public endpoint for accepting organization invitations. tags: - invitations requestBody: content: application/json: schema: $ref: '#/components/schemas/AcceptInvitation' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/AcceptInvitation' multipart/form-data: schema: $ref: '#/components/schemas/AcceptInvitation' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/AcceptInvitationResponse' description: Invitation accepted '400': description: Already a member, or invalid token '402': description: Organization is at its seat limit '404': description: Invitation not found '409': description: Duplicate invitation /notifications/: get: operationId: notifications_list description: 'Returns the authenticated user''s IN_APP notifications with status in (SENT, READ). Ordered by creation date (newest first). Paginated via page/page_size passthrough. Envelope: {results: [...], page: int, page_size: int, count: int}.' summary: List all in-app notifications for the authenticated user parameters: - in: query name: page schema: type: integer description: Page number (1-based). Defaults to 1. - in: query name: page_size schema: type: integer description: Number of items per page. Defaults to 10, max 100. tags: - Notifications security: - jwtAuth: [] - cookieAuth: [] responses: '200': description: 'Passthrough-paginated list of all in-app notifications. Envelope: {results: [...], page: int, page_size: int, count: int}.' '400': description: Invalid page / page_size parameter '401': description: Unauthenticated /notifications{format}: get: operationId: notifications_formatted_list description: 'Returns the authenticated user''s IN_APP notifications with status in (SENT, READ). Ordered by creation date (newest first). Paginated via page/page_size passthrough. Envelope: {results: [...], page: int, page_size: int, count: int}.' summary: List all in-app notifications for the authenticated user parameters: - in: path name: format schema: type: string enum: - .json required: true - in: query name: page schema: type: integer description: Page number (1-based). Defaults to 1. - in: query name: page_size schema: type: integer description: Number of items per page. Defaults to 10, max 100. tags: - Notifications security: - jwtAuth: [] - cookieAuth: [] responses: '200': description: 'Passthrough-paginated list of all in-app notifications. Envelope: {results: [...], page: int, page_size: int, count: int}.' '400': description: Invalid page / page_size parameter '401': description: Unauthenticated /notifications/{id}/mark-read/: post: operationId: notifications_mark_read_create description: 'Marks a single in-app notification as read for the authenticated user. Fully native + ownership-scoped via mark_read_bulk: an id that is missing, owned by another user, or in a non-SENT/READ state yields 404. Idempotent: an already-READ owned notification returns 200.' summary: Mark a notification as read parameters: - in: path name: id schema: type: string required: true tags: - Notifications requestBody: content: application/json: schema: $ref: '#/components/schemas/Notification' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Notification' multipart/form-data: schema: $ref: '#/components/schemas/Notification' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Notification' description: Notification marked as read. Returns the updated notification. '401': description: Unauthenticated '404': description: Notification not found or not owned by the authenticated user /notifications/{id}/mark-read{format}: post: operationId: notifications_mark_read_formatted_create description: 'Marks a single in-app notification as read for the authenticated user. Fully native + ownership-scoped via mark_read_bulk: an id that is missing, owned by another user, or in a non-SENT/READ state yields 404. Idempotent: an already-READ owned notification returns 200.' summary: Mark a notification as read parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - Notifications requestBody: content: application/json: schema: $ref: '#/components/schemas/Notification' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Notification' multipart/form-data: schema: $ref: '#/components/schemas/Notification' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Notification' description: Notification marked as read. Returns the updated notification. '401': description: Unauthenticated '404': description: Notification not found or not owned by the authenticated user /notifications/mark-read-bulk/: post: operationId: notifications_mark_read_bulk_create description: 'Marks multiple in-app notifications as read for the authenticated user. Ownership-scoped: notifications belonging to other users in the id list are silently skipped (no IDOR error). Idempotent: already-READ ids are returned in the results without error. Non-existent ids are silently skipped.' summary: Mark multiple notifications as read (bulk) tags: - Notifications requestBody: content: application/json: schema: $ref: '#/components/schemas/BulkMarkRead' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BulkMarkRead' multipart/form-data: schema: $ref: '#/components/schemas/BulkMarkRead' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': description: 'Notifications marked as read. Envelope: {results: [...serialized notifications that are READ after the op...]}.' '400': description: Invalid request body (empty or missing ids list) '401': description: Unauthenticated /notifications/mark-read-bulk{format}: post: operationId: notifications_mark_read_bulk_formatted_create description: 'Marks multiple in-app notifications as read for the authenticated user. Ownership-scoped: notifications belonging to other users in the id list are silently skipped (no IDOR error). Idempotent: already-READ ids are returned in the results without error. Non-existent ids are silently skipped.' summary: Mark multiple notifications as read (bulk) parameters: - in: path name: format schema: type: string enum: - .json required: true tags: - Notifications requestBody: content: application/json: schema: $ref: '#/components/schemas/BulkMarkRead' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/BulkMarkRead' multipart/form-data: schema: $ref: '#/components/schemas/BulkMarkRead' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': description: 'Notifications marked as read. Envelope: {results: [...serialized notifications that are READ after the op...]}.' '400': description: Invalid request body (empty or missing ids list) '401': description: Unauthenticated /notifications/unread/: get: operationId: notifications_unread_retrieve description: |- GET /notifications/unread/ — unread in-app notifications for the current user. Uses the native vintasend NotificationService.get_in_app_unread(user_id, page, page_size) which returns an Iterable of vintasend Notification dataclasses (not an ORM queryset). Count comes from get_in_app_unread_count(user_id). Passthrough pagination: the caller controls page + page_size. summary: List unread in-app notifications for the authenticated user parameters: - in: query name: page schema: type: integer description: Page number (1-based). Defaults to 1. - in: query name: page_size schema: type: integer description: Number of items per page. Defaults to 10. tags: - Notifications security: - jwtAuth: [] - cookieAuth: [] responses: '200': description: 'Passthrough-paginated list of unread notifications. Envelope: {results: [...], page: int, page_size: int, count: int}. count reflects the total number of unread notifications for the user.' '400': description: Invalid page / page_size parameter '401': description: Unauthenticated /notifications/unread{format}: get: operationId: notifications_unread_formatted_retrieve description: |- GET /notifications/unread/ — unread in-app notifications for the current user. Uses the native vintasend NotificationService.get_in_app_unread(user_id, page, page_size) which returns an Iterable of vintasend Notification dataclasses (not an ORM queryset). Count comes from get_in_app_unread_count(user_id). Passthrough pagination: the caller controls page + page_size. summary: List unread in-app notifications for the authenticated user parameters: - in: path name: format schema: type: string enum: - .json required: true - in: query name: page schema: type: integer description: Page number (1-based). Defaults to 1. - in: query name: page_size schema: type: integer description: Number of items per page. Defaults to 10. tags: - Notifications security: - jwtAuth: [] - cookieAuth: [] responses: '200': description: 'Passthrough-paginated list of unread notifications. Envelope: {results: [...], page: int, page_size: int, count: int}. count reflects the total number of unread notifications for the user.' '400': description: Invalid page / page_size parameter '401': description: Unauthenticated /organization-members/: get: operationId: organization_members_list description: |- A viewset for listing, retrieving, and managing organization members. Admin-only endpoint — lists both active and inactive members of the caller's organization, suitable for a datatable view. Non-admin members get 403. Actions: - `deactivate`: POST to disable a member (prevent self-deactivation and protect the last active admin). - `reactivate`: POST to re-enable a member. - `update-role`: POST to change a member's role (protect the last active admin). parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: email schema: type: string description: Filter by partial email match - in: query name: first_name schema: type: string description: Filter by partial first name match - in: query name: last_name schema: type: string description: Filter by partial last name match - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: search schema: type: string description: Search by first name, last name, or email (OR) tags: - organization-members security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedOrganizationMembershipList' description: '' /organization-members{format}: get: operationId: organization_members_formatted_list description: |- A viewset for listing, retrieving, and managing organization members. Admin-only endpoint — lists both active and inactive members of the caller's organization, suitable for a datatable view. Non-admin members get 403. Actions: - `deactivate`: POST to disable a member (prevent self-deactivation and protect the last active admin). - `reactivate`: POST to re-enable a member. - `update-role`: POST to change a member's role (protect the last active admin). parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: query name: email schema: type: string description: Filter by partial email match - in: query name: first_name schema: type: string description: Filter by partial first name match - in: path name: format schema: type: string enum: - .json required: true - in: query name: last_name schema: type: string description: Filter by partial last name match - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: query name: search schema: type: string description: Search by first name, last name, or email (OR) tags: - organization-members security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedOrganizationMembershipList' description: '' /organization-members/{user_id}/: get: operationId: organization_members_retrieve description: |- A viewset for listing, retrieving, and managing organization members. Admin-only endpoint — lists both active and inactive members of the caller's organization, suitable for a datatable view. Non-admin members get 403. Actions: - `deactivate`: POST to disable a member (prevent self-deactivation and protect the last active admin). - `reactivate`: POST to re-enable a member. - `update-role`: POST to change a member's role (protect the last active admin). parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: user_id schema: type: string required: true tags: - organization-members security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' description: '' /organization-members/{user_id}{format}: get: operationId: organization_members_formatted_retrieve description: |- A viewset for listing, retrieving, and managing organization members. Admin-only endpoint — lists both active and inactive members of the caller's organization, suitable for a datatable view. Non-admin members get 403. Actions: - `deactivate`: POST to disable a member (prevent self-deactivation and protect the last active admin). - `reactivate`: POST to re-enable a member. - `update-role`: POST to change a member's role (protect the last active admin). parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: user_id schema: type: string required: true tags: - organization-members security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' description: '' /organization-members/{user_id}/deactivate/: post: operationId: organization_members_deactivate_create description: |- Deactivate a member (set is_active=False). Guards: - Cannot deactivate own membership (self-lockout prevention). - Cannot deactivate the last active admin (org lockout prevention). Idempotency: deactivating an already-inactive member is a no-op success. summary: Deactivate an organization member parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: user_id schema: type: string required: true tags: - organization-members requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationMembership' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationMembership' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' description: '' '400': description: Cannot deactivate self or last active admin '403': description: Not an admin '404': description: Member not found or cross-org /organization-members/{user_id}/deactivate{format}: post: operationId: organization_members_deactivate_formatted_create description: |- Deactivate a member (set is_active=False). Guards: - Cannot deactivate own membership (self-lockout prevention). - Cannot deactivate the last active admin (org lockout prevention). Idempotency: deactivating an already-inactive member is a no-op success. summary: Deactivate an organization member parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: user_id schema: type: string required: true tags: - organization-members requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationMembership' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationMembership' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' description: '' '400': description: Cannot deactivate self or last active admin '403': description: Not an admin '404': description: Member not found or cross-org /organization-members/{user_id}/reactivate/: post: operationId: organization_members_reactivate_create description: |- Reactivate a member (set is_active=True). The seat-limit check lives in ``OrganizationService.reactivate_membership`` (the service layer, not the viewset), so this action only resolves the target and serializes the result. Idempotency: reactivating an already-active member is a no-op success. summary: Reactivate an organization member parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: user_id schema: type: string required: true tags: - organization-members requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationMembership' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationMembership' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' description: '' '402': description: Organization is at its seat limit '403': description: Not an admin '404': description: Member not found or cross-org /organization-members/{user_id}/reactivate{format}: post: operationId: organization_members_reactivate_formatted_create description: |- Reactivate a member (set is_active=True). The seat-limit check lives in ``OrganizationService.reactivate_membership`` (the service layer, not the viewset), so this action only resolves the target and serializes the result. Idempotency: reactivating an already-active member is a no-op success. summary: Reactivate an organization member parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: user_id schema: type: string required: true tags: - organization-members requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/OrganizationMembership' multipart/form-data: schema: $ref: '#/components/schemas/OrganizationMembership' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' description: '' '402': description: Organization is at its seat limit '403': description: Not an admin '404': description: Member not found or cross-org /organization-members/{user_id}/update-role/: post: operationId: organization_members_update_role_create description: |- Update a member's role (member <-> admin). Guards: - Cannot demote the last active admin (org lockout prevention). Idempotency: setting the role to its current value is a no-op success. summary: Update an organization member's role parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: user_id schema: type: string required: true tags: - organization-members requestBody: content: application/json: schema: $ref: '#/components/schemas/UpdateMembershipRole' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/UpdateMembershipRole' multipart/form-data: schema: $ref: '#/components/schemas/UpdateMembershipRole' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' description: '' '400': description: Invalid role or would demote the last active admin '403': description: Not an admin '404': description: Member not found or cross-org /organization-members/{user_id}/update-role{format}: post: operationId: organization_members_update_role_formatted_create description: |- Update a member's role (member <-> admin). Guards: - Cannot demote the last active admin (org lockout prevention). Idempotency: setting the role to its current value is a no-op success. summary: Update an organization member's role parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: user_id schema: type: string required: true tags: - organization-members requestBody: content: application/json: schema: $ref: '#/components/schemas/UpdateMembershipRole' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/UpdateMembershipRole' multipart/form-data: schema: $ref: '#/components/schemas/UpdateMembershipRole' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/OrganizationMembership' description: '' '400': description: Invalid role or would demote the last active admin '403': description: Not an admin '404': description: Member not found or cross-org /organizations/: post: operationId: organizations_create description: |- Create a new organization for the authenticated user. Overrides ``CreateModelMixin.create`` to handle the post-write refetch correctly for members who already have one or more memberships. We skip the base mixin's post-write ``_resolve_active_organization`` call entirely. For the ``create`` action — exempted via ``active_org_optional_actions = ("mine", "create")`` — that re-resolve would leave a multi-org caller with no ``X-Organization-Id`` header resolved to ``None``, making ``get_queryset`` return nothing and causing the re-fetch to raise ``DoesNotExist`` / 500. Instead, after ``perform_create`` we look up the just-created membership directly and stash it on the request so the re-fetch (via ``get_queryset``) is scoped to the new organization. tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/Organization' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Organization' multipart/form-data: schema: $ref: '#/components/schemas/Organization' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' /organizations{format}: post: operationId: organizations_formatted_create description: |- Create a new organization for the authenticated user. Overrides ``CreateModelMixin.create`` to handle the post-write refetch correctly for members who already have one or more memberships. We skip the base mixin's post-write ``_resolve_active_organization`` call entirely. For the ``create`` action — exempted via ``active_org_optional_actions = ("mine", "create")`` — that re-resolve would leave a multi-org caller with no ``X-Organization-Id`` header resolved to ``None``, making ``get_queryset`` return nothing and causing the re-fetch to raise ``DoesNotExist`` / 500. Instead, after ``perform_create`` we look up the just-created membership directly and stash it on the request so the re-fetch (via ``get_queryset``) is scoped to the new organization. parameters: - in: path name: format schema: type: string enum: - .json required: true tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/Organization' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Organization' multipart/form-data: schema: $ref: '#/components/schemas/Organization' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' /organizations/{id}/: get: operationId: organizations_retrieve description: A viewset for managing organizations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - organizations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' put: operationId: organizations_update description: |- Override update to: 1. Upsert the org's ``GoogleCalendarServiceAccount`` when ``google_service_account`` is present in the request body (create-or-update, one per org, calendar FK=None). 2. Trigger rooms sync when ``should_sync_rooms`` flips False→True — but only when a service account is configured (either already stored or just provided in this PATCH). If the flag is being enabled and no service account is configured (neither stored nor in the request), return **400** so the admin knows to configure first. Uses select_for_update to lock the row during snapshot + write, serializing concurrent PATCHes and preventing double-fire of the sync on False→True transition. The creds check is performed BEFORE any write so that unrelated field changes (e.g. renaming the org) are NOT persisted when the 400 is returned. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/Organization' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Organization' multipart/form-data: schema: $ref: '#/components/schemas/Organization' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' patch: operationId: organizations_partial_update description: A viewset for managing organizations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedOrganization' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedOrganization' multipart/form-data: schema: $ref: '#/components/schemas/PatchedOrganization' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' delete: operationId: organizations_destroy description: A viewset for managing organizations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - organizations security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /organizations/{id}{format}: get: operationId: organizations_formatted_retrieve description: A viewset for managing organizations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - organizations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' put: operationId: organizations_formatted_update description: |- Override update to: 1. Upsert the org's ``GoogleCalendarServiceAccount`` when ``google_service_account`` is present in the request body (create-or-update, one per org, calendar FK=None). 2. Trigger rooms sync when ``should_sync_rooms`` flips False→True — but only when a service account is configured (either already stored or just provided in this PATCH). If the flag is being enabled and no service account is configured (neither stored nor in the request), return **400** so the admin knows to configure first. Uses select_for_update to lock the row during snapshot + write, serializing concurrent PATCHes and preventing double-fire of the sync on False→True transition. The creds check is performed BEFORE any write so that unrelated field changes (e.g. renaming the org) are NOT persisted when the 400 is returned. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/Organization' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Organization' multipart/form-data: schema: $ref: '#/components/schemas/Organization' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' patch: operationId: organizations_formatted_partial_update description: A viewset for managing organizations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedOrganization' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedOrganization' multipart/form-data: schema: $ref: '#/components/schemas/PatchedOrganization' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' delete: operationId: organizations_formatted_destroy description: A viewset for managing organizations. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - organizations security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /organizations/{id}/sync-calendars/: post: operationId: organizations_sync_calendars_create description: |- POST /organizations/{id}/sync-calendars/ — enqueue a sync of all calendars. Each active calendar in the organization is synced using its owner's linked account. Calendars without an owner or a linked provider account are reported under ``skipped`` rather than failing the whole request. Body (``CalendarSyncRequestSerializer``): ``start_datetime``, ``end_datetime`` (required ISO 8601) and ``should_update_events``. Returns HTTP 202 with ``{"synced": [...], "skipped": [...]}``. summary: Trigger a sync of every calendar in the organization parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarSyncRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarSyncRequest' multipart/form-data: schema: $ref: '#/components/schemas/CalendarSyncRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': description: 'Sync enqueued. Body: {synced: [calendar_id, ...], skipped: [{calendar_id, reason}, ...]}.' '400': description: Invalid sync window '403': description: Not an admin '404': description: Organization not found /organizations/{id}/sync-calendars{format}: post: operationId: organizations_sync_calendars_formatted_create description: |- POST /organizations/{id}/sync-calendars/ — enqueue a sync of all calendars. Each active calendar in the organization is synced using its owner's linked account. Calendars without an owner or a linked provider account are reported under ``skipped`` rather than failing the whole request. Body (``CalendarSyncRequestSerializer``): ``start_datetime``, ``end_datetime`` (required ISO 8601) and ``should_update_events``. Returns HTTP 202 with ``{"synced": [...], "skipped": [...]}``. summary: Trigger a sync of every calendar in the organization parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarSyncRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarSyncRequest' multipart/form-data: schema: $ref: '#/components/schemas/CalendarSyncRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': description: 'Sync enqueued. Body: {synced: [calendar_id, ...], skipped: [{calendar_id, reason}, ...]}.' '400': description: Invalid sync window '403': description: Not an admin '404': description: Organization not found /organizations/{id}/sync-rooms/: post: operationId: organizations_sync_rooms_create description: |- POST /organizations/{id}/sync-rooms/ — enqueue a calendar resources import. Optional body fields: - ``start_time``: ISO 8601 datetime for the import window start. - ``end_time``: ISO 8601 datetime for the import window end. Defaults (when omitted): ``start_time=now``, ``end_time=now+365d``. Returns HTTP 202 on success. summary: Trigger a rooms/resources import for the organization parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/Organization' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Organization' multipart/form-data: schema: $ref: '#/components/schemas/Organization' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' '400': description: Invalid datetime format '403': description: Not an admin '404': description: Organization not found /organizations/{id}/sync-rooms{format}: post: operationId: organizations_sync_rooms_formatted_create description: |- POST /organizations/{id}/sync-rooms/ — enqueue a calendar resources import. Optional body fields: - ``start_time``: ISO 8601 datetime for the import window start. - ``end_time``: ISO 8601 datetime for the import window end. Defaults (when omitted): ``start_time=now``, ``end_time=now+365d``. Returns HTTP 202 on success. summary: Trigger a rooms/resources import for the organization parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - organizations requestBody: content: application/json: schema: $ref: '#/components/schemas/Organization' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Organization' multipart/form-data: schema: $ref: '#/components/schemas/Organization' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '202': content: application/json: schema: $ref: '#/components/schemas/Organization' description: '' '400': description: Invalid datetime format '403': description: Not an admin '404': description: Organization not found /organizations/current/: get: operationId: organizations_current_retrieve description: |- Return the caller's organization and role. HTTP 200 — the user is onboarded (has a membership). HTTP 404 — the user is gated (no membership yet). summary: Current organization + role for the authenticated user parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - organizations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CurrentMembership' description: '' '404': description: No organization membership (gated user) /organizations/current{format}: get: operationId: organizations_current_formatted_retrieve description: |- Return the caller's organization and role. HTTP 200 — the user is onboarded (has a membership). HTTP 404 — the user is gated (no membership yet). summary: Current organization + role for the authenticated user parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - organizations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/CurrentMembership' description: '' '404': description: No organization membership (gated user) /organizations/mine/: get: operationId: organizations_mine_list description: |- Return all active memberships for the authenticated caller. Designed for the frontend org switcher: the client calls this endpoint *before* it knows which ``X-Organization-Id`` to send, so no header is required. The response is always HTTP 200; gated users receive an empty list (``[]``). summary: List the authenticated user's active organization memberships tags: - organizations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/MyMembership' description: '' /organizations/mine{format}: get: operationId: organizations_mine_formatted_list description: |- Return all active memberships for the authenticated caller. Designed for the frontend org switcher: the client calls this endpoint *before* it knows which ``X-Organization-Id`` to send, so no header is required. The response is always HTTP 200; gated users receive an empty list (``[]``). summary: List the authenticated user's active organization memberships parameters: - in: path name: format schema: type: string enum: - .json required: true tags: - organizations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/MyMembership' description: '' /payments/{id}/payment-update/{provider}/: post: operationId: payments_payment_update_create description: This endpoint is used to receive payment updates from a payment provider. summary: Receive payment updates parameters: - in: path name: id schema: type: string required: true - in: path name: provider schema: type: string required: true tags: - payments security: - {} responses: '200': content: application/json: schema: description: Payment update received. description: '' '400': content: application/json: schema: description: Malformed payload. description: '' '403': content: application/json: schema: description: Invalid or missing signature. description: '' '404': content: application/json: schema: description: Unknown payment provider. description: '' /payments/{id}/payment-update/{provider}{format}: post: operationId: payments_payment_update_formatted_create description: This endpoint is used to receive payment updates from a payment provider. summary: Receive payment updates parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true - in: path name: provider schema: type: string required: true tags: - payments security: - {} responses: '200': content: application/json: schema: description: Payment update received. description: '' '400': content: application/json: schema: description: Malformed payload. description: '' '403': content: application/json: schema: description: Invalid or missing signature. description: '' '404': content: application/json: schema: description: Unknown payment provider. description: '' /payments/{id}/subscription-payment-update/{provider}/: post: operationId: payments_subscription_payment_update_create description: This endpoint is used to receive subscription payment updates from a payment provider. summary: Receive subscription payment updates parameters: - in: path name: id schema: type: string required: true - in: path name: provider schema: type: string required: true tags: - payments security: - {} responses: '200': content: application/json: schema: description: Subscription payment update received. description: '' '400': content: application/json: schema: description: Malformed payload. description: '' '403': content: application/json: schema: description: Invalid or missing signature. description: '' '404': content: application/json: schema: description: Unknown payment provider. description: '' /payments/{id}/subscription-payment-update/{provider}{format}: post: operationId: payments_subscription_payment_update_formatted_create description: This endpoint is used to receive subscription payment updates from a payment provider. summary: Receive subscription payment updates parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true - in: path name: provider schema: type: string required: true tags: - payments security: - {} responses: '200': content: application/json: schema: description: Subscription payment update received. description: '' '400': content: application/json: schema: description: Malformed payload. description: '' '403': content: application/json: schema: description: Invalid or missing signature. description: '' '404': content: application/json: schema: description: Unknown payment provider. description: '' /policy-documents/: get: operationId: policy_documents_list description: |- Read-only REST surface for policy documents. ``PolicyDocument`` is a global, non-tenant-scoped model (privacy policy, terms of use, SMS-messaging consent). This viewset intentionally does not build on the ``*VintaScheduleModelViewSet`` family: those bases mix in ``TenantScopedViewMixin`` (irrelevant — no organization here) and ``GenericVirtualModelViewMixin`` (requires a ``VirtualModelSerializer`` with a ``virtual_model``, which this flat, no-N+1 serializer doesn't need). A plain DRF ``ReadOnlyModelViewSet`` mirrors the existing precedent in ``organizations.views.ServiceAccountViewSet`` for this shape. Auth split: - ``latest`` / ``latest_by_type`` are **public** (``AllowAny``) — the frontend must be able to render policy text before a session exists (mid-signup, pre-OAuth-completion). - ``list`` (full history) / ``retrieve`` (by id) require authentication — these expose the full version history rather than just the currently-relevant text, so they stay behind the default auth gate. No write surface is exposed anywhere on this viewset. parameters: - in: query name: document_type schema: type: string enum: - privacy_policy - sms_consent - terms_of_use description: |- Filter by document type * `privacy_policy` - Privacy Policy * `terms_of_use` - Terms of Use * `sms_consent` - SMS Messaging Consent - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - Legal security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedPolicyDocumentList' description: '' /policy-documents{format}: get: operationId: policy_documents_formatted_list description: |- Read-only REST surface for policy documents. ``PolicyDocument`` is a global, non-tenant-scoped model (privacy policy, terms of use, SMS-messaging consent). This viewset intentionally does not build on the ``*VintaScheduleModelViewSet`` family: those bases mix in ``TenantScopedViewMixin`` (irrelevant — no organization here) and ``GenericVirtualModelViewMixin`` (requires a ``VirtualModelSerializer`` with a ``virtual_model``, which this flat, no-N+1 serializer doesn't need). A plain DRF ``ReadOnlyModelViewSet`` mirrors the existing precedent in ``organizations.views.ServiceAccountViewSet`` for this shape. Auth split: - ``latest`` / ``latest_by_type`` are **public** (``AllowAny``) — the frontend must be able to render policy text before a session exists (mid-signup, pre-OAuth-completion). - ``list`` (full history) / ``retrieve`` (by id) require authentication — these expose the full version history rather than just the currently-relevant text, so they stay behind the default auth gate. No write surface is exposed anywhere on this viewset. parameters: - in: query name: document_type schema: type: string enum: - privacy_policy - sms_consent - terms_of_use description: |- Filter by document type * `privacy_policy` - Privacy Policy * `terms_of_use` - Terms of Use * `sms_consent` - SMS Messaging Consent - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - Legal security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedPolicyDocumentList' description: '' /policy-documents/{id}/: get: operationId: policy_documents_retrieve description: |- Read-only REST surface for policy documents. ``PolicyDocument`` is a global, non-tenant-scoped model (privacy policy, terms of use, SMS-messaging consent). This viewset intentionally does not build on the ``*VintaScheduleModelViewSet`` family: those bases mix in ``TenantScopedViewMixin`` (irrelevant — no organization here) and ``GenericVirtualModelViewMixin`` (requires a ``VirtualModelSerializer`` with a ``virtual_model``, which this flat, no-N+1 serializer doesn't need). A plain DRF ``ReadOnlyModelViewSet`` mirrors the existing precedent in ``organizations.views.ServiceAccountViewSet`` for this shape. Auth split: - ``latest`` / ``latest_by_type`` are **public** (``AllowAny``) — the frontend must be able to render policy text before a session exists (mid-signup, pre-OAuth-completion). - ``list`` (full history) / ``retrieve`` (by id) require authentication — these expose the full version history rather than just the currently-relevant text, so they stay behind the default auth gate. No write surface is exposed anywhere on this viewset. parameters: - in: path name: id schema: type: string required: true tags: - Legal security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PolicyDocument' description: '' /policy-documents/{id}{format}: get: operationId: policy_documents_formatted_retrieve description: |- Read-only REST surface for policy documents. ``PolicyDocument`` is a global, non-tenant-scoped model (privacy policy, terms of use, SMS-messaging consent). This viewset intentionally does not build on the ``*VintaScheduleModelViewSet`` family: those bases mix in ``TenantScopedViewMixin`` (irrelevant — no organization here) and ``GenericVirtualModelViewMixin`` (requires a ``VirtualModelSerializer`` with a ``virtual_model``, which this flat, no-N+1 serializer doesn't need). A plain DRF ``ReadOnlyModelViewSet`` mirrors the existing precedent in ``organizations.views.ServiceAccountViewSet`` for this shape. Auth split: - ``latest`` / ``latest_by_type`` are **public** (``AllowAny``) — the frontend must be able to render policy text before a session exists (mid-signup, pre-OAuth-completion). - ``list`` (full history) / ``retrieve`` (by id) require authentication — these expose the full version history rather than just the currently-relevant text, so they stay behind the default auth gate. No write surface is exposed anywhere on this viewset. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - Legal security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PolicyDocument' description: '' /policy-documents/latest/: get: operationId: policy_documents_latest_list description: |- GET /policy-documents/latest/ — one row per document_type (highest version). Public — no authentication required. summary: List the latest published version of each policy document type parameters: - in: query name: document_type schema: type: string enum: - privacy_policy - sms_consent - terms_of_use description: |- Filter by document type * `privacy_policy` - Privacy Policy * `terms_of_use` - Terms of Use * `sms_consent` - SMS Messaging Consent tags: - Legal security: - jwtAuth: [] - cookieAuth: [] - {} responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/PolicyDocument' description: '' /policy-documents/latest{format}: get: operationId: policy_documents_latest_formatted_list description: |- GET /policy-documents/latest/ — one row per document_type (highest version). Public — no authentication required. summary: List the latest published version of each policy document type parameters: - in: query name: document_type schema: type: string enum: - privacy_policy - sms_consent - terms_of_use description: |- Filter by document type * `privacy_policy` - Privacy Policy * `terms_of_use` - Terms of Use * `sms_consent` - SMS Messaging Consent - in: path name: format schema: type: string enum: - .json required: true tags: - Legal security: - jwtAuth: [] - cookieAuth: [] - {} responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/PolicyDocument' description: '' /policy-documents/latest/{document_type}/: get: operationId: policy_documents_latest_retrieve description: |- GET /policy-documents/latest/{document_type}/ — highest version of one type. Public — no authentication required. 404s on an unknown enum value or a type with no published rows yet. summary: Retrieve the latest published version of a single document type parameters: - in: path name: document_type schema: type: string description: One of PolicyDocumentType's values (e.g. sms_consent). required: true tags: - Legal security: - jwtAuth: [] - cookieAuth: [] - {} responses: '200': content: application/json: schema: $ref: '#/components/schemas/PolicyDocument' description: '' '404': description: Unknown document_type, or no published version yet /policy-documents/latest/{document_type}{format}: get: operationId: policy_documents_latest_formatted_retrieve description: |- GET /policy-documents/latest/{document_type}/ — highest version of one type. Public — no authentication required. 404s on an unknown enum value or a type with no published rows yet. summary: Retrieve the latest published version of a single document type parameters: - in: path name: document_type schema: type: string description: One of PolicyDocumentType's values (e.g. sms_consent). required: true - in: path name: format schema: type: string enum: - .json required: true tags: - Legal security: - jwtAuth: [] - cookieAuth: [] - {} responses: '200': content: application/json: schema: $ref: '#/components/schemas/PolicyDocument' description: '' '404': description: Unknown document_type, or no published version yet /profile/{user}/: get: operationId: profile_retrieve description: Retrieve the profile of the currently authenticated user or a specific user. parameters: - in: path name: user schema: type: string description: User ID to retrieve or update the profile. Use 'me' to refer to the currently authenticated user. required: true tags: - profile security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Profile' description: '' put: operationId: profile_update parameters: - in: path name: user schema: type: string description: User ID to update the profile. Use 'me' to refer to the currently authenticated user. required: true tags: - profile requestBody: content: application/json: schema: $ref: '#/components/schemas/Profile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Profile' multipart/form-data: schema: $ref: '#/components/schemas/Profile' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Profile' description: '' patch: operationId: profile_partial_update parameters: - in: path name: user schema: type: string description: User ID to update the profile. Use 'me' to refer to the currently authenticated user. required: true tags: - profile requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedProfile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedProfile' multipart/form-data: schema: $ref: '#/components/schemas/PatchedProfile' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Profile' description: '' /profile/{user}{format}: get: operationId: profile_formatted_retrieve description: Retrieve the profile of the currently authenticated user or a specific user. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: user schema: type: string description: User ID to retrieve or update the profile. Use 'me' to refer to the currently authenticated user. required: true tags: - profile security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Profile' description: '' put: operationId: profile_formatted_update parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: user schema: type: string description: User ID to update the profile. Use 'me' to refer to the currently authenticated user. required: true tags: - profile requestBody: content: application/json: schema: $ref: '#/components/schemas/Profile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Profile' multipart/form-data: schema: $ref: '#/components/schemas/Profile' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Profile' description: '' patch: operationId: profile_formatted_partial_update parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: user schema: type: string description: User ID to update the profile. Use 'me' to refer to the currently authenticated user. required: true tags: - profile requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedProfile' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedProfile' multipart/form-data: schema: $ref: '#/components/schemas/PatchedProfile' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/Profile' description: '' /profile/{user}/profile-picture-upload-params/: post: operationId: profile_profile_picture_upload_params_create description: Returns the parameters needed to upload a profile picture directly to S3. Only works for your own profile. summary: Get S3 upload params for profile picture parameters: - in: path name: user schema: type: string description: User ID. Use 'me' to refer to the currently authenticated user. required: true tags: - profile requestBody: content: application/json: schema: $ref: '#/components/schemas/ProfilePictureUploadParamsRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ProfilePictureUploadParamsRequest' multipart/form-data: schema: $ref: '#/components/schemas/ProfilePictureUploadParamsRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ProfilePictureUploadParams' description: '' /profile/{user}/profile-picture-upload-params{format}: post: operationId: profile_profile_picture_upload_params_formatted_create description: Returns the parameters needed to upload a profile picture directly to S3. Only works for your own profile. summary: Get S3 upload params for profile picture parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: user schema: type: string description: User ID. Use 'me' to refer to the currently authenticated user. required: true tags: - profile requestBody: content: application/json: schema: $ref: '#/components/schemas/ProfilePictureUploadParamsRequest' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ProfilePictureUploadParamsRequest' multipart/form-data: schema: $ref: '#/components/schemas/ProfilePictureUploadParamsRequest' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ProfilePictureUploadParams' description: '' /public-api-docs/: get: operationId: public_api_docs_list description: GET /public-api-docs/ — one entry per allow-listed concept doc. summary: List the available concept docs tags: - Docs security: - {} responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/ConceptDocSummary' description: '' /public-api-docs{format}: get: operationId: public_api_docs_formatted_list description: GET /public-api-docs/ — one entry per allow-listed concept doc. summary: List the available concept docs parameters: - in: path name: format schema: type: string enum: - .json required: true tags: - Docs security: - {} responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/ConceptDocSummary' description: '' /public-api-docs/{slug}/: get: operationId: public_api_docs_retrieve description: |- GET /public-api-docs/{slug}/ — raw markdown for one concept doc. ``slug`` is looked up as a key of the allow-list built in ``public_api.docs_content``; never joined into a filesystem path. Any slug not a key of the allow-list — including path-traversal payloads — 404s via ``get_concept_doc``. summary: Retrieve a single concept doc's raw markdown parameters: - in: path name: slug schema: type: string pattern: ^[a-z0-9-]+$ required: true tags: - Docs security: - {} responses: '200': content: application/json: schema: $ref: '#/components/schemas/ConceptDoc' description: '' '404': description: Unknown slug (including any traversal attempt) /public-api-docs/{slug}{format}: get: operationId: public_api_docs_formatted_retrieve description: |- GET /public-api-docs/{slug}/ — raw markdown for one concept doc. ``slug`` is looked up as a key of the allow-list built in ``public_api.docs_content``; never joined into a filesystem path. Any slug not a key of the allow-list — including path-traversal payloads — 404s via ``get_concept_doc``. summary: Retrieve a single concept doc's raw markdown parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: slug schema: type: string pattern: ^[a-z0-9-]+$ required: true tags: - Docs security: - {} responses: '200': content: application/json: schema: $ref: '#/components/schemas/ConceptDoc' description: '' '404': description: Unknown slug (including any traversal attempt) /public-api-docs/webhook-events/: get: operationId: public_api_docs_webhook_events_list description: |- GET /public-api-docs/webhook-events/ — one entry per ``WebhookEventType`` member. Returned in enum declaration order. Must stay ``detail=False`` so the router registers it before the ``{slug}`` detail route. ``webhook-events`` is therefore a reserved slug that a concept doc may never use. summary: List the webhook event catalog tags: - Docs security: - {} responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/WebhookEventDoc' description: '' /public-api-docs/webhook-events{format}: get: operationId: public_api_docs_webhook_events_formatted_list description: |- GET /public-api-docs/webhook-events/ — one entry per ``WebhookEventType`` member. Returned in enum declaration order. Must stay ``detail=False`` so the router registers it before the ``{slug}`` detail route. ``webhook-events`` is therefore a reserved slug that a concept doc may never use. summary: List the webhook event catalog parameters: - in: path name: format schema: type: string enum: - .json required: true tags: - Docs security: - {} responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/WebhookEventDoc' description: '' /public-api-tokens/: get: operationId: public_api_tokens_list description: |- Admin-only viewset for managing public-API tokens (SystemUser + ResourceAccess rows). Supports create, list, retrieve, revoke, and editing resource grants. ``POST /public-api-tokens/`` creates a new ``SystemUser`` for the caller's organisation, persists the requested ``ResourceAccess`` rows, and returns the plaintext token **once**. The token is never recoverable after this response. ``GET /public-api-tokens/`` lists the caller's org tokens without secrets. ``GET /public-api-tokens/{id}/`` retrieves a single token without secrets. parameters: - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - public-api-tokens security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedSystemUserTokenList' description: '' post: operationId: public_api_tokens_create description: |- Create a SystemUser and ResourceAccess rows; return the plaintext token once. Returns HTTP 201 on success. The response body includes ``id``, ``integration_name``, ``is_active``, ``available_resources``, and a write-once ``token`` field — never ``long_lived_token_hash``. HTTP 400 is returned for: - Invalid or unknown ``available_resources`` values. - Empty ``available_resources`` list. - Duplicate ``integration_name`` (unique constraint). HTTP 403 is returned for non-admin callers; HTTP 401 for unauthenticated. tags: - public-api-tokens requestBody: content: application/json: schema: $ref: '#/components/schemas/SystemUserTokenCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/SystemUserTokenCreate' multipart/form-data: schema: $ref: '#/components/schemas/SystemUserTokenCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/SystemUserTokenResponse' description: '' /public-api-tokens{format}: get: operationId: public_api_tokens_formatted_list description: |- Admin-only viewset for managing public-API tokens (SystemUser + ResourceAccess rows). Supports create, list, retrieve, revoke, and editing resource grants. ``POST /public-api-tokens/`` creates a new ``SystemUser`` for the caller's organisation, persists the requested ``ResourceAccess`` rows, and returns the plaintext token **once**. The token is never recoverable after this response. ``GET /public-api-tokens/`` lists the caller's org tokens without secrets. ``GET /public-api-tokens/{id}/`` retrieves a single token without secrets. parameters: - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - public-api-tokens security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedSystemUserTokenList' description: '' post: operationId: public_api_tokens_formatted_create description: |- Create a SystemUser and ResourceAccess rows; return the plaintext token once. Returns HTTP 201 on success. The response body includes ``id``, ``integration_name``, ``is_active``, ``available_resources``, and a write-once ``token`` field — never ``long_lived_token_hash``. HTTP 400 is returned for: - Invalid or unknown ``available_resources`` values. - Empty ``available_resources`` list. - Duplicate ``integration_name`` (unique constraint). HTTP 403 is returned for non-admin callers; HTTP 401 for unauthenticated. parameters: - in: path name: format schema: type: string enum: - .json required: true tags: - public-api-tokens requestBody: content: application/json: schema: $ref: '#/components/schemas/SystemUserTokenCreate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/SystemUserTokenCreate' multipart/form-data: schema: $ref: '#/components/schemas/SystemUserTokenCreate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/SystemUserTokenResponse' description: '' /public-api-tokens/{id}/: get: operationId: public_api_tokens_retrieve description: |- Admin-only viewset for managing public-API tokens (SystemUser + ResourceAccess rows). Supports create, list, retrieve, revoke, and editing resource grants. ``POST /public-api-tokens/`` creates a new ``SystemUser`` for the caller's organisation, persists the requested ``ResourceAccess`` rows, and returns the plaintext token **once**. The token is never recoverable after this response. ``GET /public-api-tokens/`` lists the caller's org tokens without secrets. ``GET /public-api-tokens/{id}/`` retrieves a single token without secrets. parameters: - in: path name: id schema: type: string required: true tags: - public-api-tokens security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' description: '' put: operationId: public_api_tokens_update description: |- Update a token's resource grants via PUT (full replacement). Accepts ``available_resources`` (a non-empty list of valid resource values). Reconciles ResourceAccess rows: adds new, removes dropped, de-duplicates. ``integration_name`` and ``token`` are never mutated; if sent in the body, they are silently ignored. Returns HTTP 200 with the updated token serialized via SystemUserTokenSerializer. HTTP 400 is returned for invalid resource values or empty list. HTTP 403 is returned for non-admin callers; HTTP 404 if the token does not exist or belongs to another organization; HTTP 401 for unauthenticated. parameters: - in: path name: id schema: type: string required: true tags: - public-api-tokens requestBody: content: application/json: schema: $ref: '#/components/schemas/SystemUserTokenUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/SystemUserTokenUpdate' multipart/form-data: schema: $ref: '#/components/schemas/SystemUserTokenUpdate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' description: '' patch: operationId: public_api_tokens_partial_update description: |- Update a token's resource grants via PATCH (full replacement). PATCH and PUT behave identically for this endpoint: both require the full ``available_resources`` list and replace grants completely. Accepts ``available_resources`` (a non-empty list of valid resource values). Reconciles ResourceAccess rows: adds new, removes dropped, de-duplicates. ``integration_name`` and ``token`` are never mutated; if sent in the body, they are silently ignored. Returns HTTP 200 with the updated token serialized via SystemUserTokenSerializer. HTTP 400 is returned for invalid resource values or empty list. HTTP 403 is returned for non-admin callers; HTTP 404 if the token does not exist or belongs to another organization; HTTP 401 for unauthenticated. parameters: - in: path name: id schema: type: string required: true tags: - public-api-tokens requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedSystemUserTokenUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedSystemUserTokenUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedSystemUserTokenUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' description: '' /public-api-tokens/{id}{format}: get: operationId: public_api_tokens_formatted_retrieve description: |- Admin-only viewset for managing public-API tokens (SystemUser + ResourceAccess rows). Supports create, list, retrieve, revoke, and editing resource grants. ``POST /public-api-tokens/`` creates a new ``SystemUser`` for the caller's organisation, persists the requested ``ResourceAccess`` rows, and returns the plaintext token **once**. The token is never recoverable after this response. ``GET /public-api-tokens/`` lists the caller's org tokens without secrets. ``GET /public-api-tokens/{id}/`` retrieves a single token without secrets. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - public-api-tokens security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' description: '' put: operationId: public_api_tokens_formatted_update description: |- Update a token's resource grants via PUT (full replacement). Accepts ``available_resources`` (a non-empty list of valid resource values). Reconciles ResourceAccess rows: adds new, removes dropped, de-duplicates. ``integration_name`` and ``token`` are never mutated; if sent in the body, they are silently ignored. Returns HTTP 200 with the updated token serialized via SystemUserTokenSerializer. HTTP 400 is returned for invalid resource values or empty list. HTTP 403 is returned for non-admin callers; HTTP 404 if the token does not exist or belongs to another organization; HTTP 401 for unauthenticated. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - public-api-tokens requestBody: content: application/json: schema: $ref: '#/components/schemas/SystemUserTokenUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/SystemUserTokenUpdate' multipart/form-data: schema: $ref: '#/components/schemas/SystemUserTokenUpdate' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' description: '' patch: operationId: public_api_tokens_formatted_partial_update description: |- Update a token's resource grants via PATCH (full replacement). PATCH and PUT behave identically for this endpoint: both require the full ``available_resources`` list and replace grants completely. Accepts ``available_resources`` (a non-empty list of valid resource values). Reconciles ResourceAccess rows: adds new, removes dropped, de-duplicates. ``integration_name`` and ``token`` are never mutated; if sent in the body, they are silently ignored. Returns HTTP 200 with the updated token serialized via SystemUserTokenSerializer. HTTP 400 is returned for invalid resource values or empty list. HTTP 403 is returned for non-admin callers; HTTP 404 if the token does not exist or belongs to another organization; HTTP 401 for unauthenticated. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - public-api-tokens requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedSystemUserTokenUpdate' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedSystemUserTokenUpdate' multipart/form-data: schema: $ref: '#/components/schemas/PatchedSystemUserTokenUpdate' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' description: '' /public-api-tokens/{id}/revoke/: post: operationId: public_api_tokens_revoke_create description: |- Revoke a public-API token by setting its SystemUser.is_active to False. The token will no longer authenticate requests via check_system_user_token. This is idempotent: revoking an already-revoked token is a 200 no-op. Returns HTTP 200 with the updated token serialized via SystemUserTokenSerializer. HTTP 403 is returned for non-admin callers; HTTP 404 if the token does not exist or belongs to another organization; HTTP 401 for unauthenticated. parameters: - in: path name: id schema: type: string required: true tags: - public-api-tokens requestBody: content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/SystemUserToken' multipart/form-data: schema: $ref: '#/components/schemas/SystemUserToken' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' description: '' /public-api-tokens/{id}/revoke{format}: post: operationId: public_api_tokens_revoke_formatted_create description: |- Revoke a public-API token by setting its SystemUser.is_active to False. The token will no longer authenticate requests via check_system_user_token. This is idempotent: revoking an already-revoked token is a 200 no-op. Returns HTTP 200 with the updated token serialized via SystemUserTokenSerializer. HTTP 403 is returned for non-admin callers; HTTP 404 if the token does not exist or belongs to another organization; HTTP 401 for unauthenticated. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - public-api-tokens requestBody: content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/SystemUserToken' multipart/form-data: schema: $ref: '#/components/schemas/SystemUserToken' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/SystemUserToken' description: '' /public/organizations/{organization_id}/events/: get: operationId: public_organizations_events_list description: List events with token-based authentication. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer - in: path name: organization_id schema: type: string pattern: ^\d+$ required: true tags: - public responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedCalendarEventList' description: '' post: operationId: public_organizations_events_create description: Create an event with token-based authentication checked first. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: organization_id schema: type: string pattern: ^\d+$ required: true tags: - public requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarEvent' multipart/form-data: schema: $ref: '#/components/schemas/CalendarEvent' required: true responses: '201': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' /public/organizations/{organization_id}/events/{id}/: get: operationId: public_organizations_events_retrieve description: Retrieve a single event with token-based authentication. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: integer description: A unique integer value identifying this calendar event. required: true - in: path name: organization_id schema: type: string pattern: ^\d+$ required: true tags: - public responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' put: operationId: public_organizations_events_update description: Update an event with token-based authentication checked first. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: integer description: A unique integer value identifying this calendar event. required: true - in: path name: organization_id schema: type: string pattern: ^\d+$ required: true tags: - public requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/CalendarEvent' multipart/form-data: schema: $ref: '#/components/schemas/CalendarEvent' required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' patch: operationId: public_organizations_events_partial_update description: Partial update an event with token-based authentication checked first. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: integer description: A unique integer value identifying this calendar event. required: true - in: path name: organization_id schema: type: string pattern: ^\d+$ required: true tags: - public requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedCalendarEvent' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedCalendarEvent' multipart/form-data: schema: $ref: '#/components/schemas/PatchedCalendarEvent' responses: '200': content: application/json: schema: $ref: '#/components/schemas/CalendarEvent' description: '' delete: operationId: public_organizations_events_destroy description: Delete an event with token-based authentication checked first. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: integer description: A unique integer value identifying this calendar event. required: true - in: path name: organization_id schema: type: string pattern: ^\d+$ required: true tags: - public responses: '204': description: No response body /service-accounts/: get: operationId: service_accounts_list description: |- Admin-only CRUD for the organization's Google Calendar service account. Manages **only** the org-level service account (``calendar_fk IS NULL``) — the one used for rooms sync. Per-calendar service accounts are auto-assigned by the calendar auth flow and are intentionally not exposed here. Secrets (``private_key``, ``private_key_id``) are write-only and never echoed; all responses use ``ServiceAccountReadSerializer``. There is at most one org-level account per organization: ``create`` refuses a duplicate (rotate via PUT/PATCH or DELETE first). Cross-org ids resolve to 404 via the org-scoped queryset; non-admins get 403; anonymous requests 401. parameters: - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - service-accounts security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedServiceAccountReadList' description: '' post: operationId: service_accounts_create description: |- Create the org-level service account (one per organization). HTTP 201 with the secret-free representation. HTTP 400 if an org-level account already exists (rotate via PUT/PATCH or DELETE first) or the payload is invalid. tags: - service-accounts requestBody: content: application/json: schema: $ref: '#/components/schemas/ServiceAccountWrite' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ServiceAccountWrite' multipart/form-data: schema: $ref: '#/components/schemas/ServiceAccountWrite' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/ServiceAccountRead' description: '' /service-accounts{format}: get: operationId: service_accounts_formatted_list description: |- Admin-only CRUD for the organization's Google Calendar service account. Manages **only** the org-level service account (``calendar_fk IS NULL``) — the one used for rooms sync. Per-calendar service accounts are auto-assigned by the calendar auth flow and are intentionally not exposed here. Secrets (``private_key``, ``private_key_id``) are write-only and never echoed; all responses use ``ServiceAccountReadSerializer``. There is at most one org-level account per organization: ``create`` refuses a duplicate (rotate via PUT/PATCH or DELETE first). Cross-org ids resolve to 404 via the org-scoped queryset; non-admins get 403; anonymous requests 401. parameters: - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - service-accounts security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedServiceAccountReadList' description: '' post: operationId: service_accounts_formatted_create description: |- Create the org-level service account (one per organization). HTTP 201 with the secret-free representation. HTTP 400 if an org-level account already exists (rotate via PUT/PATCH or DELETE first) or the payload is invalid. parameters: - in: path name: format schema: type: string enum: - .json required: true tags: - service-accounts requestBody: content: application/json: schema: $ref: '#/components/schemas/ServiceAccountWrite' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ServiceAccountWrite' multipart/form-data: schema: $ref: '#/components/schemas/ServiceAccountWrite' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/ServiceAccountRead' description: '' /service-accounts/{id}/: get: operationId: service_accounts_retrieve description: |- Admin-only CRUD for the organization's Google Calendar service account. Manages **only** the org-level service account (``calendar_fk IS NULL``) — the one used for rooms sync. Per-calendar service accounts are auto-assigned by the calendar auth flow and are intentionally not exposed here. Secrets (``private_key``, ``private_key_id``) are write-only and never echoed; all responses use ``ServiceAccountReadSerializer``. There is at most one org-level account per organization: ``create`` refuses a duplicate (rotate via PUT/PATCH or DELETE first). Cross-org ids resolve to 404 via the org-scoped queryset; non-admins get 403; anonymous requests 401. parameters: - in: path name: id schema: type: string required: true tags: - service-accounts security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ServiceAccountRead' description: '' put: operationId: service_accounts_update description: |- Rotate/update the org-level service account. PUT requires all writable fields; PATCH updates the provided subset (secrets are retained when omitted). Returns HTTP 200 with the secret-free representation. parameters: - in: path name: id schema: type: string required: true tags: - service-accounts requestBody: content: application/json: schema: $ref: '#/components/schemas/ServiceAccountWrite' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ServiceAccountWrite' multipart/form-data: schema: $ref: '#/components/schemas/ServiceAccountWrite' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ServiceAccountRead' description: '' patch: operationId: service_accounts_partial_update description: |- Admin-only CRUD for the organization's Google Calendar service account. Manages **only** the org-level service account (``calendar_fk IS NULL``) — the one used for rooms sync. Per-calendar service accounts are auto-assigned by the calendar auth flow and are intentionally not exposed here. Secrets (``private_key``, ``private_key_id``) are write-only and never echoed; all responses use ``ServiceAccountReadSerializer``. There is at most one org-level account per organization: ``create`` refuses a duplicate (rotate via PUT/PATCH or DELETE first). Cross-org ids resolve to 404 via the org-scoped queryset; non-admins get 403; anonymous requests 401. parameters: - in: path name: id schema: type: string required: true tags: - service-accounts requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedServiceAccountWrite' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedServiceAccountWrite' multipart/form-data: schema: $ref: '#/components/schemas/PatchedServiceAccountWrite' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ServiceAccountWrite' description: '' delete: operationId: service_accounts_destroy description: Delete the org-level service account. HTTP 204. parameters: - in: path name: id schema: type: string required: true tags: - service-accounts security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /service-accounts/{id}{format}: get: operationId: service_accounts_formatted_retrieve description: |- Admin-only CRUD for the organization's Google Calendar service account. Manages **only** the org-level service account (``calendar_fk IS NULL``) — the one used for rooms sync. Per-calendar service accounts are auto-assigned by the calendar auth flow and are intentionally not exposed here. Secrets (``private_key``, ``private_key_id``) are write-only and never echoed; all responses use ``ServiceAccountReadSerializer``. There is at most one org-level account per organization: ``create`` refuses a duplicate (rotate via PUT/PATCH or DELETE first). Cross-org ids resolve to 404 via the org-scoped queryset; non-admins get 403; anonymous requests 401. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - service-accounts security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ServiceAccountRead' description: '' put: operationId: service_accounts_formatted_update description: |- Rotate/update the org-level service account. PUT requires all writable fields; PATCH updates the provided subset (secrets are retained when omitted). Returns HTTP 200 with the secret-free representation. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - service-accounts requestBody: content: application/json: schema: $ref: '#/components/schemas/ServiceAccountWrite' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/ServiceAccountWrite' multipart/form-data: schema: $ref: '#/components/schemas/ServiceAccountWrite' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ServiceAccountRead' description: '' patch: operationId: service_accounts_formatted_partial_update description: |- Admin-only CRUD for the organization's Google Calendar service account. Manages **only** the org-level service account (``calendar_fk IS NULL``) — the one used for rooms sync. Per-calendar service accounts are auto-assigned by the calendar auth flow and are intentionally not exposed here. Secrets (``private_key``, ``private_key_id``) are write-only and never echoed; all responses use ``ServiceAccountReadSerializer``. There is at most one org-level account per organization: ``create`` refuses a duplicate (rotate via PUT/PATCH or DELETE first). Cross-org ids resolve to 404 via the org-scoped queryset; non-admins get 403; anonymous requests 401. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - service-accounts requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedServiceAccountWrite' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedServiceAccountWrite' multipart/form-data: schema: $ref: '#/components/schemas/PatchedServiceAccountWrite' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/ServiceAccountWrite' description: '' delete: operationId: service_accounts_formatted_destroy description: Delete the org-level service account. HTTP 204. parameters: - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - service-accounts security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /webhook-configurations/: get: operationId: webhook_configurations_list description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - webhook-configurations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedWebhookConfigurationList' description: '' post: operationId: webhook_configurations_create description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. tags: - webhook-configurations requestBody: content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/WebhookConfiguration' multipart/form-data: schema: $ref: '#/components/schemas/WebhookConfiguration' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' description: '' /webhook-configurations{format}: get: operationId: webhook_configurations_formatted_list description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - webhook-configurations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedWebhookConfigurationList' description: '' post: operationId: webhook_configurations_formatted_create description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true tags: - webhook-configurations requestBody: content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/WebhookConfiguration' multipart/form-data: schema: $ref: '#/components/schemas/WebhookConfiguration' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '201': content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' description: '' /webhook-configurations/{id}/: get: operationId: webhook_configurations_retrieve description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - webhook-configurations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' description: '' put: operationId: webhook_configurations_update description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - webhook-configurations requestBody: content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/WebhookConfiguration' multipart/form-data: schema: $ref: '#/components/schemas/WebhookConfiguration' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' description: '' patch: operationId: webhook_configurations_partial_update description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - webhook-configurations requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedWebhookConfiguration' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedWebhookConfiguration' multipart/form-data: schema: $ref: '#/components/schemas/PatchedWebhookConfiguration' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' description: '' delete: operationId: webhook_configurations_destroy description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - webhook-configurations security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /webhook-configurations/{id}{format}: get: operationId: webhook_configurations_formatted_retrieve description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - webhook-configurations security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' description: '' put: operationId: webhook_configurations_formatted_update description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - webhook-configurations requestBody: content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/WebhookConfiguration' multipart/form-data: schema: $ref: '#/components/schemas/WebhookConfiguration' required: true security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' description: '' patch: operationId: webhook_configurations_formatted_partial_update description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - webhook-configurations requestBody: content: application/json: schema: $ref: '#/components/schemas/PatchedWebhookConfiguration' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/PatchedWebhookConfiguration' multipart/form-data: schema: $ref: '#/components/schemas/PatchedWebhookConfiguration' security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookConfiguration' description: '' delete: operationId: webhook_configurations_formatted_destroy description: |- ViewSet for managing webhook configurations. Provides full CRUD operations: create, retrieve, update, partial_update, destroy, and list. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - webhook-configurations security: - jwtAuth: [] - cookieAuth: [] responses: '204': description: No response body /webhook-events/: get: operationId: webhook_events_list description: |- ViewSet for webhook events. Provides read-only operations (list, retrieve) and a custom retry action. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - webhook-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedWebhookEventList' description: '' /webhook-events{format}: get: operationId: webhook_events_formatted_list description: |- ViewSet for webhook events. Provides read-only operations (list, retrieve) and a custom retry action. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - name: limit required: false in: query description: Number of results to return per page. schema: type: integer - name: offset required: false in: query description: The initial index from which to return the results. schema: type: integer tags: - webhook-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/PaginatedWebhookEventList' description: '' /webhook-events/{id}/: get: operationId: webhook_events_retrieve description: |- ViewSet for webhook events. Provides read-only operations (list, retrieve) and a custom retry action. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - webhook-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookEvent' description: '' /webhook-events/{id}{format}: get: operationId: webhook_events_formatted_retrieve description: |- ViewSet for webhook events. Provides read-only operations (list, retrieve) and a custom retry action. parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - webhook-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookEvent' description: '' /webhook-events/{id}/retry/: post: operationId: webhook_events_retry_create description: Retry a failed webhook event parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: id schema: type: string required: true tags: - webhook-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookEvent' description: '' '400': content: application/json: schema: description: Bad request - event cannot be retried description: '' '404': content: application/json: schema: description: Event not found description: '' /webhook-events/{id}/retry{format}: post: operationId: webhook_events_retry_formatted_create description: Retry a failed webhook event parameters: - in: header name: X-Organization-Id schema: type: string description: Selects the active organization for this request. Optional for callers that belong to exactly one active organization — the single membership is resolved implicitly. **Required** when the caller has two or more active memberships; omitting it in that case returns **400**. If the header names an organization the caller is not an active member of, the server returns **403**. - in: path name: format schema: type: string enum: - .json required: true - in: path name: id schema: type: string required: true tags: - webhook-events security: - jwtAuth: [] - cookieAuth: [] responses: '200': content: application/json: schema: $ref: '#/components/schemas/WebhookEvent' description: '' '400': content: application/json: schema: description: Bad request - event cannot be retried description: '' '404': content: application/json: schema: description: Event not found description: '' components: schemas: AcceptInvitation: type: object description: Serializer for accepting invitations via public endpoint. properties: token: type: string required: - token AcceptInvitationResponse: type: object properties: message: type: string organization_id: type: integer organization_name: type: string required: - message - organization_id - organization_name ActionEnum: enum: - create - update - delete type: string description: |- * `create` - create * `update` - update * `delete` - delete AddOnPurchaseRequest: type: object description: |- Body of ``POST /billing/add-ons/``. See ``ChangePlanRequestSerializer`` for why ``payment_token`` is present despite not being in the documented request shape -- an add-on purchase is a one-time charge and needs an instrument to charge, exactly like a first-ever plan upgrade does. properties: resource_key: $ref: '#/components/schemas/ResourceKeyEnum' quantity: type: integer minimum: 1 is_recurring: type: boolean default: true idempotency_key: type: string maxLength: 255 payment_token: type: string maxLength: 255 required: - idempotency_key - quantity - resource_key AvailableResourcesEnum: enum: - calendar_event - calendar - recurrence_rule - external_attendee - external_attendance - attendance - user - resource_allocation - event_recurring_exception - blocked_time - blocked_time_recurring_exception - available_time - available_time_recurring_exception - availability_windows - unavailable_windows - organization - calendar_group - system_user - membership - invitation - branding - child_org_analytics - calendar_booking_code - create_resource_calendar - disable_resource_calendar - update_resource_calendar - import_resource_calendars - create_availability_window - update_availability_window - delete_availability_window - batch_update_availability_windows - create_blocked_time - update_blocked_time - delete_blocked_time - calendar_bundle - create_calendar - update_calendar - create_calendar_bundle - update_calendar_bundle - disable_calendar_bundle - webhook_configuration - external_event_change_request - booking_policy - bookable_slots - group_scoped_availability_windows - batch_upsert_group_scoped_availability_windows - group_scoped_blocked_times - batch_upsert_group_scoped_blocked_times - group_scoped_quota_rules - batch_upsert_group_scoped_quota_rules type: string description: |- * `calendar_event` - Calendar Event * `calendar` - Calendar * `recurrence_rule` - Recurrence Rule * `external_attendee` - External Attendee * `external_attendance` - External Attendance * `attendance` - Attendance * `user` - User * `resource_allocation` - Resource Allocation * `event_recurring_exception` - Event Recurring Exception * `blocked_time` - Blocked Time * `blocked_time_recurring_exception` - Blocked Time Recurring Exception * `available_time` - Available Time * `available_time_recurring_exception` - Available Time Recurring Exception * `availability_windows` - Availability Windows * `unavailable_windows` - Unavailable Windows * `organization` - Organization * `calendar_group` - Calendar Group * `system_user` - System User * `membership` - Membership * `invitation` - Invitation * `branding` - Branding * `child_org_analytics` - Child Organization Analytics * `calendar_booking_code` - Calendar Booking Code * `create_resource_calendar` - Create Resource Calendar * `disable_resource_calendar` - Disable Resource Calendar * `update_resource_calendar` - Update Resource Calendar * `import_resource_calendars` - Import Resource Calendars * `create_availability_window` - Create Availability Window * `update_availability_window` - Update Availability Window * `delete_availability_window` - Delete Availability Window * `batch_update_availability_windows` - Batch Update Availability Windows * `create_blocked_time` - Create Blocked Time * `update_blocked_time` - Update Blocked Time * `delete_blocked_time` - Delete Blocked Time * `calendar_bundle` - Calendar Bundle * `create_calendar` - Create Calendar * `update_calendar` - Update Calendar * `create_calendar_bundle` - Create Calendar Bundle * `update_calendar_bundle` - Update Calendar Bundle * `disable_calendar_bundle` - Disable Calendar Bundle * `webhook_configuration` - Webhook Configuration * `external_event_change_request` - External Event Change Request * `booking_policy` - Booking Policy * `bookable_slots` - Bookable Slots * `group_scoped_availability_windows` - Group-Scoped Availability Windows * `batch_upsert_group_scoped_availability_windows` - Batch Upsert Group-Scoped Availability Windows * `group_scoped_blocked_times` - Group-Scoped Blocked Times * `batch_upsert_group_scoped_blocked_times` - Batch Upsert Group-Scoped Blocked Times * `group_scoped_quota_rules` - Group-Scoped Quota Rules * `batch_upsert_group_scoped_quota_rules` - Batch Upsert Group-Scoped Quota Rules AvailableTime: type: object description: Serializer for AvailableTime model with recurring support. properties: id: type: integer readOnly: true start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string maxLength: 50 recurrence_rule: allOf: - $ref: '#/components/schemas/RecurrenceRule' description: Recurrence rule data for creating recurring available times rrule_string: type: string writeOnly: true description: RRULE string for creating recurring available times is_recurring_instance: type: boolean description: True if this is an instance of a recurring available time readOnly: true is_recurring: type: boolean description: True if this is a recurring available time readOnly: true parent_available_time: type: object properties: id: type: integer required: - id nullable: true description: Get parent available time for instances. readOnly: true recurrence_id: type: string format: date-time readOnly: true nullable: true description: For recurring instances, this identifies which occurrence this is created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true calendar: type: integer nullable: true required: - created - end_time - id - is_recurring - is_recurring_instance - modified - parent_available_time - recurrence_id - start_time - timezone AvailableTimeBatch: type: object description: |- Transactional batch of create/update/delete operations on a calendar's available times. All operations target a single calendar (resolved from ``calendar`` or the user's default) and run in one transaction — any failure rolls the whole batch back. properties: operations: type: array items: $ref: '#/components/schemas/AvailableTimeOperation' calendar: type: integer nullable: true description: Calendar to apply the batch to. Defaults to the user's default calendar. required: - operations AvailableTimeBulkModification: type: object properties: modification_start_date: type: string format: date recurrence_rule: allOf: - $ref: '#/components/schemas/RecurrenceRule' description: Recurrence rule data for the modification range rrule_string: type: string writeOnly: true nullable: true description: RRULE string for the modification range modified_start_time_offset: type: string nullable: true modified_end_time_offset: type: string nullable: true is_cancelled: type: boolean default: false required: - modification_start_date AvailableTimeOperation: type: object description: A single create/update/delete operation in an available-times batch. properties: action: $ref: '#/components/schemas/ActionEnum' id: type: integer description: Target AvailableTime id (required for update/delete). start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string rrule_string: type: string nullable: true description: RRULE string; null clears recurrence. Omit to leave unchanged on update. required: - action AvailableTimeRecurringException: type: object description: Serializer for creating recurring available time exceptions. properties: exception_date: type: string format: date description: The date of the occurrence to modify or cancel modified_start_time: type: string format: date-time nullable: true description: New start time for the modified occurrence (if not cancelled) modified_end_time: type: string format: date-time nullable: true description: New end time for the modified occurrence (if not cancelled) is_cancelled: type: boolean default: false description: True if cancelling the occurrence, False if modifying required: - exception_date AvailableTimeWindow: type: object properties: id: type: integer start_time: type: string format: date-time end_time: type: string format: date-time can_book_partially: type: boolean required: - can_book_partially - end_time - id - start_time BillingAddress: type: object description: Serializer for BillingAddress virtual model. properties: id: type: integer readOnly: true street_name: type: string street_number: type: string neighborhood: type: string address_line_2: type: string city: type: string maxLength: 255 state: type: string maxLength: 255 country: type: string maxLength: 255 zip_code: type: string maxLength: 10 required: - city - country - id - state - street_name - street_number - zip_code BillingPlan: type: object description: |- The catalog view behind ``GET /billing/plans/`` — every active plan with its limits and entitlements, so a client can render an upgrade picker without a second round trip per plan. properties: id: type: integer readOnly: true slug: type: string readOnly: true pattern: ^[-a-zA-Z0-9_]+$ name: type: string readOnly: true is_active: type: boolean readOnly: true is_default_for_new_organizations: type: boolean readOnly: true monthly_price: type: string format: decimal pattern: ^-?\d{0,8}(?:\.\d{0,2})?$ readOnly: true annual_price: type: string format: decimal pattern: ^-?\d{0,8}(?:\.\d{0,2})?$ readOnly: true nullable: true currency: type: string readOnly: true grace_period_days: type: integer readOnly: true nullable: true limits: type: array items: $ref: '#/components/schemas/PlanLimit' readOnly: true entitlements: type: array items: $ref: '#/components/schemas/PlanEntitlement' readOnly: true required: - annual_price - currency - entitlements - grace_period_days - id - is_active - is_default_for_new_organizations - limits - monthly_price - name - slug BillingProfile: type: object description: Serializer for BillingProfile virtual model. properties: id: type: integer readOnly: true contact_first_name: type: string maxLength: 255 contact_last_name: type: string maxLength: 255 contact_email: type: string format: email maxLength: 254 contact_phone: type: string maxLength: 50 document_type: type: string maxLength: 50 document_number: type: string maxLength: 50 billing_address: $ref: '#/components/schemas/BillingAddress' created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - billing_address - contact_email - contact_first_name - created - document_number - document_type - id - modified BillingStateEnum: enum: - free - active - grace - restricted - cancelled type: string description: |- * `free` - Free * `active` - Active * `grace` - Grace period * `restricted` - Restricted * `cancelled` - Cancelled BlockedTime: type: object description: Serializer for BlockedTime model with recurring support. properties: id: type: integer readOnly: true start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string maxLength: 50 reason: type: string maxLength: 255 recurrence_rule: allOf: - $ref: '#/components/schemas/RecurrenceRule' description: Recurrence rule data for creating recurring blocked times rrule_string: type: string writeOnly: true description: RRULE string for creating recurring blocked times external_id: type: string readOnly: true is_recurring_instance: type: boolean description: True if this is an instance of a recurring blocked time readOnly: true is_recurring: type: boolean description: True if this is a recurring blocked time readOnly: true parent_blocked_time: type: object properties: id: type: integer reason: type: string nullable: true required: - id - reason nullable: true description: Get parent blocked time for instances. readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true calendar: type: integer nullable: true required: - created - end_time - external_id - id - is_recurring - is_recurring_instance - modified - parent_blocked_time - start_time - timezone BlockedTimeBulkModification: type: object properties: modification_start_date: type: string format: date modified_reason: type: string nullable: true recurrence_rule: allOf: - $ref: '#/components/schemas/RecurrenceRule' description: Recurrence rule data for the modification range rrule_string: type: string writeOnly: true nullable: true description: RRULE string for the modification range modified_start_time_offset: type: string nullable: true modified_end_time_offset: type: string nullable: true is_cancelled: type: boolean default: false required: - modification_start_date BlockedTimeRecurringException: type: object description: Serializer for creating recurring blocked time exceptions. properties: exception_date: type: string format: date description: The date of the occurrence to modify or cancel modified_reason: type: string nullable: true description: New reason for the modified occurrence (if not cancelled) maxLength: 255 modified_start_time: type: string format: date-time nullable: true description: New start time for the modified occurrence (if not cancelled) modified_end_time: type: string format: date-time nullable: true description: New end time for the modified occurrence (if not cancelled) is_cancelled: type: boolean default: false description: True if cancelling the occurrence, False if modifying required: - exception_date BookableSlotProposal: type: object properties: start_time: type: string format: date-time end_time: type: string format: date-time required: - end_time - start_time BookingPolicy: type: object description: |- Serializer for ``BookingPolicy`` CRUD. Exactly one of ``calendar``, ``membership_user_id``, ``calendar_group``, or ``is_organization_default`` must be set on create. Targets are immutable after creation — only the four rule-field seconds are writable on update. Validation: - ``validate()`` enforces the exactly-one-target invariant on create. - ``validate_membership_user_id()`` checks that the supplied user id belongs to the caller's organization (on create only; targets are immutable on update). - ``DuplicateBookingPolicyError`` from the service is caught and surfaced as a 400 validation error so the client gets a named conflict message. - The four rule fields use ``min_value=0`` so DRF rejects negatives with a clear field-level 400 before the value reaches the model's ``PositiveIntegerField`` constraint. Write paths (create / update) delegate to ``BookingPolicyService`` stored on the serializer context as ``"booking_policy_service"`` (the viewset sets it). properties: id: type: integer readOnly: true calendar: type: integer nullable: true calendar_group: type: integer nullable: true membership_user_id: type: integer nullable: true is_organization_default: type: boolean default: false lead_time_seconds: type: integer minimum: 0 default: 0 max_horizon_seconds: type: integer minimum: 0 default: 0 buffer_before_seconds: type: integer minimum: 0 default: 0 buffer_after_seconds: type: integer minimum: 0 default: 0 created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - created - id - modified BulkBlockedTime: type: object description: Serializer for creating multiple blocked times. properties: blocked_times: type: array items: $ref: '#/components/schemas/BlockedTime' required: - blocked_times BulkMarkRead: type: object description: |- Input serializer for the bulk mark-as-read endpoint. Validates that the request body contains a non-empty list of notification ids. Empty list or missing field → DRF 400 validation error. properties: ids: type: array items: type: integer maxItems: 100 required: - ids Calendar: type: object properties: id: type: integer readOnly: true name: type: string maxLength: 255 description: type: string email: type: string format: email readOnly: true external_id: type: string readOnly: true provider: allOf: - $ref: '#/components/schemas/ProviderEnum' readOnly: true calendar_type: allOf: - $ref: '#/components/schemas/CalendarTypeEnum' readOnly: true description: |- The type of calendar. Personal calendars are for individual use, resource calendars are for shared resources, and virtual calendars are for online meetings or events. * `personal` - Personal Calendar * `resource` - Resource Calendar * `virtual` - Virtual Calendar * `bundle` - Bundle Calendar capacity: type: integer maximum: 2147483647 minimum: 0 nullable: true description: The maximum number of attendees that can be accommodated in this calendar's events. This is only applicable for resource calendars. manage_available_windows: type: boolean description: If true, this calendar can manage its own available time windows. If not, it will use the available time windows of the external calendar it's attached to. visibility: allOf: - $ref: '#/components/schemas/VisibilityEnum' description: |- Controls how this calendar appears in queries. active: listed and available for booking (default). unlisted: hidden from listing/booking queries but still synced for conflict detection; survives re-import so user opt-out is preserved. inactive: soft-deleted, hidden from all queries and not synced. Use DELETE /calendars/{id}/ to transition to inactive instead of hard-deleting. * `active` - Active * `unlisted` - Unlisted * `inactive` - Inactive sync_enabled: type: boolean description: Whether this calendar's events are pulled from the external provider. Set to False to skip syncing calendars that aren't useful for scheduling — holidays, birthdays, organization-wide events, etc. When False, no new CalendarSync is requested for this calendar (including webhook- and import-triggered syncs); previously synced events are left untouched. Default True keeps existing calendars syncing as before. required: - calendar_type - email - external_id - id - name - provider CalendarBundleCreate: type: object properties: name: type: string maxLength: 255 bundle_calendars: type: array items: type: integer primary_calendar: type: integer nullable: true required: - bundle_calendars - name - primary_calendar CalendarEvent: type: object properties: id: type: integer readOnly: true provider: type: string writeOnly: true title: type: string maxLength: 255 description: type: string start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string maxLength: 50 created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true external_id: type: string readOnly: true external_attendances: type: array items: $ref: '#/components/schemas/EventExternalAttendance' attendances: type: array items: $ref: '#/components/schemas/EventAttendance' resource_allocations: type: array items: $ref: '#/components/schemas/ResourceAllocation' recurrence_rule: allOf: - $ref: '#/components/schemas/RecurrenceRule' description: Recurrence rule data for creating recurring events rrule_string: type: string writeOnly: true description: RRULE string for creating recurring events parent_recurring_object_id: type: integer writeOnly: true description: ID of parent event for recurring instances parent_recurring_object: allOf: - $ref: '#/components/schemas/ParentEvent' readOnly: true is_recurring_instance: type: boolean description: True if this is an instance of a recurring event readOnly: true is_recurring: type: boolean description: True if this is a recurring event readOnly: true is_recurring_exception: type: boolean description: True if this object is an exception to the recurrence rule (modified occurrence) recurrence_id: type: string format: date-time nullable: true description: For recurring instances, this identifies which occurrence this is google_calendar_service_account: type: integer writeOnly: true calendar: type: integer writeOnly: true required: - attendances - created - end_time - external_attendances - external_id - id - is_recurring - is_recurring_instance - modified - parent_recurring_object - resource_allocations - start_time - timezone - title CalendarEventTransfer: type: object properties: target_calendar_id: type: integer required: - target_calendar_id CalendarGroup: type: object properties: id: type: integer readOnly: true name: type: string maxLength: 255 description: type: string slots: type: array items: $ref: '#/components/schemas/CalendarGroupSlot' created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - created - id - modified - name - slots CalendarGroupAvailabilityQuery: type: object description: 'Input for the availability action: list of [start, end] windows.' properties: ranges: type: array items: $ref: '#/components/schemas/_RangeInput' required: - ranges CalendarGroupEventCreate: type: object description: |- Input for booking an event through a CalendarGroup. On `save()` this delegates to `CalendarGroupService.create_grouped_event` and returns the created `CalendarEvent`. The view is responsible for serializing the result (typically with `CalendarEventSerializer`). properties: title: type: string description: type: string default: '' start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string slot_selections: type: array items: $ref: '#/components/schemas/_CalendarGroupSlotSelectionInput' attendances: type: array items: type: object additionalProperties: {} external_attendances: type: array items: type: object additionalProperties: {} required: - end_time - slot_selections - start_time - timezone - title CalendarGroupRangeAvailability: type: object properties: start_time: type: string format: date-time end_time: type: string format: date-time slots: type: array items: $ref: '#/components/schemas/CalendarGroupSlotAvailability' required: - end_time - slots - start_time CalendarGroupSlot: type: object description: |- Nested slot representation used inside CalendarGroupSerializer. On write, accepts `calendar_ids: list[int]`; on read exposes the calendar pool via `calendars` (the M2M). We deliberately keep slot writes to payload-time data only — persistence happens through `CalendarGroupSerializer` which delegates to `CalendarGroupService`. properties: id: type: integer readOnly: true name: type: string maxLength: 255 description: type: string order: type: integer maximum: 32767 minimum: 0 required_count: type: integer maximum: 32767 minimum: 0 description: How many calendars from the pool must be selected when booking. Default 1; use larger values when a slot needs multiple calendars (e.g. two nurses). calendars: type: array items: $ref: '#/components/schemas/Calendar' readOnly: true calendar_ids: type: array items: type: integer writeOnly: true required: - calendar_ids - calendars - id - name CalendarGroupSlotAvailability: type: object properties: slot_id: type: integer available_calendar_ids: type: array items: type: integer required_count: type: integer is_bookable: type: boolean readOnly: true required: - available_calendar_ids - is_bookable - required_count - slot_id CalendarSync: type: object properties: id: type: integer readOnly: true status: allOf: - $ref: '#/components/schemas/CalendarSyncStatusEnum' readOnly: true start_datetime: type: string format: date-time end_datetime: type: string format: date-time should_update_events: type: boolean trigger_source: allOf: - $ref: '#/components/schemas/TriggerSourceEnum' readOnly: true description: |- What kicked off this sync: import, manual, webhook, or admin. * `import` - Import * `manual` - Manual * `webhook` - Webhook * `admin` - Admin error_message: type: string readOnly: true required: - end_datetime - error_message - id - should_update_events - start_datetime - status - trigger_source CalendarSyncRequest: type: object properties: start_datetime: type: string format: date-time end_datetime: type: string format: date-time should_update_events: type: boolean default: false required: - end_datetime - start_datetime CalendarSyncStatusEnum: enum: - success - failed - in_progress - not_started type: string description: |- * `success` - Success * `failed` - Failed * `in_progress` - In Progress * `not_started` - Not Started CalendarTypeEnum: enum: - personal - resource - virtual - bundle type: string description: |- * `personal` - Personal Calendar * `resource` - Resource Calendar * `virtual` - Virtual Calendar * `bundle` - Bundle Calendar ChangePlanRequest: type: object description: |- Body of ``POST /billing/subscription/change-plan/``. ``payment_token`` is not part of the documented request body (only ``plan_slug``/``billing_interval``/``idempotency_key``) but is required in practice the *first* time a billing root ever attaches a payment instrument -- there is otherwise no provider-facing card/token to create the provider-side subscription against. Optional here (blank by default) because it is only actually required when ``Subscription.external_id`` is still blank; see ``SubscriptionService._initiate_upgrade`` for the exact condition and ``PaymentTokenRequiredError`` for the 400 a caller gets if it omits the token when one was needed. This is a deliberate deviation from the documented request shape. properties: plan_slug: type: string pattern: ^[-a-zA-Z0-9_]+$ billing_interval: allOf: - $ref: '#/components/schemas/PendingBillingIntervalEnum' default: monthly idempotency_key: type: string maxLength: 255 payment_token: type: string maxLength: 255 required: - idempotency_key - plan_slug ConceptDoc: type: object description: |- Read-only representation of a single concept doc's full content. Plain ``Serializer`` over a :class:`public_api.docs_content.ConceptDoc` dict — there is no model backing this. Returns raw markdown; the frontend owns rendering and sanitization. properties: slug: type: string readOnly: true title: type: string readOnly: true markdown: type: string readOnly: true required: - markdown - slug - title ConceptDocSummary: type: object description: |- Read-only manifest entry for a concept doc (list view). Plain ``Serializer`` over a :class:`public_api.docs_content.ConceptDocSummary` dict — there is no model backing this. properties: slug: type: string readOnly: true title: type: string readOnly: true required: - slug - title ConsentCreate: type: object description: |- Validates the input for the authenticated consent-record endpoint (OAuth step). ``document_type`` is required — the consenting user comes from the authenticated request, and audit metadata (IP, user-agent, source) is captured server-side in the view. ``phone_number`` is optional (phone-keyed consent): an OAuth user can consent a phone number here, before phone verification, so the SMS gate can later be satisfied for that phone. properties: document_type: $ref: '#/components/schemas/DocumentTypeEnum' phone_number: type: string maxLength: 20 required: - document_type CurrentMembership: type: object description: |- Read-only serializer for the caller's current organization membership. Returns the membership role and the nested organization so the frontend can distinguish between an onboarded user and a gated (membership-less) user. properties: role: allOf: - $ref: '#/components/schemas/RoleEnum' readOnly: true description: |- Role the user holds in this organization. Admins can manage organization-scoped resources (e.g. CalendarGroups) regardless of direct ownership. * `member` - Member * `admin` - Admin organization: type: object additionalProperties: {} description: Serialize the related organization using OrganizationSerializer. readOnly: true can_manage_branding: type: boolean description: |- Whether the membership's organization is branding-eligible. Computed as parentless-and-entitled -- deliberately excludes the slug condition (Organization Auth-Area Branding plan, Phase 4 Capability signal guiding decision), so an organization missing only a slug still sees the branding page instead of it being silently absent. Shares ``organizations.permissions.is_branding_eligible_organization`` rather than restating the two-condition check, so this tracks the same gate that governs ``GET /branding/`` (see ``OrganizationBrandingView._check_branding_read_gate``) rather than the three-condition write gate. readOnly: true required: - can_manage_branding - organization - role DocumentTypeEnum: enum: - privacy_policy - terms_of_use - sms_consent type: string description: |- * `privacy_policy` - Privacy Policy * `terms_of_use` - Terms of Use * `sms_consent` - SMS Messaging Consent EffectiveLimitUsage: type: object description: |- One row of ``GET /billing/usage/`` -- an ``EffectiveLimit`` paired with the ``current_usage`` ``EntitlementService.check_limit`` would compare it against. Not a ``ModelSerializer``: the source is a dataclass plus a separately-fetched usage count, not one model instance. properties: resource_key: type: string kind: type: string nullable: true limit_value: type: integer nullable: true current_usage: type: integer nullable: true overage_unit_price: type: string format: decimal pattern: ^-?\d{0,6}(?:\.\d{0,4})?$ nullable: true required: - current_usage - kind - limit_value - overage_unit_price - resource_key EntitlementKeyEnum: enum: - external_calendar_google - external_calendar_microsoft - partner_api - white_label_branding - advanced_scheduling type: string description: |- * `external_calendar_google` - Google Calendar sync * `external_calendar_microsoft` - Microsoft Calendar sync * `partner_api` - Partner / public API access * `white_label_branding` - White-label branding * `advanced_scheduling` - Advanced scheduling EventAttendance: type: object properties: id: type: integer nullable: true description: ID of the external attendee. membership: allOf: - $ref: '#/components/schemas/OwnershipMembership' readOnly: true user_id: type: integer writeOnly: true status: allOf: - $ref: '#/components/schemas/RSVPStatusEnum' readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - created - membership - modified - status - user_id EventBulkModification: type: object description: Serializer for creating bulk modifications on recurring events from a given date. properties: modification_start_date: type: string format: date modified_title: type: string nullable: true modified_description: type: string nullable: true recurrence_rule: allOf: - $ref: '#/components/schemas/RecurrenceRule' description: Recurrence rule data for the modification range rrule_string: type: string writeOnly: true nullable: true description: RRULE string for the modification range modified_start_time_offset: type: string nullable: true modified_end_time_offset: type: string nullable: true is_cancelled: type: boolean default: false required: - modification_start_date EventExternalAttendance: type: object properties: id: type: integer nullable: true description: ID of the external attendee. external_attendee: $ref: '#/components/schemas/ExternalAttendee' status: allOf: - $ref: '#/components/schemas/RSVPStatusEnum' readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - created - external_attendee - modified - status EventRecurringException: type: object description: Serializer for creating recurring event exceptions. properties: exception_date: type: string format: date description: The date of the occurrence to modify or cancel modified_title: type: string nullable: true description: New title for the modified occurrence (if not cancelled) maxLength: 255 modified_description: type: string nullable: true description: New description for the modified occurrence (if not cancelled) modified_start_time: type: string format: date-time nullable: true description: New start time for the modified occurrence (if not cancelled) modified_end_time: type: string format: date-time nullable: true description: New end time for the modified occurrence (if not cancelled) is_cancelled: type: boolean default: false description: True if cancelling the occurrence, False if modifying required: - exception_date EventTypeEnum: enum: - calendar_event_created - calendar_event_updated - calendar_event_deleted - calendar_event_attendee_added - calendar_event_attendee_removed - calendar_event_attendee_updated - organization_member_created type: string description: |- * `calendar_event_created` - Calendar Event Created * `calendar_event_updated` - Calendar Event Updated * `calendar_event_deleted` - Calendar Event Deleted * `calendar_event_attendee_added` - Calendar Event Attendee Added * `calendar_event_attendee_removed` - Calendar Event Attendee Removed * `calendar_event_attendee_updated` - Calendar Event Attendee Updated * `organization_member_created` - Organization member created ExternalAttendee: type: object properties: id: type: integer nullable: true description: ID of the external attendee. name: type: string maxLength: 255 email: type: string format: email maxLength: 254 created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - created - email - modified ExternalEventChangeRequest: type: object description: |- Read-only serializer for ``ExternalEventChangeRequest``. Exposes the fields needed by the first-party frontend to list, approve, and reject change requests. The ``resolved_by`` field surfaces only the user id and display name — never raw membership / organization ids — to avoid leaking cross-tenant identity data. properties: id: type: integer readOnly: true event_id: type: integer readOnly: true kind: allOf: - $ref: '#/components/schemas/ExternalEventChangeRequestKindEnum' readOnly: true status: allOf: - $ref: '#/components/schemas/ExternalEventChangeRequestStatusEnum' readOnly: true provider: allOf: - $ref: '#/components/schemas/ProviderEnum' readOnly: true proposed_values: readOnly: true description: 'Proposed field values: title, description, start_time, end_time.' retained_values: readOnly: true description: Snapshot of local field values before the change, used to undo on rejection. resolved_by_user_id: type: integer nullable: true description: Return the resolver's user id, or ``None`` when unresolved. readOnly: true resolved_at: type: string format: date-time readOnly: true nullable: true created: type: string format: date-time readOnly: true required: - created - event_id - id - kind - proposed_values - provider - resolved_at - resolved_by_user_id - retained_values - status ExternalEventChangeRequestKindEnum: enum: - update - delete type: string description: |- * `update` - Update * `delete` - Delete ExternalEventChangeRequestStatusEnum: enum: - pending - approved - rejected - stale - auto_undone type: string description: |- * `pending` - Pending * `approved` - Approved * `rejected` - Rejected * `stale` - Stale * `auto_undone` - Auto-undone ExternalEventUpdatePolicyEnum: enum: - allow - change_request - forbidden type: string description: |- * `allow` - Allow direct updates * `change_request` - Updates create change requests * `forbidden` - Updates are forbidden FrequencyEnum: enum: - DAILY - WEEKLY - MONTHLY - YEARLY type: string description: |- * `DAILY` - Daily * `WEEKLY` - Weekly * `MONTHLY` - Monthly * `YEARLY` - Yearly GroupScopedAvailabilityOrphanedBooking: type: object description: |- Minimal identification of a booking a narrowing write orphaned (spec UC-6) -- enough for an admin to act on. Nothing about the booking itself is modified by the write that produced this entry. properties: id: type: integer readOnly: true calendar_id: type: integer readOnly: true title: type: string readOnly: true start_time: type: string format: date-time readOnly: true end_time: type: string format: date-time readOnly: true required: - calendar_id - end_time - id - start_time - title GroupScopedAvailabilityWindow: type: object description: |- Read representation of a group-scoped availability window (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 1c). Deliberately narrower than ``AvailableTimeSerializer``: there is no nested ``recurrence_rule`` write path here, only ``rrule_string`` -- matching exactly what ``CalendarGroupService``'s window-write methods accept, so the REST shape and the service signature cannot drift apart. properties: id: type: integer readOnly: true calendar_id: type: integer readOnly: true group_slot_id: type: integer readOnly: true start_time: type: string format: date-time readOnly: true end_time: type: string format: date-time readOnly: true timezone: type: string readOnly: true rrule_string: type: string nullable: true description: RRULE string for the window's recurrence, or ``None`` when it doesn't recur. readOnly: true is_recurring: type: boolean readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - calendar_id - created - end_time - group_slot_id - id - is_recurring - modified - rrule_string - start_time - timezone GroupScopedAvailabilityWindowCreate: type: object description: |- Input for creating a group-scoped availability window. Field names map 1:1 onto ``CalendarGroupService.create_group_scoped_availability_window``'s keyword arguments (``calendar_id``, ``start_time``, ``end_time``, ``tz``, ``rrule_string``) so the REST shape can never silently drift from the service signature it delegates to. properties: calendar: type: integer description: Calendar this window applies to. Must be a member of the target slot. start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string rrule_string: type: string nullable: true description: RRULE string for a recurring window. Omit for a one-off window. required: - calendar - end_time - start_time - timezone GroupScopedAvailabilityWriteResult: type: object description: |- Wraps a ``GroupScopedAvailabilityWriteResult``: the saved window plus any confirmed future bookings the write orphaned. Returned by the create and update actions of ``GroupScopedAvailabilityWindowViewSet``. properties: window: allOf: - $ref: '#/components/schemas/GroupScopedAvailabilityWindow' readOnly: true orphaned_bookings: type: array items: $ref: '#/components/schemas/GroupScopedAvailabilityOrphanedBooking' readOnly: true description: Confirmed future bookings in this slot for this window's calendar that no longer fall inside the calendar's group-scoped availability after this write. Nothing about them is modified or cancelled -- act on them manually if needed. required: - orphaned_bookings - window GroupScopedBlockOrphanedBooking: type: object description: |- Minimal identification of a booking a block write orphaned (spec UC-6's rule applied to blocks) -- enough for an admin to act on. Nothing about the booking itself is modified by the write that produced this entry. properties: id: type: integer readOnly: true calendar_id: type: integer readOnly: true title: type: string readOnly: true start_time: type: string format: date-time readOnly: true end_time: type: string format: date-time readOnly: true required: - calendar_id - end_time - id - start_time - title GroupScopedBlockWriteResult: type: object description: |- Wraps a ``GroupScopedBlockWriteResult``: the saved block plus any confirmed future bookings the write orphaned. Returned by the create and update actions of ``GroupScopedBlockedTimeViewSet``. properties: block: allOf: - $ref: '#/components/schemas/GroupScopedBlockedTime' readOnly: true orphaned_bookings: type: array items: $ref: '#/components/schemas/GroupScopedBlockOrphanedBooking' readOnly: true description: Confirmed future bookings in this slot for this block's calendar that now fall inside the calendar's group-scoped blocked time after this write. Nothing about them is modified or cancelled -- act on them manually if needed. required: - block - orphaned_bookings GroupScopedBlockedTime: type: object description: |- Read representation of a group-scoped blocked time (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 2b). Mirrors ``GroupScopedAvailabilityWindowSerializer`` exactly, plus ``reason`` -- there is no nested ``recurrence_rule`` write path here, only ``rrule_string``, matching exactly what ``CalendarGroupService``'s block-write methods accept, so the REST shape and the service signature cannot drift apart. properties: id: type: integer readOnly: true calendar_id: type: integer readOnly: true group_slot_id: type: integer readOnly: true start_time: type: string format: date-time readOnly: true end_time: type: string format: date-time readOnly: true timezone: type: string readOnly: true reason: type: string readOnly: true rrule_string: type: string nullable: true description: RRULE string for the block's recurrence, or ``None`` when it doesn't recur. readOnly: true is_recurring: type: boolean readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - calendar_id - created - end_time - group_slot_id - id - is_recurring - modified - reason - rrule_string - start_time - timezone GroupScopedBlockedTimeCreate: type: object description: |- Input for creating a group-scoped blocked time. Field names map 1:1 onto ``CalendarGroupService.create_group_scoped_blocked_time``'s keyword arguments (``calendar_id``, ``start_time``, ``end_time``, ``tz``, ``reason``, ``rrule_string``) so the REST shape can never silently drift from the service signature it delegates to. properties: calendar: type: integer description: Calendar this block applies to. Must be a member of the target slot. start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string reason: type: string default: '' rrule_string: type: string nullable: true description: RRULE string for a recurring block. Omit for a one-off block. required: - calendar - end_time - start_time - timezone GroupScopedQuotaRule: type: object description: |- Read representation of a group-scoped quota rule (CALENDAR_GROUP_SCOPED_AVAILABILITY Phase 3c). Simpler than ``GroupScopedAvailabilityWindowSerializer``/ ``GroupScopedBlockedTimeSerializer``: quota rules are non-recurring (no ``rrule_string``, no ``timezone``, no time range) -- just the period and the cap, matching exactly what ``CalendarGroupService``'s quota-write methods accept, so the REST shape and the service signature cannot drift apart. properties: id: type: integer readOnly: true calendar_id: type: integer readOnly: true group_slot_id: type: integer readOnly: true period: allOf: - $ref: '#/components/schemas/PeriodEnum' readOnly: true description: |- Fixed calendar period the cap applies to (day, week, or month). * `day` - Day * `week` - Week * `month` - Month cap: type: integer readOnly: true description: Maximum number of live bookings made through this group slot a calendar may hold within one period. Must be at least 1. created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - calendar_id - cap - created - group_slot_id - id - modified - period GroupScopedQuotaRuleCreate: type: object description: |- Input for creating a group-scoped quota rule. Field names map 1:1 onto ``CalendarGroupService.create_group_scoped_quota_rule``'s keyword arguments (``calendar_id``, ``period``, ``cap``) so the REST shape can never silently drift from the service signature it delegates to. properties: calendar: type: integer description: Calendar this quota rule applies to. Must be a member of the target slot. period: allOf: - $ref: '#/components/schemas/PeriodEnum' description: |- Fixed calendar period the cap applies to (day, week, or month). * `day` - Day * `week` - Week * `month` - Month cap: type: integer minimum: 1 description: Maximum number of live bookings made through this group slot the calendar may hold within one period. required: - calendar - cap - period MyMembership: type: object description: |- Read-only serializer for the caller's active organization memberships. Used by ``GET /organizations/mine/`` to power the frontend org switcher. Returns a list of ``{organization: {id, name}, role, can_manage_branding}`` entries — one per active membership — without requiring the ``X-Organization-Id`` header. properties: organization: allOf: - $ref: '#/components/schemas/OrganizationBrief' readOnly: true role: allOf: - $ref: '#/components/schemas/RoleEnum' readOnly: true description: |- Role the user holds in this organization. Admins can manage organization-scoped resources (e.g. CalendarGroups) regardless of direct ownership. * `member` - Member * `admin` - Admin can_manage_branding: type: boolean description: |- Whether this membership's organization is branding-eligible (parentless-and-entitled, excluding the slug condition) -- see ``CurrentMembershipSerializer.get_can_manage_branding`` for the full rationale. Computed per-membership (not per-role): a non-admin member's entry reports the same organization-level capability as an admin's, matching the read gate's own admin-agnostic eligibility check -- role-based write authorization is enforced separately by ``IsOrganizationAdmin`` on the branding endpoints themselves. Reads from the batch ``_MyMembershipListSerializer`` precomputes on the shared context when serializing a list (the ``many=True`` path this serializer is actually used on). Falls back to the single-organization ``is_branding_eligible_organization`` call when there is no such batch in context (e.g. this serializer instantiated directly against one membership), which is exactly what the batch entry would have computed for that one organization anyway. readOnly: true required: - can_manage_branding - organization - role Notification: type: object description: |- Read-only serializer for in-app notification objects. Works for both vintasend Notification dataclasses (returned by get_in_app_unread) and vintasend_django model instances (ORM rows). Fields: - id, title, notification_type, status: present on both dataclass and model. - body: rendered at read time via body_template + best-available context. - created, modified: model-only; None for dataclass instances. properties: id: type: string readOnly: true title: type: string readOnly: true notification_type: type: string readOnly: true status: type: string readOnly: true body: type: string description: |- Render the body template with the best-available context. Priority: 1. context_used — the context that was recorded at send time (on model rows, set by the backend when the notification was processed). 2. context_kwargs — the original kwargs passed at creation time. 3. Empty dict — render the template with no context (graceful degradation). Returns an empty string on rendering failure so the response always serialises. readOnly: true created: type: string nullable: true description: |- Return the creation timestamp as ISO 8601 string, or None for dataclasses. The vintasend Notification dataclass has no `created` attribute; only the vintasend_django ORM model does. readOnly: true modified: type: string nullable: true description: |- Return the last-modified timestamp as ISO 8601 string, or None for dataclasses. In vintasend 1.2.0 the Notification dataclass carries `modified`; ORM rows also have it. Returns None when the attribute is absent or None. readOnly: true required: - body - created - id - modified - notification_type - status - title Organization: type: object description: |- Serializer for Organization instances. The ``google_service_account`` field supports both reading and writing: - **Write**: accepts ``email``, ``admin_email``, ``private_key_id`` (write-only), and ``private_key`` (write-only). Omitting the field on PATCH leaves existing credentials unchanged. - **Read**: returns ``email``, ``admin_email``, and ``configured: true/false``. Secret fields are never returned. properties: id: type: integer readOnly: true name: type: string maxLength: 255 slug: type: string nullable: true maxLength: 63 should_sync_rooms: type: boolean description: Whether to sync rooms for this organization. external_event_update_policy: allOf: - $ref: '#/components/schemas/ExternalEventUpdatePolicyEnum' description: |- Policy for handling inbound external provider edits and deletions to synced events. ALLOW: apply directly. CHANGE_REQUEST: route to approval. FORBIDDEN: auto-undo. * `allow` - Allow direct updates * `change_request` - Updates create change requests * `forbidden` - Updates are forbidden week_start: allOf: - $ref: '#/components/schemas/OrganizationWeekStartEnum' description: |- Day of the week that starts the week for quota period boundaries. Used for calculating quota periods in group-scoped availability rules. * `monday` - Monday * `sunday` - Sunday google_service_account: type: object additionalProperties: {} nullable: true description: Return read-only service account info (no secrets), or None if unconfigured. readOnly: true can_invite_organizations: type: boolean readOnly: true description: Whether this organization can invite/create other organizations. DB/Django-admin only — never exposed via any API. Enables the whole reseller capability bundle. created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - can_invite_organizations - created - google_service_account - id - modified - name OrganizationBranding: type: object description: |- Serializer for OrganizationBranding (reseller-admin REST endpoints). Exposes app_name, logo_url, primary_color, secondary_color, support_email, and redirect_url. NEVER exposes can_invite_organizations or makes organization writable (the org is set from the acting org in the view). Validates: - Color format: #RRGGBB or #RRGGBBAA (regex). - redirect_url: HTTPS scheme, no wildcard character, no path-prefix pattern (organizations.redirect_url_validation). ``logo_url`` round-trips through ``BrandingLogoField``: reads return a time-limited signed URL for the stored object (``None`` when no logo is set), writes accept the uploaded S3 key from the ``branding_logos`` S3Direct destination. properties: app_name: type: string description: The display name of the white-labeled app (e.g., 'MyScheduler'). maxLength: 120 logo_url: type: string nullable: true primary_color: type: string description: 'Primary color as hex code: #RRGGBB or #RRGGBBAA.' maxLength: 9 secondary_color: type: string description: 'Secondary color as hex code: #RRGGBB or #RRGGBBAA.' maxLength: 9 support_email: type: string format: email description: Email address for the From/reply-to on branded transactional emails. maxLength: 254 redirect_url: type: string format: uri description: 'Single post-authentication redirect destination for this organization. Replaces the old return_url_allowlist: no caller-supplied redirect target is ever honored, so there is nothing to validate at request time and no open-redirect surface. Must be HTTPS with no wildcard character and no path-prefix pattern (organizations.redirect_url_validation).' maxLength: 200 required: - app_name OrganizationBrandingLogoUploadParams: type: object description: |- Response body for ``OrganizationBrandingLogoUploadParamsView`` — the same shape ``organizations.branding_logo.sign_branding_logo_upload`` and the GraphQL ``create_branding_logo_upload`` mutation return. ``upload_url`` is a complete SigV4 presigned PUT URL. The client uploads by PUTting the file body straight to it with a matching Content-Type and no other headers — no AWS credentials reach the browser and nothing has to be signed client-side. Mirrors ``users.serializers.ProfilePictureUploadParamsSerializer``. properties: object_key: type: string upload_url: type: string format: uri expires_in: type: integer required: - expires_in - object_key - upload_url OrganizationBrandingLogoUploadParamsRequest: type: object description: |- Request body for ``OrganizationBrandingLogoUploadParamsView``. Mirrors ``users.serializers.ProfilePictureUploadParamsRequestSerializer``. properties: file_name: type: string file_type: type: string file_size: type: integer minimum: 1 required: - file_name - file_size - file_type OrganizationBrief: type: object description: |- Lightweight read-only serializer for an Organization. Exposes only the fields needed for the org-switcher list: ``id``, ``name``, and the read-only ``slug`` (so the frontend can render/link the branded login URL without a second request). Intentionally avoids the heavier ``OrganizationSerializer`` (which loads the Google service account) to keep ``GET /organizations/mine/`` fast. properties: id: type: integer readOnly: true name: type: string readOnly: true slug: type: string readOnly: true nullable: true description: 'Public, URL-safe identifier used by the organization''s branded login page and by brandingForTenant. Optional until the organization sets one self-serve; stored as NULL (never empty string) when unset — default=None keeps a field left blank in a form/serializer NULL rather than '''', which is what lets the unique index admit any number of organizations with no slug. Mutable after set: changing it orphans previously-issued branded login URLs, which then fall back to the default identity rather than erroring. Format, reserved-word, and confusable-character rules live in organizations.slug_validation and are enforced by each write surface (REST serializer, admin form, GraphQL input), not here.' pattern: ^[-a-zA-Z0-9_]+$ required: - id - name - slug OrganizationInvitation: type: object description: Serializer for managing OrganizationInvitation instances. properties: id: type: integer readOnly: true email: type: string format: email maxLength: 254 first_name: type: string maxLength: 150 last_name: type: string maxLength: 150 organization: type: integer readOnly: true invited_by: type: integer readOnly: true nullable: true accepted_at: type: string format: date-time readOnly: true nullable: true expires_at: type: string format: date-time readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - accepted_at - created - email - expires_at - id - invited_by - modified - organization OrganizationMembership: type: object description: |- Read-only serializer for listing and retrieving organization members. Exposes membership role, active status, and flattened user information (email, first_name, last_name) for the admin datatable. properties: user_id: type: integer readOnly: true organization_id: type: integer readOnly: true role: allOf: - $ref: '#/components/schemas/RoleEnum' readOnly: true description: |- Role the user holds in this organization. Admins can manage organization-scoped resources (e.g. CalendarGroups) regardless of direct ownership. * `member` - Member * `admin` - Admin is_active: type: boolean readOnly: true description: 'Whether this membership is active. Inactive memberships are treated as gated: the user still has a row but loses all tenant-scoped access until reactivated. Use this to disable a user without deleting their membership record (which would lose role/history). Default True keeps every existing read unchanged.' user_email: type: string format: email readOnly: true user_first_name: type: string readOnly: true user_last_name: type: string readOnly: true required: - is_active - organization_id - role - user_email - user_first_name - user_id - user_last_name OrganizationWeekStartEnum: enum: - monday - sunday type: string description: |- * `monday` - Monday * `sunday` - Sunday OwnershipMembership: type: object description: |- Membership identity for a calendar owner. A membership has no scalar id (it is identified by the ``(user_id, organization_id)`` pair), so the representation exposes that pair plus the membership ``role``. properties: user_id: type: integer readOnly: true organization_id: type: integer readOnly: true role: type: string readOnly: true required: - organization_id - role - user_id PaginatedAvailableTimeList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/AvailableTime' PaginatedBillingPlanList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/BillingPlan' PaginatedBlockedTimeList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/BlockedTime' PaginatedBookableSlotProposalList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/BookableSlotProposal' PaginatedBookingPolicyList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/BookingPolicy' PaginatedCalendarEventList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/CalendarEvent' PaginatedCalendarGroupList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/CalendarGroup' PaginatedCalendarGroupRangeAvailabilityList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/CalendarGroupRangeAvailability' PaginatedCalendarList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/Calendar' PaginatedExternalEventChangeRequestList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/ExternalEventChangeRequest' PaginatedGroupScopedAvailabilityWindowList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/GroupScopedAvailabilityWindow' PaginatedGroupScopedBlockedTimeList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/GroupScopedBlockedTime' PaginatedGroupScopedQuotaRuleList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/GroupScopedQuotaRule' PaginatedOrganizationInvitationList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/OrganizationInvitation' PaginatedOrganizationMembershipList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/OrganizationMembership' PaginatedPolicyDocumentList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/PolicyDocument' PaginatedServiceAccountReadList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/ServiceAccountRead' PaginatedSystemUserTokenList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/SystemUserToken' PaginatedWebhookConfigurationList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/WebhookConfiguration' PaginatedWebhookEventList: type: object required: - count - results properties: count: type: integer example: 123 next: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=400&limit=100 previous: type: string nullable: true format: uri example: http://api.example.org/accounts/?offset=200&limit=100 results: type: array items: $ref: '#/components/schemas/WebhookEvent' ParentEvent: type: object properties: id: type: integer readOnly: true title: type: string maxLength: 255 external_id: type: string readOnly: true start_time: type: string format: date-time readOnly: true end_time: type: string format: date-time readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - created - end_time - external_id - id - modified - start_time - title PatchedAvailableTime: type: object description: Serializer for AvailableTime model with recurring support. properties: id: type: integer readOnly: true start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string maxLength: 50 recurrence_rule: allOf: - $ref: '#/components/schemas/RecurrenceRule' description: Recurrence rule data for creating recurring available times rrule_string: type: string writeOnly: true description: RRULE string for creating recurring available times is_recurring_instance: type: boolean description: True if this is an instance of a recurring available time readOnly: true is_recurring: type: boolean description: True if this is a recurring available time readOnly: true parent_available_time: type: object properties: id: type: integer required: - id nullable: true description: Get parent available time for instances. readOnly: true recurrence_id: type: string format: date-time readOnly: true nullable: true description: For recurring instances, this identifies which occurrence this is created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true calendar: type: integer nullable: true PatchedBillingProfile: type: object description: Serializer for BillingProfile virtual model. properties: id: type: integer readOnly: true contact_first_name: type: string maxLength: 255 contact_last_name: type: string maxLength: 255 contact_email: type: string format: email maxLength: 254 contact_phone: type: string maxLength: 50 document_type: type: string maxLength: 50 document_number: type: string maxLength: 50 billing_address: $ref: '#/components/schemas/BillingAddress' created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true PatchedBlockedTime: type: object description: Serializer for BlockedTime model with recurring support. properties: id: type: integer readOnly: true start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string maxLength: 50 reason: type: string maxLength: 255 recurrence_rule: allOf: - $ref: '#/components/schemas/RecurrenceRule' description: Recurrence rule data for creating recurring blocked times rrule_string: type: string writeOnly: true description: RRULE string for creating recurring blocked times external_id: type: string readOnly: true is_recurring_instance: type: boolean description: True if this is an instance of a recurring blocked time readOnly: true is_recurring: type: boolean description: True if this is a recurring blocked time readOnly: true parent_blocked_time: type: object properties: id: type: integer reason: type: string nullable: true required: - id - reason nullable: true description: Get parent blocked time for instances. readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true calendar: type: integer nullable: true PatchedBookingPolicy: type: object description: |- Serializer for ``BookingPolicy`` CRUD. Exactly one of ``calendar``, ``membership_user_id``, ``calendar_group``, or ``is_organization_default`` must be set on create. Targets are immutable after creation — only the four rule-field seconds are writable on update. Validation: - ``validate()`` enforces the exactly-one-target invariant on create. - ``validate_membership_user_id()`` checks that the supplied user id belongs to the caller's organization (on create only; targets are immutable on update). - ``DuplicateBookingPolicyError`` from the service is caught and surfaced as a 400 validation error so the client gets a named conflict message. - The four rule fields use ``min_value=0`` so DRF rejects negatives with a clear field-level 400 before the value reaches the model's ``PositiveIntegerField`` constraint. Write paths (create / update) delegate to ``BookingPolicyService`` stored on the serializer context as ``"booking_policy_service"`` (the viewset sets it). properties: id: type: integer readOnly: true calendar: type: integer nullable: true calendar_group: type: integer nullable: true membership_user_id: type: integer nullable: true is_organization_default: type: boolean default: false lead_time_seconds: type: integer minimum: 0 default: 0 max_horizon_seconds: type: integer minimum: 0 default: 0 buffer_before_seconds: type: integer minimum: 0 default: 0 buffer_after_seconds: type: integer minimum: 0 default: 0 created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true PatchedCalendar: type: object properties: id: type: integer readOnly: true name: type: string maxLength: 255 description: type: string email: type: string format: email readOnly: true external_id: type: string readOnly: true provider: allOf: - $ref: '#/components/schemas/ProviderEnum' readOnly: true calendar_type: allOf: - $ref: '#/components/schemas/CalendarTypeEnum' readOnly: true description: |- The type of calendar. Personal calendars are for individual use, resource calendars are for shared resources, and virtual calendars are for online meetings or events. * `personal` - Personal Calendar * `resource` - Resource Calendar * `virtual` - Virtual Calendar * `bundle` - Bundle Calendar capacity: type: integer maximum: 2147483647 minimum: 0 nullable: true description: The maximum number of attendees that can be accommodated in this calendar's events. This is only applicable for resource calendars. manage_available_windows: type: boolean description: If true, this calendar can manage its own available time windows. If not, it will use the available time windows of the external calendar it's attached to. visibility: allOf: - $ref: '#/components/schemas/VisibilityEnum' description: |- Controls how this calendar appears in queries. active: listed and available for booking (default). unlisted: hidden from listing/booking queries but still synced for conflict detection; survives re-import so user opt-out is preserved. inactive: soft-deleted, hidden from all queries and not synced. Use DELETE /calendars/{id}/ to transition to inactive instead of hard-deleting. * `active` - Active * `unlisted` - Unlisted * `inactive` - Inactive sync_enabled: type: boolean description: Whether this calendar's events are pulled from the external provider. Set to False to skip syncing calendars that aren't useful for scheduling — holidays, birthdays, organization-wide events, etc. When False, no new CalendarSync is requested for this calendar (including webhook- and import-triggered syncs); previously synced events are left untouched. Default True keeps existing calendars syncing as before. PatchedCalendarBundleUpdate: type: object description: Serializer for updating a bundle calendar's child calendars and primary calendar. properties: bundle_calendars: type: array items: type: integer primary_calendar: type: integer nullable: true PatchedCalendarEvent: type: object properties: id: type: integer readOnly: true provider: type: string writeOnly: true title: type: string maxLength: 255 description: type: string start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string maxLength: 50 created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true external_id: type: string readOnly: true external_attendances: type: array items: $ref: '#/components/schemas/EventExternalAttendance' attendances: type: array items: $ref: '#/components/schemas/EventAttendance' resource_allocations: type: array items: $ref: '#/components/schemas/ResourceAllocation' recurrence_rule: allOf: - $ref: '#/components/schemas/RecurrenceRule' description: Recurrence rule data for creating recurring events rrule_string: type: string writeOnly: true description: RRULE string for creating recurring events parent_recurring_object_id: type: integer writeOnly: true description: ID of parent event for recurring instances parent_recurring_object: allOf: - $ref: '#/components/schemas/ParentEvent' readOnly: true is_recurring_instance: type: boolean description: True if this is an instance of a recurring event readOnly: true is_recurring: type: boolean description: True if this is a recurring event readOnly: true is_recurring_exception: type: boolean description: True if this object is an exception to the recurrence rule (modified occurrence) recurrence_id: type: string format: date-time nullable: true description: For recurring instances, this identifies which occurrence this is google_calendar_service_account: type: integer writeOnly: true calendar: type: integer writeOnly: true PatchedCalendarGroup: type: object properties: id: type: integer readOnly: true name: type: string maxLength: 255 description: type: string slots: type: array items: $ref: '#/components/schemas/CalendarGroupSlot' created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true PatchedGroupScopedAvailabilityWindowUpdate: type: object description: |- Input for partially updating a group-scoped availability window. Every field is optional -- only provided fields change, mirroring ``CalendarGroupService.update_group_scoped_availability_window``. properties: start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string rrule_string: type: string nullable: true description: RRULE string for a recurring window. Set to null to make it non-recurring. PatchedGroupScopedBlockedTimeUpdate: type: object description: |- Input for partially updating a group-scoped blocked time. Every field is optional -- only provided fields change, mirroring ``CalendarGroupService.update_group_scoped_blocked_time``. properties: start_time: type: string format: date-time end_time: type: string format: date-time timezone: type: string reason: type: string rrule_string: type: string nullable: true description: RRULE string for a recurring block. Set to null to make it non-recurring. PatchedGroupScopedQuotaRuleUpdate: type: object description: |- Input for partially updating a group-scoped quota rule. Every field is optional -- only provided fields change, mirroring ``CalendarGroupService.update_group_scoped_quota_rule``. properties: period: $ref: '#/components/schemas/PeriodEnum' cap: type: integer minimum: 1 PatchedOrganization: type: object description: |- Serializer for Organization instances. The ``google_service_account`` field supports both reading and writing: - **Write**: accepts ``email``, ``admin_email``, ``private_key_id`` (write-only), and ``private_key`` (write-only). Omitting the field on PATCH leaves existing credentials unchanged. - **Read**: returns ``email``, ``admin_email``, and ``configured: true/false``. Secret fields are never returned. properties: id: type: integer readOnly: true name: type: string maxLength: 255 slug: type: string nullable: true maxLength: 63 should_sync_rooms: type: boolean description: Whether to sync rooms for this organization. external_event_update_policy: allOf: - $ref: '#/components/schemas/ExternalEventUpdatePolicyEnum' description: |- Policy for handling inbound external provider edits and deletions to synced events. ALLOW: apply directly. CHANGE_REQUEST: route to approval. FORBIDDEN: auto-undo. * `allow` - Allow direct updates * `change_request` - Updates create change requests * `forbidden` - Updates are forbidden week_start: allOf: - $ref: '#/components/schemas/OrganizationWeekStartEnum' description: |- Day of the week that starts the week for quota period boundaries. Used for calculating quota periods in group-scoped availability rules. * `monday` - Monday * `sunday` - Sunday google_service_account: type: object additionalProperties: {} nullable: true description: Return read-only service account info (no secrets), or None if unconfigured. readOnly: true can_invite_organizations: type: boolean readOnly: true description: Whether this organization can invite/create other organizations. DB/Django-admin only — never exposed via any API. Enables the whole reseller capability bundle. created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true PatchedOrganizationBranding: type: object description: |- Serializer for OrganizationBranding (reseller-admin REST endpoints). Exposes app_name, logo_url, primary_color, secondary_color, support_email, and redirect_url. NEVER exposes can_invite_organizations or makes organization writable (the org is set from the acting org in the view). Validates: - Color format: #RRGGBB or #RRGGBBAA (regex). - redirect_url: HTTPS scheme, no wildcard character, no path-prefix pattern (organizations.redirect_url_validation). ``logo_url`` round-trips through ``BrandingLogoField``: reads return a time-limited signed URL for the stored object (``None`` when no logo is set), writes accept the uploaded S3 key from the ``branding_logos`` S3Direct destination. properties: app_name: type: string description: The display name of the white-labeled app (e.g., 'MyScheduler'). maxLength: 120 logo_url: type: string nullable: true primary_color: type: string description: 'Primary color as hex code: #RRGGBB or #RRGGBBAA.' maxLength: 9 secondary_color: type: string description: 'Secondary color as hex code: #RRGGBB or #RRGGBBAA.' maxLength: 9 support_email: type: string format: email description: Email address for the From/reply-to on branded transactional emails. maxLength: 254 redirect_url: type: string format: uri description: 'Single post-authentication redirect destination for this organization. Replaces the old return_url_allowlist: no caller-supplied redirect target is ever honored, so there is nothing to validate at request time and no open-redirect surface. Must be HTTPS with no wildcard character and no path-prefix pattern (organizations.redirect_url_validation).' maxLength: 200 PatchedProfile: type: object properties: id: type: integer readOnly: true first_name: type: string maxLength: 255 last_name: type: string maxLength: 255 profile_picture: type: string nullable: true maxLength: 255 PatchedServiceAccountWrite: type: object description: |- Write serializer for creating/rotating the org-level service account. ``private_key`` and ``private_key_id`` are write-only and are never echoed back in any response (reads go through ``ServiceAccountReadSerializer``). properties: email: type: string format: email maxLength: 254 admin_email: type: string format: email description: Google Workspace super-admin email used as the DWD impersonation subject. The service account must have domain-wide delegation granted for the Admin SDK and Calendar API scopes in the Google Admin Console. maxLength: 255 private_key_id: type: string writeOnly: true maxLength: 255 private_key: type: string writeOnly: true PatchedSystemUserTokenUpdate: type: object description: |- Input serializer for updating a public-API token's resource grants. Accepts ``available_resources`` (a non-empty list of valid ``PublicAPIResources`` values) only. ``integration_name`` and ``token`` are never accepted or changed. The view reconciles ResourceAccess rows: adds rows for newly-granted resources, removes rows for dropped resources. properties: available_resources: type: array items: $ref: '#/components/schemas/AvailableResourcesEnum' PatchedWebhookConfiguration: type: object properties: id: type: integer readOnly: true event_type: $ref: '#/components/schemas/EventTypeEnum' url: type: string format: uri maxLength: 2000 headers: {} PaymentProviderEnum: enum: - mercadopago - stripe type: string description: |- * `mercadopago` - MercadoPago * `stripe` - Stripe PendingBillingIntervalEnum: enum: - monthly - annual type: string description: |- * `monthly` - Monthly * `annual` - Annual PeriodEnum: enum: - day - week - month type: string description: |- * `day` - Day * `week` - Week * `month` - Month PlanEntitlement: type: object properties: entitlement_key: allOf: - $ref: '#/components/schemas/EntitlementKeyEnum' readOnly: true is_enabled: type: boolean readOnly: true required: - entitlement_key - is_enabled PlanLimit: type: object properties: resource_key: allOf: - $ref: '#/components/schemas/ResourceKeyEnum' readOnly: true limit_value: type: integer readOnly: true nullable: true kind: allOf: - $ref: '#/components/schemas/PlanLimitKindEnum' readOnly: true overage_unit_price: type: string format: decimal pattern: ^-?\d{0,6}(?:\.\d{0,4})?$ readOnly: true nullable: true required: - kind - limit_value - overage_unit_price - resource_key PlanLimitKindEnum: enum: - prepaid - postpaid type: string description: |- * `prepaid` - Prepaid * `postpaid` - Postpaid PolicyDocument: type: object description: |- Read-only representation of a published :class:`PolicyDocument` version. Every field is read-only — this app exposes no write surface for policy documents over the REST API; documents are authored in Django admin. properties: id: type: integer readOnly: true document_type: allOf: - $ref: '#/components/schemas/DocumentTypeEnum' readOnly: true version: type: integer readOnly: true description: Monotonically increasing per document_type. title: type: string readOnly: true body_markdown: type: string readOnly: true description: Raw markdown body, rendered client-side. published_at: type: string format: date-time readOnly: true required: - body_markdown - document_type - id - published_at - title - version Profile: type: object properties: id: type: integer readOnly: true first_name: type: string maxLength: 255 last_name: type: string maxLength: 255 profile_picture: type: string nullable: true maxLength: 255 required: - id ProfilePictureUploadParams: type: object properties: object_key: type: string upload_url: type: string format: uri expires_in: type: integer required: - expires_in - object_key - upload_url ProfilePictureUploadParamsRequest: type: object properties: file_name: type: string file_type: type: string file_size: type: integer minimum: 1 required: - file_name - file_size - file_type ProviderEnum: enum: - internal - google - microsoft - apple - ics type: string description: |- * `internal` - Internal Calendar * `google` - Google Calendar * `microsoft` - Microsoft Outlook Calendar * `apple` - Apple Calendar * `ics` - ICS RSVPStatusEnum: enum: - accepted - declined - pending type: string description: |- * `accepted` - Accepted * `declined` - Declined * `pending` - Pending RecurrenceRule: type: object properties: id: type: integer readOnly: true frequency: allOf: - $ref: '#/components/schemas/FrequencyEnum' description: |- How often the event repeats (DAILY, WEEKLY, MONTHLY, YEARLY) * `DAILY` - Daily * `WEEKLY` - Weekly * `MONTHLY` - Monthly * `YEARLY` - Yearly interval: type: integer maximum: 2147483647 minimum: 0 description: The interval between each frequency iteration (e.g., every 2 weeks) count: type: integer maximum: 2147483647 minimum: 0 nullable: true description: Number of occurrences after which the recurrence ends until: type: string format: date-time nullable: true description: The date and time until which the recurrence is valid by_weekday: type: string description: Comma-separated list of weekdays (e.g., 'MO,WE,FR') maxLength: 100 by_month_day: type: string description: Comma-separated list of month days (e.g., '1,15,-1' for 1st, 15th, last day) maxLength: 100 by_month: type: string description: Comma-separated list of months (1-12) maxLength: 50 by_year_day: type: string description: Comma-separated list of year days (1-366 or -366 to -1) maxLength: 100 by_week_number: type: string description: Comma-separated list of week numbers (1-53 or -53 to -1) maxLength: 100 by_hour: type: string description: Comma-separated list of hours (0-23) maxLength: 100 by_minute: type: string description: Comma-separated list of minutes (0-59) maxLength: 200 by_second: type: string description: Comma-separated list of seconds (0-59) maxLength: 200 week_start: allOf: - $ref: '#/components/schemas/RecurrenceRuleWeekStartEnum' description: |- First day of the week * `MO` - Monday * `TU` - Tuesday * `WE` - Wednesday * `TH` - Thursday * `FR` - Friday * `SA` - Saturday * `SU` - Sunday rrule_string: type: string readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - created - frequency - id - modified - rrule_string RecurrenceRuleWeekStartEnum: enum: - MO - TU - WE - TH - FR - SA - SU type: string description: |- * `MO` - Monday * `TU` - Tuesday * `WE` - Wednesday * `TH` - Thursday * `FR` - Friday * `SA` - Saturday * `SU` - Sunday ResourceAllocation: type: object properties: id: type: integer nullable: true description: ID of the external attendee. calendar: type: integer status: allOf: - $ref: '#/components/schemas/RSVPStatusEnum' readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - calendar - created - modified - status ResourceCalendarCreate: type: object description: Create an internal (manual) resource calendar. Admin-gated at the view layer. properties: name: type: string maxLength: 255 description: type: string capacity: type: integer maximum: 2147483647 minimum: 0 nullable: true description: The maximum number of attendees that can be accommodated in this calendar's events. This is only applicable for resource calendars. manage_available_windows: type: boolean description: If true, this calendar can manage its own available time windows. If not, it will use the available time windows of the external calendar it's attached to. required: - name ResourceKeyEnum: enum: - organization_members - resource_calendars - calendar_groups - bundle_calendars - availability_windows - webhook_subscriptions - public_api_system_users - event_occurrences type: string description: |- * `organization_members` - Organization members * `resource_calendars` - Resource calendars * `calendar_groups` - Calendar groups * `bundle_calendars` - Bundle calendars * `availability_windows` - Availability windows * `webhook_subscriptions` - Webhook subscriptions * `public_api_system_users` - Public API system users * `event_occurrences` - Event occurrences RoleEnum: enum: - member - admin type: string description: |- * `member` - Member * `admin` - Admin ServiceAccountRead: type: object description: |- Read serializer for the org-level Google Calendar service account (CRUD surface). Exposes only non-secret fields plus a ``configured`` flag. ``private_key`` and ``private_key_id`` are never returned. properties: id: type: integer readOnly: true email: type: string format: email readOnly: true admin_email: type: string format: email readOnly: true description: Google Workspace super-admin email used as the DWD impersonation subject. The service account must have domain-wide delegation granted for the Admin SDK and Calendar API scopes in the Google Admin Console. configured: type: boolean description: A persisted row is, by definition, configured. readOnly: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - admin_email - configured - created - email - id - modified ServiceAccountWrite: type: object description: |- Write serializer for creating/rotating the org-level service account. ``private_key`` and ``private_key_id`` are write-only and are never echoed back in any response (reads go through ``ServiceAccountReadSerializer``). properties: email: type: string format: email maxLength: 254 admin_email: type: string format: email description: Google Workspace super-admin email used as the DWD impersonation subject. The service account must have domain-wide delegation granted for the Admin SDK and Calendar API scopes in the Google Admin Console. maxLength: 255 private_key_id: type: string writeOnly: true maxLength: 255 private_key: type: string writeOnly: true required: - email - private_key - private_key_id SourceEnum: enum: - signup_form - oauth_step - api type: string description: |- * `signup_form` - Signup Form * `oauth_step` - OAuth Consent Step * `api` - API Subscription: type: object description: Serializer for Subscription virtual model. properties: id: type: integer readOnly: true plan: allOf: - $ref: '#/components/schemas/BillingPlan' readOnly: true billing_state: allOf: - $ref: '#/components/schemas/BillingStateEnum' readOnly: true billing_interval: allOf: - $ref: '#/components/schemas/PendingBillingIntervalEnum' readOnly: true payment_provider: allOf: - $ref: '#/components/schemas/PaymentProviderEnum' readOnly: true current_period_start: type: string format: date-time readOnly: true current_period_end: type: string format: date-time readOnly: true grace_period_ends_at: type: string format: date-time readOnly: true nullable: true pending_plan_slug: type: string readOnly: true pending_billing_interval: allOf: - $ref: '#/components/schemas/PendingBillingIntervalEnum' readOnly: true pending_plan_effective_at: type: string format: date-time readOnly: true nullable: true add_ons: type: array items: $ref: '#/components/schemas/SubscriptionAddOn' readOnly: true required: - add_ons - billing_interval - billing_state - current_period_end - current_period_start - grace_period_ends_at - id - payment_provider - pending_billing_interval - pending_plan_effective_at - pending_plan_slug - plan SubscriptionAddOn: type: object properties: id: type: integer readOnly: true resource_key: allOf: - $ref: '#/components/schemas/ResourceKeyEnum' readOnly: true quantity: type: integer readOnly: true is_recurring: type: boolean readOnly: true is_active: type: boolean readOnly: true external_id: type: string readOnly: true created: type: string format: date-time readOnly: true required: - created - external_id - id - is_active - is_recurring - quantity - resource_key SystemUserToken: type: object description: |- Read-only serializer for listing and retrieving public-API tokens. Exposes ``id``, ``integration_name``, ``is_active``, ``available_resources`` (list of resource_name strings from the related ``ResourceAccess`` rows), and ``scoped_to_user`` (the owner's User id from the denormalized membership column, null for org-wide tokens). The REST field name ``scoped_to_user`` is kept for API stability; the value is the denormalized ``scoped_to_membership_user_id``. Never exposes ``long_lived_token_hash`` or ``token``. Optimized for list queries: uses prefetched ``available_resources`` from the viewset's ``get_queryset`` to avoid N+1 queries. ``scoped_to_membership_user_id`` is a concrete column on the row, so it needs no join. properties: id: type: integer readOnly: true integration_name: type: string readOnly: true is_active: type: boolean readOnly: true description: Indicates if the user is active. available_resources: type: array items: type: string description: Return a list of resource_name values from the prefetched ResourceAccess rows. readOnly: true scoped_to_user: type: integer nullable: true description: |- Return the owner's User id from the denormalized membership column, or None. ``scoped_to_membership_user_id`` is a concrete column already storing the membership's user_id, so the value is returned directly with no extra query. readOnly: true required: - available_resources - id - integration_name - is_active - scoped_to_user SystemUserTokenCreate: type: object description: |- Input serializer for creating a new public-API token (SystemUser + ResourceAccess rows). Accepts ``integration_name``, ``available_resources`` (a non-empty list of valid ``PublicAPIResources`` values), and an optional ``scoped_to_user`` (internal User id). ``create()`` provisions the ``SystemUser`` via the injected auth service and bulk-creates the ``ResourceAccess`` grants, attaching the write-once plaintext ``token`` to the returned instance. Never stores or exposes ``long_lived_token_hash``. When ``scoped_to_user`` is supplied and non-null: - The referenced user must be an active member of the caller's organisation. - ``available_resources`` must be a subset of ``PROVIDER_SCOPED_RESOURCES``. When ``scoped_to_user`` is absent or null, behaviour is exactly as before: any valid ``PublicAPIResources`` value is accepted and the token is org-wide. properties: integration_name: type: string maxLength: 150 available_resources: type: array items: $ref: '#/components/schemas/AvailableResourcesEnum' scoped_to_user: type: integer nullable: true required: - available_resources - integration_name SystemUserTokenResponse: type: object description: |- Read serializer for the created SystemUser. Includes the write-once ``token`` field (sourced from the view) and ``available_resources`` (derived from the related ``ResourceAccess`` rows). Exposes ``scoped_to_user`` as the owner's User id derived from the stored membership reference (null for org-wide tokens). The REST field name ``scoped_to_user`` is kept for API stability; the value is the denormalized ``scoped_to_membership_user_id``. Never exposes ``long_lived_token_hash``. properties: id: type: integer readOnly: true integration_name: type: string readOnly: true is_active: type: boolean readOnly: true description: Indicates if the user is active. available_resources: type: array items: type: string description: Return a list of resource_name values from the related ResourceAccess rows. readOnly: true scoped_to_user: type: integer nullable: true description: |- Return the owner's User id from the denormalized membership column, or None. ``scoped_to_membership_user_id`` already stores the membership's user_id, so the value is returned directly with no extra query. readOnly: true token: type: string readOnly: true required: - available_resources - id - integration_name - is_active - scoped_to_user - token SystemUserTokenUpdate: type: object description: |- Input serializer for updating a public-API token's resource grants. Accepts ``available_resources`` (a non-empty list of valid ``PublicAPIResources`` values) only. ``integration_name`` and ``token`` are never accepted or changed. The view reconciles ResourceAccess rows: adds rows for newly-granted resources, removes rows for dropped resources. properties: available_resources: type: array items: $ref: '#/components/schemas/AvailableResourcesEnum' required: - available_resources TriggerSourceEnum: enum: - import - manual - webhook - admin type: string description: |- * `import` - Import * `manual` - Manual * `webhook` - Webhook * `admin` - Admin UnavailableTimeWindow: type: object properties: id: type: integer reason: type: string start_time: type: string format: date-time end_time: type: string format: date-time reason_description: type: string readOnly: true required: - end_time - id - reason - reason_description - start_time UpdateMembershipRole: type: object description: Request serializer for updating an organization member's role. properties: role: $ref: '#/components/schemas/RoleEnum' required: - role UsageResponse: type: object properties: billing_state: type: string limits: type: array items: $ref: '#/components/schemas/EffectiveLimitUsage' required: - billing_state - limits UserConsent: type: object description: |- Read-only representation of a recorded :class:`UserConsent`. Returned by ``ConsentViewSet.create`` after ``ConsentService.record_consent`` persists the acceptance; every field is read-only here too. properties: id: type: integer readOnly: true document_type: type: string readOnly: true policy_document: type: integer readOnly: true policy_document_version: type: integer readOnly: true source: allOf: - $ref: '#/components/schemas/SourceEnum' readOnly: true accepted_at: type: string format: date-time readOnly: true ip_address: type: string readOnly: true nullable: true user_agent: type: string readOnly: true phone_number: type: string readOnly: true required: - accepted_at - document_type - id - ip_address - phone_number - policy_document - policy_document_version - source - user_agent VisibilityEnum: enum: - active - unlisted - inactive type: string description: |- * `active` - Active * `unlisted` - Unlisted * `inactive` - Inactive WebhookConfiguration: type: object properties: id: type: integer readOnly: true event_type: $ref: '#/components/schemas/EventTypeEnum' url: type: string format: uri maxLength: 2000 headers: {} required: - id - url WebhookEvent: type: object properties: configuration: type: integer readOnly: true main_event: type: integer readOnly: true event_type: $ref: '#/components/schemas/EventTypeEnum' url: type: string format: uri maxLength: 2000 status: allOf: - $ref: '#/components/schemas/WebhookEventStatusEnum' readOnly: true headers: {} payload: {} response_status: type: integer readOnly: true nullable: true response_body: readOnly: true nullable: true response_headers: readOnly: true nullable: true retry_number: type: integer readOnly: true nullable: true send_after: type: string format: date-time readOnly: true nullable: true created: type: string format: date-time readOnly: true modified: type: string format: date-time readOnly: true required: - configuration - created - event_type - main_event - modified - payload - response_body - response_headers - response_status - retry_number - send_after - status - url WebhookEventDoc: type: object description: |- Read-only catalog entry for a single ``WebhookEventType`` member. Plain ``Serializer`` over a dict built from the enum and ``webhooks.constants.WEBHOOK_EVENT_DESCRIPTIONS`` — there is no model backing this. properties: value: type: string readOnly: true label: type: string readOnly: true description: type: string readOnly: true required: - description - label - value WebhookEventStatusEnum: enum: - pending - success - failed type: string description: |- * `pending` - Pending * `success` - Success * `failed` - Failed _CalendarGroupSlotSelectionInput: type: object properties: slot_id: type: integer calendar_ids: type: array items: type: integer required: - calendar_ids - slot_id _RangeInput: type: object properties: start_time: type: string format: date-time end_time: type: string format: date-time required: - end_time - start_time securitySchemes: cookieAuth: type: apiKey in: cookie name: sessionid jwtAuth: type: http scheme: bearer bearerFormat: JWT