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

# Replace the loyalty program config

> Upserts the ENTIRE loyalty plan — this is a full replace, not a patch, so send every field including the complete tier ladder (at least one rung; omitted rungs are deleted). GET the current config first and edit that document. The change takes effect immediately for every subsequent accrual, redemption and tier resolution, and it does NOT restate existing ledger entries, so past earns keep the rate they were granted at. 400 on a malformed plan (e.g. an empty `tiers` array).



## OpenAPI

````yaml /openapi.json put /v1/loyalty/config
openapi: 3.0.3
info:
  title: Solya POS API
  version: 1.0.0
  description: >-
    The Solya POS backend HTTP surface. Every documented operation is
    agent-ready: it carries an `operationId`, an agent-facing `description`, the
    `pos.*` scopes it enforces (`x-required-permissions`) and an `x-agent-tier`.
    Success responses return the payload as raw JSON; failures return the
    `ErrorResponse` envelope (`{ error: { code, message, statusCode } }`).
servers:
  - url: /
    description: The backend, relative to its deployed origin.
security: []
paths:
  /v1/loyalty/config:
    put:
      tags:
        - Loyalty
      summary: Replace the loyalty program config
      description: >-
        Upserts the ENTIRE loyalty plan — this is a full replace, not a patch,
        so send every field including the complete tier ladder (at least one
        rung; omitted rungs are deleted). GET the current config first and edit
        that document. The change takes effect immediately for every subsequent
        accrual, redemption and tier resolution, and it does NOT restate
        existing ledger entries, so past earns keep the rate they were granted
        at. 400 on a malformed plan (e.g. an empty `tiers` array).
      operationId: updateLoyaltyConfig
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                accrualPointsPerCent:
                  type: number
                  minimum: 0
                redemptionCentsPerPoint:
                  type: number
                  minimum: 0
                tiers:
                  minItems: 1
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: string
                        minLength: 1
                      name:
                        type: string
                        minLength: 1
                      minLifetimePoints:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                      earnMultiplier:
                        type: number
                        minimum: 0
                        exclusiveMinimum: true
                    required:
                      - id
                      - name
                      - minLifetimePoints
                expiry:
                  type: object
                  properties:
                    mode:
                      type: string
                      enum:
                        - sinceEarned
                        - sinceLastActivity
                    months:
                      type: integer
                      minimum: 0
                      exclusiveMinimum: true
                      maximum: 9007199254740991
                  required:
                    - mode
                    - months
              required:
                - accrualPointsPerCent
                - redemptionCentsPerPoint
                - tiers
              example:
                accrualPointsPerCent: 0.01
                redemptionCentsPerPoint: 2
                tiers:
                  - id: bronze
                    name: Bronze
                    minLifetimePoints: 0
                  - id: argent
                    name: Argent
                    minLifetimePoints: 400
                  - id: or
                    name: Or
                    minLifetimePoints: 1000
                  - id: platine
                    name: Platine
                    minLifetimePoints: 3000
      responses:
        '200':
          description: The saved program rules.
          content:
            application/json:
              schema:
                type: object
                properties:
                  accrualPointsPerCent:
                    type: number
                    minimum: 0
                  redemptionCentsPerPoint:
                    type: number
                    minimum: 0
                  tiers:
                    minItems: 1
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          minLength: 1
                        name:
                          type: string
                          minLength: 1
                        minLifetimePoints:
                          type: integer
                          minimum: 0
                          maximum: 9007199254740991
                        earnMultiplier:
                          type: number
                          minimum: 0
                          exclusiveMinimum: true
                      required:
                        - id
                        - name
                        - minLifetimePoints
                      additionalProperties: false
                  expiry:
                    type: object
                    properties:
                      mode:
                        type: string
                        enum:
                          - sinceEarned
                          - sinceLastActivity
                      months:
                        type: integer
                        minimum: 0
                        exclusiveMinimum: true
                        maximum: 9007199254740991
                    required:
                      - mode
                      - months
                    additionalProperties: false
                required:
                  - accrualPointsPerCent
                  - redemptionCentsPerPoint
                  - tiers
                additionalProperties: false
                description: The saved program rules.
                example:
                  accrualPointsPerCent: 0.01
                  redemptionCentsPerPoint: 2
                  tiers:
                    - id: bronze
                      name: Bronze
                      minLifetimePoints: 0
                    - id: argent
                      name: Argent
                      minLifetimePoints: 400
                    - id: or
                      name: Or
                      minLifetimePoints: 1000
                    - id: platine
                      name: Platine
                      minLifetimePoints: 3000
        '400':
          description: >-
            The request failed schema validation; `error.fieldErrors` lists the
            fields.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorResponse'
        '401':
          description: No valid credential was presented — send a bearer token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: The actor is authenticated but lacks the required `pos.*` scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: An unexpected server error — safe to retry idempotent requests.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - bearerAuth: []
components:
  schemas:
    ValidationErrorResponse:
      type: object
      required:
        - error
      additionalProperties: false
      description: >-
        A `VALIDATION_FAILED` envelope carrying the offending fields in
        `fieldErrors`.
      properties:
        error:
          type: object
          required:
            - code
            - message
            - statusCode
          additionalProperties: false
          properties:
            code:
              type: string
              enum:
                - VALIDATION_FAILED
            message:
              type: string
            statusCode:
              type: integer
            fieldErrors:
              type: array
              description: >-
                One entry per rejected field: the field path and why it was
                rejected.
              items:
                type: object
                required:
                  - field
                  - message
                additionalProperties: false
                properties:
                  field:
                    type: string
                    description: Dot-path of the offending field.
                  message:
                    type: string
                    description: Why the field was rejected.
    ErrorResponse:
      type: object
      required:
        - error
      additionalProperties: false
      description: The uniform failure envelope every non-2xx response returns.
      properties:
        error:
          type: object
          required:
            - code
            - message
            - statusCode
          additionalProperties: false
          properties:
            code:
              type: string
              enum:
                - VALIDATION_FAILED
                - UNAUTHORIZED
                - FORBIDDEN
                - NOT_FOUND
                - CONFLICT
                - BUSINESS_RULE_VIOLATION
                - INTERNAL_ERROR
              description: >-
                Machine-readable kernel `ResultCode` — branch on this, not on
                `message`.
            message:
              type: string
              description: >-
                Human-readable explanation. Safe to surface; never leaks server
                internals.
            statusCode:
              type: integer
              description: >-
                The HTTP status, mirrored into the body so a client need not
                read headers.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        `Authorization: Bearer <token>`. Accepts EITHER a Keycloak access token
        (scopes-in-token) OR an opaque POS session token; both resolve to the
        same `pos.*` scope vocabulary the route guards enforce.

````