> ## Documentation Index
> Fetch the complete documentation index at: https://absentify.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Preview recurring requests (dry run)

> Answer what POST /requests/recurring would do with this payload without creating anything: the number of occurrences, the summed workday duration and its split by fiscal year, whether the member's allowance covers the whole series, and the per-occurrence conflicts.

**Input:** identical to POST /requests/recurring. `files` is accepted and ignored. `reason`, `admin_approval_option`, `ignoreMaximumAbsence` and `representative_member_ids` are validated exactly as on create.

**Duration:** `duration.workday_absence_duration` follows the same days-vs-minutes rule as `workday_absence_duration` on GET /requests and is the sum over all occurrences; preview and created series always agree. `per_year` splits that sum by fiscal year.

**Allowance:** `is_allowance_sufficient` is one verdict for the whole series, matching the all-or-nothing behaviour of POST /requests/recurring: `false` means creating the series fails with the allowance error and leaves nothing behind. Leave types that do not deduct return `true`.

**Conflicts:** `conflicts` lists occurrences overlapping the member's existing requests, `department_limit_conflicts` the occurrences on which a department's maximum absent limit is already reached, `representative_overlaps` the occurrences on which the member is someone else's representative (pending or accepted), and `representative_unavailable` the occurrences on which a passed representative is absent.

**Rejection:** when the payload would be rejected with 400 by POST /requests/recurring, the response is still 200 with `rejection` set and `can_create: false`. Match on `rejection.code`: neither `until` nor `count` given (RECURRENCE_END_REQUIRED), day/month/pos/weekDays not matching `repeat` (RECURRENCE_RULE_INVALID), a series reaching more than 365 days into the future (RECURRENCE_WINDOW_EXCEEDED), fewer than two occurrences (RECURRENCE_TOO_FEW_OCCURRENCES), an hour-based leave type without a valid start_time/end_time (HOUR_LEAVE_TYPE_REQUIRES_TIME_RANGE), a half_day on a full-day-only leave type (HALF_DAY_NOT_ALLOWED_FOR_FULL_DAY_LEAVE_TYPE), or an occurrence outside the member's employment period (OUTSIDE_EMPLOYMENT_PERIOD). After that the rules the create endpoint applies to every occurrence follow, in its order: a leave type disabled for the member (LEAVE_TYPE_DISABLED), a missing or too long reason (REASON_REQUIRED, REASON_TOO_LONG), an occurrence outside the bookable fiscal-year window (OUTSIDE_FISCAL_YEAR_WINDOW) or too far in the past for the caller's role (PAST_DATE_NOT_ALLOWED), an occurrence on which a non-admin is someone else's accepted representative (REPRESENTATIVE_CONFLICT), a leave type needing approval while the member has no approver (NO_APPROVER_SET), and the representative requirement (REPRESENTATIVES_INVALID, REQUIRED_REPRESENTATIVES_COUNT_NOT_MET). A workspace without the Essentials plan, or a non-admin passing `admin_approval_option: approve_request_immediately`, gets the same 403 as the create endpoint.

**Verdict:** `can_create` is true only when `rejection` is null, `conflicts` is empty, `department_limit_conflicts` is empty (or the caller is an admin passing `ignoreMaximumAbsence`) and the allowance covers the series. `representative_unavailable` is informational.

**Side effects:** no request, approver, notification, webhook or calendar sync entry is created. Safe to repeat; each call evaluates the data as it is at that moment.



## OpenAPI

````yaml https://api.absentify.com/api/v1/openapi.json post /requests/recurring/preview
openapi: 3.1.0
info:
  title: absentify CRUD API
  description: OpenAPI compliant REST API
  version: 1.0.0
servers:
  - url: https://api.absentify.com/api/v1
security: []
tags:
  - name: Departments
  - name: Leave types
  - name: Members
  - name: Requests
  - name: Public holidays
  - name: Workspace
  - name: Absences
  - name: Allowance Management
  - name: Out of office
externalDocs:
  url: https://absentify.com/docs/en/api-reference
