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

# Simulate multiple salary scenarios (not persisted)

> Run payroll calculations for multiple salary scenarios without persisting results.

Intended for quote/preview UIs, HR tools, and compensation planning:
- "What will my total employer cost be if I hire at X salary?"
- "Show me the ISR impact of a 20% raise vs. a 40% raise."

**Important**: simulation results are NOT payroll records. They are never
stored and do not appear in any reports or audit logs. The response includes
`is_simulation: true` as an explicit flag.

Supports 1–20 scenarios per request. Each scenario can vary salary and
enable/disable optional perceptions independently.



## OpenAPI

````yaml /openapi.json post /v1/payroll/simulate
openapi: 3.1.0
info:
  title: Payroll API
  description: >

    ## Payroll Infrastructure API for Latin America


    **Stripe for payroll** — embedded payroll calculation infrastructure.


    ### Overview


    This API performs payroll calculations for Mexico, Colombia, Brazil,
    Argentina,

    and Chile. It is a **pure computation engine**: give it employee data, get
    back

    a fully itemized payroll breakdown.


    ### Key concepts


    - **Scheme** — A country-specific payroll ruleset (e.g., Mexico `ordinario`,
    Colombia `integral`).
      Use `GET /v1/payroll/schemes/{country}` to discover available schemes.
    - **Calculation** — A single employee payroll run for one period.

    - **Audit trail** — A step-by-step trace of every formula evaluated.
    Required for
      legal compliance in most Latin American countries.

    ### Decimal precision


    All monetary amounts are returned as **JSON strings** (not numbers) to
    prevent

    floating-point precision loss in client languages. Parse them with your
    language's

    `Decimal` type before doing any arithmetic.


    ```

    ✅ correct:  {"net_salary": "20039.25"}

    ❌ wrong:    {"net_salary": 20039.25}  // could lose precision

    ```


    ### Authentication


    All endpoints require a Bearer API key:

    ```

    Authorization: Bearer your_api_key_here

    ```


    ### Error format


    All errors follow a consistent structure:

    ```json

    {
      "error": {
        "code": "SCHEME_NOT_FOUND",
        "message": "Human-readable description",
        "details": {},
        "request_id": "abc123"
      }
    }

    ```


    Use the `code` field (not the HTTP status) for programmatic error handling.

    The `request_id` links to server logs — include it when contacting support.


    ### Payouts (mock provider)


    The `/v1/payouts` surface lets you pay an employee their `net_salary` after
    a

    calculation. In v1 every payout is handled by an **in-process mock
    provider** —

    no real funds are moved. Every response is marked with `mock: true` in the
    body

    and `X-Clevis-Mock: true` as a response header.


    - Magic `external_id` prefixes (`MOCK_REJECT_INTAKE_`, `MOCK_REJECT_BANK_`,
      `MOCK_SLOW_`, `MOCK_STUCK_`, `MOCK_OK_`) trigger deterministic outcomes for
      demos and tests.
    - `processing.step_seconds: 0` makes the payout transition to its terminal
      status synchronously before the response is returned.
    - Idempotency is supported via the `Idempotency-Key` header or via the
      `external_id` field (per API key).

    See `docs/specs/04_payout_mock_endpoint.md` for the full contract. The real

    dLocal integration will be a config flip behind the same interface; this

    client-facing contract does not change.
  contact:
    name: Payroll API Support
    email: api@payroll.io
  license:
    name: Proprietary
  version: 1.0.0
servers: []
security: []
tags:
  - name: Payroll
    description: Payroll calculation endpoints. All monetary values are JSON strings.
  - name: Payouts
    description: >-
      Single-beneficiary payout endpoints backed by an in-process **mock**
      provider in v1 — no real funds are moved. Every response carries `mock:
      true` and the `X-Clevis-Mock: true` header. Magic trigger values (e.g.
      `external_id` prefixed with `MOCK_REJECT_BANK_`) force deterministic
      outcomes for demos and tests.
  - name: Schemes & Health
    description: Discover available payroll schemes and check service health.
  - name: Health
    description: Service health and readiness checks.
