> ## 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 a request (dry run)

> Answer what POST /requests would do with this payload without creating anything: the workday duration, its split by fiscal year, whether the member's allowance covers it, the existing requests it would collide with, and a single `can_create` verdict.

**Input:** identical to POST /requests, so one payload can be sent to both URLs. `reason`, `representative_member_ids` and `ignoreRepresentativeRequirement` are evaluated exactly as on create.

**Duration:** `duration.workday_absence_duration` uses the same days-vs-minutes rule as `workday_absence_duration` on GET /requests (days for day and half-day leave units, minutes for hour units); preview and created request always agree. For day-based leave types the time of day in `start` / `end` is ignored, exactly as on create.

**Allowance:** `is_allowance_sufficient: false` means POST /requests with this payload fails with the allowance error; `true` means it passes that check. Leave types that do not deduct return `takes_from_allowance: false`, `allowance_type: null` and `is_allowance_sufficient: true`.

**Conflicts:** `conflicts` lists the member's existing requests that overlap this absence and would block creation. Every leave type counts, including informational ones such as home office; only declined or cancelled requests are ignored.

**Rejection:** the preview runs the same validations as POST /requests, in the same order, with one difference: an overlapping request is not a rejection but goes to `conflicts`, so where POST /requests would stop with the overlap error the preview keeps going and may name a later rule in `rejection` while the overlap sits in `conflicts`. When a rule fails, the response is still 200 with `rejection` set instead of a 400, and `can_create` is false. Match on `rejection.code`: START_AFTER_END, HALF_DAY_NOT_ALLOWED_FOR_FULL_DAY_LEAVE_TYPE, LEAVE_TYPE_DISABLED (disabled for this member), REASON_REQUIRED, REASON_TOO_LONG, OUTSIDE_FISCAL_YEAR_WINDOW, PAST_DATE_NOT_ALLOWED (manager or self-service retroactive limits), OUTSIDE_EMPLOYMENT_PERIOD, REPRESENTATIVE_CONFLICT (the member is someone else's accepted representative in that period), MAX_ABSENT_REACHED (department limit), NO_APPROVER_SET (the leave type needs approval and the member has no approver), REPRESENTATIVES_INVALID, REQUIRED_REPRESENTATIVES_COUNT_NOT_MET. Permission and scoping failures stay real 403 / 404 responses.

**Verdict:** `can_create` is true only when `rejection` is null, `conflicts` is empty and the allowance covers the absence. It is the one field to gate a create call on.

**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/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/preview:
    post:
      tags:
        - Requests
      summary: Preview a request (dry run)
      description: >-
        Answer what POST /requests would do with this payload without creating
        anything: the workday duration, its split by fiscal year, whether the
        member's allowance covers it, the existing requests it would collide
        with, and a single `can_create` verdict.


        **Input:** identical to POST /requests, so one payload can be sent to
        both URLs. `reason`, `representative_member_ids` and
        `ignoreRepresentativeRequirement` are evaluated exactly as on create.


        **Duration:** `duration.workday_absence_duration` uses the same
        days-vs-minutes rule as `workday_absence_duration` on GET /requests
        (days for day and half-day leave units, minutes for hour units); preview
        and created request always agree. For day-based leave types the time of
        day in `start` / `end` is ignored, exactly as on create.


        **Allowance:** `is_allowance_sufficient: false` means POST /requests
        with this payload fails with the allowance error; `true` means it passes
        that check. Leave types that do not deduct return `takes_from_allowance:
        false`, `allowance_type: null` and `is_allowance_sufficient: true`.


        **Conflicts:** `conflicts` lists the member's existing requests that
        overlap this absence and would block creation. Every leave type counts,
        including informational ones such as home office; only declined or
        cancelled requests are ignored.


        **Rejection:** the preview runs the same validations as POST /requests,
        in the same order, with one difference: an overlapping request is not a
        rejection but goes to `conflicts`, so where POST /requests would stop
        with the overlap error the preview keeps going and may name a later rule
        in `rejection` while the overlap sits in `conflicts`. When a rule fails,
        the response is still 200 with `rejection` set instead of a 400, and
        `can_create` is false. Match on `rejection.code`: START_AFTER_END,
        HALF_DAY_NOT_ALLOWED_FOR_FULL_DAY_LEAVE_TYPE, LEAVE_TYPE_DISABLED
        (disabled for this member), REASON_REQUIRED, REASON_TOO_LONG,
        OUTSIDE_FISCAL_YEAR_WINDOW, PAST_DATE_NOT_ALLOWED (manager or
        self-service retroactive limits), OUTSIDE_EMPLOYMENT_PERIOD,
        REPRESENTATIVE_CONFLICT (the member is someone else's accepted
        representative in that period), MAX_ABSENT_REACHED (department limit),
        NO_APPROVER_SET (the leave type needs approval and the member has no
        approver), REPRESENTATIVES_INVALID,
        REQUIRED_REPRESENTATIVES_COUNT_NOT_MET. Permission and scoping failures
        stay real 403 / 404 responses.


        **Verdict:** `can_create` is true only when `rejection` is null,
        `conflicts` is empty and the allowance covers the absence. It is the one
        field to gate a create call on.


        **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-previewRequest
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                start:
                  description: >-
                    Start/end of the request in ISO 8601. For day-based leave
                    types (`leave_unit: days`) send the date at midnight UTC
                    (e.g. `2026-06-01T00:00:00Z`) and use `start_at` / `end_at`
                    to set half-days. For hour-based leave types include the
                    exact time of day (e.g. `2026-06-01T09:00:00Z`); `start_at`
                    / `end_at` are then ignored.
                  type: string
                end:
                  description: >-
                    Start/end of the request in ISO 8601. For day-based leave
                    types (`leave_unit: days`) send the date at midnight UTC
                    (e.g. `2026-06-01T00:00:00Z`) and use `start_at` / `end_at`
                    to set half-days. For hour-based leave types include the
                    exact time of day (e.g. `2026-06-01T09:00:00Z`); `start_at`
                    / `end_at` are then ignored.
                  type: string
                start_at:
                  type: string
                  enum:
                    - morning
                    - afternoon
                  description: >-
                    For day-based leave types: whether the absence starts in the
                    `morning` (the whole first day counts) or `afternoon` (only
                    the second half of the first day). Ignored for hour-based
                    leave types.
                end_at:
                  type: string
                  enum:
                    - lunchtime
                    - end_of_day
                  description: >-
                    For day-based leave types: whether the absence ends at
                    `lunchtime` (only the first half of the last day) or
                    `end_of_day` (the whole last day counts). Ignored for
                    hour-based leave types.
                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})$
                reason:
                  type: string
                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})$
                representative_member_ids:
                  description: >-
                    Member IDs to assign as representatives. Required for leave
                    types that mandate representatives; omitting them returns
                    REQUIRED_REPRESENTATIVES_COUNT_NOT_MET (as a 400 on create,
                    as `rejection.code` on preview).
                  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})$
                ignoreRepresentativeRequirement:
                  description: >-
                    Skip the representative requirement. Only honored when the
                    API key's member is an admin and the request is created for
                    another member.
                  type: boolean
              required:
                - start
                - end
                - leave_type_id
                - requester_member_id
      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.
                  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.
                          duration:
                            type: number
                            description: >-
                              The span as it would be stored on the request:
                              calendar days for day units, minutes for hour
                              units. Use `workday_absence_duration` for what is
                              deducted.
                          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 the whole absence falls 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
                          - 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: >-
                      True means the allowance covers this payload, false means
                      creating it fails with the allowance error. Always true
                      for leave types that do not deduct. Null when `rejection`
                      is set: the allowance was not evaluated. `conflicts` is
                      reported separately and does not affect this flag.
                  conflicts:
                    anyOf:
                      - type: array
                        items:
                          type: object
                          properties:
                            request_id:
                              type: string
                            start:
                              type: string
                            end:
                              type: string
                            start_at:
                              type: string
                              enum:
                                - morning
                                - afternoon
                            end_at:
                              type: string
                              enum:
                                - lunchtime
                                - end_of_day
                            status:
                              type: string
                            leave_type_name:
                              anyOf:
                                - type: string
                                - type: 'null'
                          required:
                            - request_id
                            - start
                            - end
                            - start_at
                            - end_at
                            - status
                            - leave_type_name
                          additionalProperties: false
                      - type: 'null'
                    description: >-
                      Existing non-declined, non-cancelled requests of the
                      member that overlap the absence, regardless of their leave
                      type. Any entry blocks creation. Null when the payload was
                      rejected before the overlap check ran (START_AFTER_END
                      through OUTSIDE_EMPLOYMENT_PERIOD); populated for every
                      later rejection so both problems are visible at once.
                  rejection:
                    anyOf:
                      - type: object
                        properties:
                          code:
                            type: string
                            enum:
                              - START_AFTER_END
                              - HALF_DAY_NOT_ALLOWED_FOR_FULL_DAY_LEAVE_TYPE
                              - LEAVE_TYPE_DISABLED
                              - REASON_REQUIRED
                              - REASON_TOO_LONG
                              - OUTSIDE_FISCAL_YEAR_WINDOW
                              - PAST_DATE_NOT_ALLOWED
                              - OUTSIDE_EMPLOYMENT_PERIOD
                              - REPRESENTATIVE_CONFLICT
                              - MAX_ABSENT_REACHED
                              - 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 payload would be rejected with
                      400: the first failing rule, in the order the create
                      endpoint applies them. Overlapping requests and the
                      allowance are never a rejection; see `conflicts` 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
                  - duration
                  - takes_from_allowance
                  - allowance_type
                  - is_allowance_sufficient
                  - conflicts
                  - 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

````