paths:
  /requests/recurring/preview:
    post:
      tags:
        - Requests
      summary: Preview recurring requests (dry run)
      description: >-
        Answer what POST /requests/recurring would do with this payload without
        creating anything: the number of occurrences, the summed workday
        duration and its split by fiscal year, whether the member's allowance
        covers the whole series, and the per-occurrence conflicts.


        **Input:** identical to POST /requests/recurring. `files` is accepted
        and ignored. `reason`, `admin_approval_option`, `ignoreMaximumAbsence`
        and `representative_member_ids` are validated exactly as on create.


        **Duration:** `duration.workday_absence_duration` follows the same
        days-vs-minutes rule as `workday_absence_duration` on GET /requests and
        is the sum over all occurrences; preview and created series always
        agree. `per_year` splits that sum by fiscal year.


        **Allowance:** `is_allowance_sufficient` is one verdict for the whole
        series, matching the all-or-nothing behaviour of POST
        /requests/recurring: `false` means creating the series fails with the
        allowance error and leaves nothing behind. Leave types that do not
        deduct return `true`.


        **Conflicts:** `conflicts` lists occurrences overlapping the member's
        existing requests, `department_limit_conflicts` the occurrences on which
        a department's maximum absent limit is already reached,
        `representative_overlaps` the occurrences on which the member is someone
        else's representative (pending or accepted), and
        `representative_unavailable` the occurrences on which a passed
        representative is absent.


        **Rejection:** when the payload would be rejected with 400 by POST
        /requests/recurring, the response is still 200 with `rejection` set and
        `can_create: false`. Match on `rejection.code`: neither `until` nor
        `count` given (RECURRENCE_END_REQUIRED), day/month/pos/weekDays not
        matching `repeat` (RECURRENCE_RULE_INVALID), a series reaching more than
        365 days into the future (RECURRENCE_WINDOW_EXCEEDED), fewer than two
        occurrences (RECURRENCE_TOO_FEW_OCCURRENCES), an hour-based leave type
        without a valid start_time/end_time
        (HOUR_LEAVE_TYPE_REQUIRES_TIME_RANGE), a half_day on a full-day-only
        leave type (HALF_DAY_NOT_ALLOWED_FOR_FULL_DAY_LEAVE_TYPE), or an
        occurrence outside the member's employment period
        (OUTSIDE_EMPLOYMENT_PERIOD). After that the rules the create endpoint
        applies to every occurrence follow, in its order: a leave type disabled
        for the member (LEAVE_TYPE_DISABLED), a missing or too long reason
        (REASON_REQUIRED, REASON_TOO_LONG), an occurrence outside the bookable
        fiscal-year window (OUTSIDE_FISCAL_YEAR_WINDOW) or too far in the past
        for the caller's role (PAST_DATE_NOT_ALLOWED), an occurrence on which a
        non-admin is someone else's accepted representative
        (REPRESENTATIVE_CONFLICT), a leave type needing approval while the
        member has no approver (NO_APPROVER_SET), and the representative
        requirement (REPRESENTATIVES_INVALID,
        REQUIRED_REPRESENTATIVES_COUNT_NOT_MET). A workspace without the
        Essentials plan, or a non-admin passing `admin_approval_option:
        approve_request_immediately`, gets the same 403 as the create endpoint.


        **Verdict:** `can_create` is true only when `rejection` is null,
        `conflicts` is empty, `department_limit_conflicts` is empty (or the
        caller is an admin passing `ignoreMaximumAbsence`) and the allowance
        covers the series. `representative_unavailable` is informational.


        **Side effects:** no request, approver, notification, webhook or
        calendar sync entry is created. Safe to repeat; each call evaluates the
        data as it is at that moment.
      operationId: request-previewRecurringRequests
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                leave_type_id:
                  type: string
                  format: uuid
                  pattern: >-
                    ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
                requester_member_id:
                  type: string
                  format: uuid
                  pattern: >-
                    ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
                reason:
                  type: string
                repeat:
                  type: string
                  enum:
                    - daily
                    - weekly
                    - monthly
                interval:
                  type: integer
                  minimum: 1
                  maximum: 100
                from:
                  description: >-
                    Date/time in ISO 8601 format, e.g. `2026-06-01` (interpreted
                    as UTC midnight) or `2026-06-01T09:00:00Z`. Always include a
                    time zone designator (`Z` for UTC) when sending a time of
                    day. Localized formats such as `01.06.2026` or `06/01/2026`
                    are misinterpreted and must not be used.
                  type: string
                until:
                  anyOf:
                    - description: >-
                        Date/time in ISO 8601 format, e.g. `2026-06-01`
                        (interpreted as UTC midnight) or `2026-06-01T09:00:00Z`.
                        Always include a time zone designator (`Z` for UTC) when
                        sending a time of day. Localized formats such as
                        `01.06.2026` or `06/01/2026` are misinterpreted and must
                        not be used.
                      type: string
                    - type: 'null'
                count:
                  anyOf:
                    - type: integer
                      minimum: 1
                      maximum: 365
                    - type: 'null'
                start_time:
                  type: object
                  properties:
                    hour:
                      type: integer
                      minimum: 0
                      maximum: 23
                    minute:
                      type: integer
                      minimum: 0
                      maximum: 59
                  required:
                    - hour
                    - minute
                end_time:
                  type: object
                  properties:
                    hour:
                      type: integer
                      minimum: 0
                      maximum: 23
                    minute:
                      type: integer
                      minimum: 0
                      maximum: 59
                  required:
                    - hour
                    - minute
                half_day:
                  type: string
                  enum:
                    - full_day
                    - morning
                    - afternoon
                weekDays:
                  anyOf:
                    - type: string
                    - type: 'null'
                day:
                  anyOf:
                    - anyOf:
                        - type: integer
                          minimum: 1
                          maximum: 31
                        - type: string
                    - type: 'null'
                month:
                  anyOf:
                    - type: integer
                      minimum: 1
                      maximum: 12
                    - type: 'null'
                pos:
                  anyOf:
                    - anyOf:
                        - type: integer
                          minimum: -1
                          maximum: 5
                        - type: string
                    - type: 'null'
                exDates:
                  type: array
                  items:
                    description: >-
                      Date/time in ISO 8601 format, e.g. `2026-06-01`
                      (interpreted as UTC midnight) or `2026-06-01T09:00:00Z`.
                      Always include a time zone designator (`Z` for UTC) when
                      sending a time of day. Localized formats such as
                      `01.06.2026` or `06/01/2026` are misinterpreted and must
                      not be used.
                    type: string
                files:
                  type: array
                  items:
                    type: string
                    format: uuid
                    pattern: >-
                      ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
                representative_member_ids:
                  type: array
                  items:
                    type: string
                    format: uuid
                    pattern: >-
                      ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$
                ignoreMaximumAbsence:
                  type: boolean
                admin_approval_option:
                  type: string
                  enum:
                    - approve_request_immediately
                    - go_through_the_normal_approval_process
              required:
                - leave_type_id
                - requester_member_id
                - repeat
                - interval
                - from
                - until
                - count
                - weekDays
                - day
                - month
                - pos
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  can_create:
                    type: boolean
                    description: >-
                      The one-line verdict: true means POST with this payload
                      passes every rule the create endpoint applies, as of the
                      data at preview time; false means it fails. False whenever
                      `rejection` is set, a blocking conflict is listed or
                      `is_allowance_sufficient` is false. Two things the preview
                      cannot see: requests created in between, and changes to
                      the member's approvers that the create call makes when
                      approver sync from Microsoft 365 is on. A snapshot, not a
                      reservation. `department_limit_conflicts` block unless the
                      caller is an admin passing `ignoreMaximumAbsence`;
                      `representative_unavailable` never blocks.
                  occurrence_count:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  duration:
                    anyOf:
                      - type: object
                        properties:
                          workday_absence_duration:
                            type: number
                            description: >-
                              Same field and unit rule as
                              `workday_absence_duration` on GET /requests: days
                              for day and half-day leave units, minutes for hour
                              and minute units. Summed over all occurrences.
                          leave_unit:
                            type: string
                            enum:
                              - days
                              - half_days
                              - hours
                              - minutes_30
                              - minutes_15
                              - minutes_10
                              - minutes_5
                              - minutes_1
                          outside_of_schedule:
                            type: boolean
                            description: >-
                              True when at least one occurrence falls entirely
                              on non-working time of the member's schedule.
                          per_year:
                            type: array
                            items:
                              type: object
                              properties:
                                fiscal_year:
                                  type: integer
                                  minimum: -9007199254740991
                                  maximum: 9007199254740991
                                workday_duration_in_days:
                                  type: number
                                workday_duration_in_minutes:
                                  type: number
                                carry_over_days_used_in_period:
                                  type: number
                                carry_over_minutes_used_in_period:
                                  type: number
                              required:
                                - fiscal_year
                                - workday_duration_in_days
                                - workday_duration_in_minutes
                                - carry_over_days_used_in_period
                                - carry_over_minutes_used_in_period
                              additionalProperties: false
                              description: >-
                                Portion of the absence falling into this fiscal
                                year. An absence crossing the fiscal year
                                boundary costs different amounts in different
                                years; the entries add up to the total.
                        required:
                          - workday_absence_duration
                          - leave_unit
                          - outside_of_schedule
                          - per_year
                        additionalProperties: false
                      - type: 'null'
                  takes_from_allowance:
                    type: boolean
                  allowance_type:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                          name:
                            type: string
                          allowance_unit:
                            type: string
                            enum:
                              - days
                              - hours
                        required:
                          - id
                          - name
                          - allowance_unit
                        additionalProperties: false
                      - type: 'null'
                    description: >-
                      The allowance the absence deducts from. Null when it
                      deducts nothing.
                  is_allowance_sufficient:
                    anyOf:
                      - type: boolean
                      - type: 'null'
                    description: >-
                      Verdict for the whole series: true means the allowance
                      covers it, false means creating it fails with the
                      allowance error and leaves nothing behind. Always true for
                      leave types that do not deduct. Null when `rejection` is
                      set: the allowance was not evaluated. The conflict arrays
                      are reported separately and do not affect this flag.
                  conflicts:
                    type: array
                    items:
                      type: object
                      properties:
                        date:
                          type: string
                        conflicts_with:
                          type: array
                          items:
                            type: object
                            properties:
                              request_id:
                                type: string
                              start:
                                type: string
                              end:
                                type: string
                              status:
                                type: string
                              leave_type_name:
                                anyOf:
                                  - type: string
                                  - type: 'null'
                            required:
                              - request_id
                              - start
                              - end
                              - status
                              - leave_type_name
                            additionalProperties: false
                      required:
                        - date
                        - conflicts_with
                      additionalProperties: false
                    description: >-
                      Occurrences that overlap an existing non-declined,
                      non-cancelled request of the member.
                  department_limit_conflicts:
                    type: array
                    items:
                      type: object
                      properties:
                        date:
                          type: string
                        department_id:
                          type: string
                        department_name:
                          type: string
                        current_absent:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        max_allowed:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                      required:
                        - date
                        - department_id
                        - department_name
                        - current_absent
                        - max_allowed
                      additionalProperties: false
                  representative_unavailable:
                    type: array
                    items:
                      type: object
                      properties:
                        date:
                          type: string
                        representatives:
                          type: array
                          items:
                            type: object
                            properties:
                              member_id:
                                type: string
                              member_name:
                                anyOf:
                                  - type: string
                                  - type: 'null'
                              reason:
                                type: string
                            required:
                              - member_id
                              - member_name
                              - reason
                            additionalProperties: false
                      required:
                        - date
                        - representatives
                      additionalProperties: false
                    description: >-
                      Only populated when representative_member_ids are passed:
                      occurrences on which a chosen representative is absent.
                  representative_overlaps:
                    type: array
                    items:
                      type: object
                      properties:
                        date:
                          type: string
                        representing_for:
                          type: array
                          items:
                            type: object
                            properties:
                              member_id:
                                type: string
                              member_name:
                                anyOf:
                                  - type: string
                                  - type: 'null'
                              request_id:
                                type: string
                              status:
                                type: string
                              start:
                                type: string
                              end:
                                type: string
                            required:
                              - member_id
                              - member_name
                              - request_id
                              - status
                              - start
                              - end
                            additionalProperties: false
                      required:
                        - date
                        - representing_for
                      additionalProperties: false
                    description: >-
                      Occurrences on which the member is someone else's
                      representative, pending or accepted. Only accepted ones
                      reject the series for non-admins
                      (REPRESENTATIVE_CONFLICT).
                  rejection:
                    anyOf:
                      - type: object
                        properties:
                          code:
                            type: string
                            enum:
                              - RECURRENCE_WINDOW_EXCEEDED
                              - RECURRENCE_END_REQUIRED
                              - RECURRENCE_RULE_INVALID
                              - RECURRENCE_TOO_FEW_OCCURRENCES
                              - HOUR_LEAVE_TYPE_REQUIRES_TIME_RANGE
                              - HALF_DAY_NOT_ALLOWED_FOR_FULL_DAY_LEAVE_TYPE
                              - OUTSIDE_EMPLOYMENT_PERIOD
                              - LEAVE_TYPE_DISABLED
                              - REASON_REQUIRED
                              - REASON_TOO_LONG
                              - OUTSIDE_FISCAL_YEAR_WINDOW
                              - PAST_DATE_NOT_ALLOWED
                              - REPRESENTATIVE_CONFLICT
                              - NO_APPROVER_SET
                              - REPRESENTATIVES_INVALID
                              - REQUIRED_REPRESENTATIVES_COUNT_NOT_MET
                              - INVALID_REQUEST
                          message:
                            type: string
                        required:
                          - code
                          - message
                        additionalProperties: false
                      - type: 'null'
                    description: >-
                      Set when creating the same series would be rejected with
                      400: the first failing rule, series rules first and then
                      the per-occurrence rules, in the order the create endpoint
                      applies them. Overlaps, the allowance and department
                      limits are never a rejection; see the conflict arrays and
                      `is_allowance_sufficient`. `duration` and
                      `is_allowance_sufficient` are null when this is set. Match
                      on `code`, not on `message` (which is translated into the
                      API key member's language).
                required:
                  - can_create
                  - occurrence_count
                  - duration
                  - takes_from_allowance
                  - allowance_type
                  - is_allowance_sufficient
                  - conflicts
                  - department_limit_conflicts
                  - representative_unavailable
                  - representative_overlaps
                  - rejection
                additionalProperties: false
        '400':
          description: Invalid input data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.BAD_REQUEST'
        '401':
          description: Authorization not provided
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.UNAUTHORIZED'
        '403':
          description: Insufficient access
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.FORBIDDEN'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.INTERNAL_SERVER_ERROR'
      security:
        - ApiKey: []
components:
  schemas:
    error.BAD_REQUEST:
      type: object
      properties:
        message:
          type: string
          description: The error message
          example: Invalid input data
        code:
          type: string
          description: The error code
          example: BAD_REQUEST
        issues:
          description: An array of issues that were responsible for the error
          example: []
          type: array
          items:
            type: object
            properties:
              message:
                type: string
            required:
              - message
            additionalProperties: false
      required:
        - message
        - code
      additionalProperties: false
      title: Invalid input data error (400)
      description: The error information
      example:
        code: BAD_REQUEST
        message: Invalid input data
        issues: []
    error.UNAUTHORIZED:
      type: object
      properties:
        message:
          type: string
          description: The error message
          example: Authorization not provided
        code:
          type: string
          description: The error code
          example: UNAUTHORIZED
        issues:
          description: An array of issues that were responsible for the error
          example: []
          type: array
          items:
            type: object
            properties:
              message:
                type: string
            required:
              - message
            additionalProperties: false
      required:
        - message
        - code
      additionalProperties: false
      title: Authorization not provided error (401)
      description: The error information
      example:
        code: UNAUTHORIZED
        message: Authorization not provided
        issues: []
    error.FORBIDDEN:
      type: object
      properties:
        message:
          type: string
          description: The error message
          example: Insufficient access
        code:
          type: string
          description: The error code
          example: FORBIDDEN
        issues:
          description: An array of issues that were responsible for the error
          example: []
          type: array
          items:
            type: object
            properties:
              message:
                type: string
            required:
              - message
            additionalProperties: false
      required:
        - message
        - code
      additionalProperties: false
      title: Insufficient access error (403)
      description: The error information
      example:
        code: FORBIDDEN
        message: Insufficient access
        issues: []
    error.INTERNAL_SERVER_ERROR:
      type: object
      properties:
        message:
          type: string
          description: The error message
          example: Internal server error
        code:
          type: string
          description: The error code
          example: INTERNAL_SERVER_ERROR
        issues:
          description: An array of issues that were responsible for the error
          example: []
          type: array
          items:
            type: object
            properties:
              message:
                type: string
            required:
              - message
            additionalProperties: false
      required:
        - message
        - code
      additionalProperties: false
      title: Internal server error error (500)
      description: The error information
      example:
        code: INTERNAL_SERVER_ERROR
        message: Internal server error
        issues: []
  securitySchemes:
    ApiKey:
      type: apiKey
      name: X-API-KEY
      in: header

````