paths:
  /v1/payroll/simulate:
    post:
      tags:
        - Payroll
      summary: Simulate multiple salary scenarios (not persisted)
      description: >-
        Run payroll calculations for multiple salary scenarios without
        persisting results.


        Intended for quote/preview UIs, HR tools, and compensation planning:

        - "What will my total employer cost be if I hire at X salary?"

        - "Show me the ISR impact of a 20% raise vs. a 40% raise."


        **Important**: simulation results are NOT payroll records. They are
        never

        stored and do not appear in any reports or audit logs. The response
        includes

        `is_simulation: true` as an explicit flag.


        Supports 1–20 scenarios per request. Each scenario can vary salary and

        enable/disable optional perceptions independently.
      operationId: Payroll_simulate
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SimulateRequest'
        required: true
      responses:
        '200':
          description: Simulation results for all scenarios.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimulateResponse'
        '401':
          description: Missing or invalid API key.
        '404':
          description: Country/scheme/year combination not found.
        '422':
          description: Invalid request data.
      security:
        - ApiKeyBearer: []
components:
  schemas:
    SimulateRequest:
      properties:
        country:
          type: string
          pattern: ^[A-Z]{2}$
          title: Country
          examples:
            - MX
        scheme:
          type: string
          title: Scheme
          examples:
            - ordinario
        year:
          type: integer
          maximum: 2030
          minimum: 2020
          title: Year
          examples:
            - 2024
        period:
          $ref: '#/components/schemas/Period'
        employee:
          $ref: '#/components/schemas/EmployeeInput'
        employer:
          $ref: '#/components/schemas/EmployerInput'
        simulation:
          $ref: '#/components/schemas/SimulationConfig'
        options:
          $ref: '#/components/schemas/CalculationOptions'
      type: object
      required:
        - country
        - scheme
        - year
        - period
        - employee
        - employer
        - simulation
      title: SimulateRequest
      description: |-
        Request body for POST /v1/payroll/simulate.

        Identical computation to /calculate but:
          1. Results are NEVER persisted
          2. Supports multiple scenarios in a single request
          3. Intended for quote/preview UIs — "what would my cost be at X salary?"

        The simulation flag is enforced at the route handler level, not here.
    SimulateResponse:
      properties:
        id:
          type: string
          title: Id
          description: Simulation ID (ULID). Not persisted — for correlation only.
        status:
          type: string
          const: success
          title: Status
          default: success
        country:
          type: string
          title: Country
        scheme:
          type: string
          title: Scheme
        year:
          type: integer
          title: Year
        is_simulation:
          type: boolean
          const: true
          title: Is Simulation
          default: true
        results:
          items:
            $ref: '#/components/schemas/SimulationScenarioResult'
          type: array
          title: Results
        dsl_version:
          type: string
          title: Dsl Version
        computed_at:
          type: string
          format: date-time
          title: Computed At
      type: object
      required:
        - id
        - country
        - scheme
        - year
        - results
        - dsl_version
        - computed_at
      title: SimulateResponse
      description: Multi-scenario simulation response. Results are never persisted.
    Period:
      properties:
        type:
          type: string
          enum:
            - monthly
            - biweekly
            - weekly
          title: Type
          description: Period frequency. Determines default days if not provided.
          examples:
            - monthly
        start_date:
          type: string
          format: date
          title: Start Date
          description: First calendar day of the payroll period (inclusive).
          examples:
            - '2024-03-01'
        end_date:
          type: string
          format: date
          title: End Date
          description: Last calendar day of the payroll period (inclusive).
          examples:
            - '2024-03-31'
        days:
          type: integer
          maximum: 31
          minimum: 1
          title: Days
          description: >-
            Number of days in the payroll period used for salary calculations.
            For a full calendar month this is typically 30 regardless of the
            actual month length (IMSS convention in Mexico).
          examples:
            - 30
      type: object
      required:
        - type
        - start_date
        - end_date
        - days
      title: Period
      description: Payroll period definition.
    EmployeeInput:
      properties:
        id:
          type: string
          title: Id
          description: Your internal employee identifier. Echoed back in the response.
          examples:
            - emp_001
        daily_salary:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
            - type: 'null'
          title: Daily Salary
          description: >-
            Employee's daily salary (Salario Diario). Required for Mexico (MX),
            where it is the legal base unit for IMSS/SDI calculations. Optional
            for other countries — those schemes use monthly_salary instead. Must
            be positive when provided.
          examples:
            - 800
        monthly_salary:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
            - type: 'null'
          title: Monthly Salary
          description: >-
            Monthly salary in full. Required for non-MX countries (Colombia,
            Argentina, Brazil, Chile, Peru). For Mexico, this field is optional
            — daily_salary is the legal base unit there.
        factor_integracion:
          anyOf:
            - type: number
              maximum: 3
              minimum: 1
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Factor Integracion
          description: >-
            Integration factor (factor de integración) for IMSS SDI calculation.
            Legal minimum: 1.0493 (15 days aguinaldo + 25% prima vacacional).
            Higher values for employers with better-than-minimum benefits.
          default: '1.0493'
          examples:
            - 1.0493
        risk_class_rate:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Risk Class Rate
          description: >-
            IMSS work risk rate (Prima de Riesgo de Trabajo). Determined by IMSS
            based on employer's declared activity. Class I default: 0.00543
            (0.543%). Range: 0.005% – 6.96%.
          default: '0.00543'
          examples:
            - 0.00543
        overtime_amount:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Overtime Amount
          description: Total overtime pay amount for this period, if any.
          default: '0'
        sunday_bonus_days:
          type: integer
          maximum: 5
          minimum: 0
          title: Sunday Bonus Days
          description: Number of Sundays worked in the period (for prima dominical).
          default: 0
        grocery_voucher_amount:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Grocery Voucher Amount
          description: Grocery voucher (vales de despensa) amount for the period.
          default: '0'
        infonavit_credit_amount:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Infonavit Credit Amount
          description: >-
            Active Infonavit credit deduction amount. Only applies if the
            employee has an active Infonavit mortgage.
          default: '0'
        num_dependentes_irrf:
          type: integer
          minimum: 0
          title: Num Dependentes Irrf
          description: >-
            BR only — number of dependents declared for IRRF deduction (RIR/2018
            Art. 71). Each dependent reduces the IRRF base by R$189.59/month.
          default: 0
        vale_transporte_amount:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Vale Transporte Amount
          description: >-
            BR only — total transportation voucher (vale-transporte) provided to
            the employee for the period. Discount capped at 6% of base salary
            (Decreto 95.247/1987).
          default: '0'
        pensao_alimenticia_amount:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Pensao Alimenticia Amount
          description: >-
            BR only — court-ordered alimony withheld from payroll (RIR/2018 Art.
            72). Fully deductible from the IRRF taxable base.
          default: '0'
        decimo_terceiro_amount:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Decimo Terceiro Amount
          description: >-
            BR only — 13th salary amount (décimo terceiro, Lei 4.090/1962) for a
            separate 13th-salary payroll run. Set monthly_salary=0 and provide
            this value to calculate the 13th folha in isolation.
          default: '0'
        ferias_amount:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Ferias Amount
          description: >-
            BR only — vacation pay base (férias, CLT Art. 129) for a separate
            vacation payroll run. The engine automatically adds the mandatory
            1/3 constitutional bonus (CF Art. 7 XVII). Set monthly_salary=0 and
            provide this value.
          default: '0'
        rat_fap_rate:
          anyOf:
            - type: number
              maximum: 1
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Rat Fap Rate
          description: >-
            BR only — RAT × FAP composite employer rate for work-accident
            insurance (Lei 8.212/1991 Art. 22 II). Rate = GIIL-RAT (1%, 2%, or
            3% by CNAE risk) multiplied by FAP (0.5–2.0, company-specific).
            Default 0.02 = medium risk, FAP 1.0. Provide the pre-multiplied
            composite rate.
          default: '0.02'
        cantidad_hijos:
          type: integer
          maximum: 20
          minimum: 0
          title: Cantidad Hijos
          description: >-
            AR only — number of dependent children under 18 declared as cargas
            de familia for Ganancias 4ta categoría (LIG Art. 30 inc. b apartado
            2). Each child reduces the Ganancias taxable base by the published
            monthly deduction (e.g., $203,905.29/month in H1 2026).
          default: 0
        cantidad_hijos_incapacitados:
          type: integer
          maximum: 20
          minimum: 0
          title: Cantidad Hijos Incapacitados
          description: >-
            AR only — number of dependent children with permanent disability
            (LIG Art. 30 inc. b apartado 2). Each one reduces the Ganancias base
            by the higher 'hijo incapacitado' monthly deduction (~2x the regular
            hijo deduction). Counted in addition to cantidad_hijos.
          default: 0
        cuota_sindical_rate:
          anyOf:
            - type: number
              maximum: 0.05
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Cuota Sindical Rate
          description: >-
            AR only — union dues (cuota sindical) rate as a fraction of monthly
            gross salary. Determined by the applicable Convenio Colectivo de
            Trabajo (typically 1–3%). Set
            input.overrides.aplica_cuota_sindical=true to apply. Range 0–5% (5%
            is a hard upper bound; legitimate rates are usually under 3%).
          default: '0'
        overrides:
          additionalProperties:
            type: boolean
          type: object
          title: Overrides
          description: >-
            Boolean flags that enable/disable optional perceptions. Keys match
            `enabled_if` expressions in the DSL. Example: {"bono_puntualidad":
            true}
          examples:
            - bono_puntualidad: true
      type: object
      required:
        - id
      title: EmployeeInput
      description: Employee-side inputs for a payroll calculation.
    EmployerInput:
      properties:
        id:
          type: string
          title: Id
          description: Your internal employer/tenant identifier.
          examples:
            - employer_001
        rfc:
          anyOf:
            - type: string
            - type: 'null'
          title: Rfc
          description: Registro Federal de Contribuyentes (Mexico tax ID). Optional.
          examples:
            - ABC123456XY0
      type: object
      required:
        - id
      title: EmployerInput
      description: Employer-side inputs for a payroll calculation.
    SimulationConfig:
      properties:
        scenarios:
          items:
            $ref: '#/components/schemas/SimulationScenario'
          type: array
          maxItems: 20
          minItems: 1
          title: Scenarios
          description: Salary scenarios to compare. 1–20 scenarios per simulation.
      type: object
      required:
        - scenarios
      title: SimulationConfig
      description: 'Simulation configuration: one or more salary scenarios.'
    CalculationOptions:
      properties:
        include_audit_trail:
          type: boolean
          title: Include Audit Trail
          description: >-
            Return the step-by-step audit trail. Set False for batch runs to
            reduce response payload size.
          default: true
        include_diagram:
          type: boolean
          title: Include Diagram
          description: Return the concept dependency diagram metadata.
          default: false
        precision:
          type: integer
          maximum: 6
          minimum: 0
          title: Precision
          description: >-
            Override the DSL's default rounding precision for this request.
            Useful for internal calculations requiring higher precision.
          default: 2
      type: object
      title: CalculationOptions
      description: >-
        Options that affect what the engine returns (not the calculation
        itself).
    SimulationScenarioResult:
      properties:
        label:
          type: string
          title: Label
          description: Scenario label from the request.
        daily_salary:
          type: string
          title: Daily Salary
          description: Daily salary used for this scenario (string).
        summary:
          $ref: '#/components/schemas/PayrollSummary'
        perceptions:
          items:
            $ref: '#/components/schemas/PerceptionResponse'
          type: array
          title: Perceptions
        deductions:
          items:
            $ref: '#/components/schemas/DeductionResponse'
          type: array
          title: Deductions
        employer_contributions:
          items:
            $ref: '#/components/schemas/EmployerContributionResponse'
          type: array
          title: Employer Contributions
      type: object
      required:
        - label
        - daily_salary
        - summary
        - perceptions
        - deductions
        - employer_contributions
      title: SimulationScenarioResult
      description: Result for one scenario in a simulation run.
    SimulationScenario:
      properties:
        label:
          type: string
          title: Label
          description: Human-readable label for this scenario.
          examples:
            - Salario actual
        daily_salary:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Daily Salary
          description: Daily salary for this scenario.
          examples:
            - 800
        overrides:
          additionalProperties:
            type: boolean
          type: object
          title: Overrides
          description: Per-scenario override flags (merged with request-level overrides).
      type: object
      required:
        - label
        - daily_salary
      title: SimulationScenario
      description: One salary scenario for a simulation run.
    PayrollSummary:
      properties:
        gross_salary:
          type: string
          title: Gross Salary
          description: Total perceptions (percepciones totales). String in JSON.
        total_perceptions:
          type: string
          title: Total Perceptions
        total_deductions:
          type: string
          title: Total Deductions
        net_salary:
          type: string
          title: Net Salary
          description: Employee take-home pay after all deductions. String in JSON.
        employer_contributions_total:
          type: string
          title: Employer Contributions Total
          description: Sum of all employer-side contributions (IMSS, Infonavit, etc.).
        employer_total_cost:
          type: string
          title: Employer Total Cost
          description: 'Total cost to employer: gross salary + employer contributions.'
      type: object
      required:
        - gross_salary
        - total_perceptions
        - total_deductions
        - net_salary
        - employer_contributions_total
        - employer_total_cost
      title: PayrollSummary
      description: High-level financial summary of the payroll calculation.
    PerceptionResponse:
      properties:
        id:
          type: string
          title: Id
          description: DSL concept ID.
        label:
          type: string
          title: Label
          description: Human-readable label (Spanish).
        amount:
          type: string
          title: Amount
          description: Amount as decimal string in response JSON.
        taxable:
          type: boolean
          title: Taxable
          description: Whether this perception is included in the ISR base.
        imss_base:
          type: boolean
          title: Imss Base
          description: Whether this perception is included in the IMSS cotization base.
        tags:
          items:
            type: string
          type: array
          title: Tags
      type: object
      required:
        - id
        - label
        - amount
        - taxable
        - imss_base
      title: PerceptionResponse
      description: One perception (earning) line in the payroll result.
    DeductionResponse:
      properties:
        id:
          type: string
          title: Id
        label:
          type: string
          title: Label
        amount:
          type: string
          title: Amount
        tags:
          items:
            type: string
          type: array
          title: Tags
        bracket_applied:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Bracket Applied
          description: Bracket tier detail if this deduction used a progressive table.
      type: object
      required:
        - id
        - label
        - amount
      title: DeductionResponse
      description: One deduction line in the payroll result.
    EmployerContributionResponse:
      properties:
        table_id:
          type: string
          title: Table Id
        label:
          type: string
          title: Label
        total:
          type: string
          title: Total
          description: Sum of all component amounts (string).
        components:
          items:
            $ref: '#/components/schemas/ContributionComponentResponse'
          type: array
          title: Components
      type: object
      required:
        - table_id
        - label
        - total
        - components
      title: EmployerContributionResponse
      description: One employer contribution table result (e.g., IMSS patrón, Infonavit).
    ContributionComponentResponse:
      properties:
        id:
          type: string
          title: Id
        label:
          type: string
          title: Label
        base:
          type: string
          title: Base
          description: Base amount (string) the rate was applied to.
        rate:
          type: string
          title: Rate
          description: Rate applied (string).
        amount:
          type: string
          title: Amount
          description: Resulting contribution amount (string).
      type: object
      required:
        - id
        - label
        - base
        - rate
        - amount
      title: ContributionComponentResponse
      description: One rate component within an employer contribution table.
  securitySchemes:
    ApiKeyBearer:
      type: http
      description: >-
        API key authentication. Send your API key as: Authorization: Bearer
        <your_api_key>
      scheme: bearer

````