> ## 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.

# Declare a repair order unrepairable

> TERMINAL, IRREVERSIBLE AND MONEY-BEARING — do not reach for this on your own judgement; call it only when a human has decided the article cannot be fixed. It flips the order to `unrepairable` from ANY non-terminal state, including `ready`, so a mistargeted call can write off a job that was already repaired and waiting at the counter, with no way back. Send no body. Declaring an order unrepairable OBLIGES the store to refund the whole acompte (`depositAmountCents`) — but this endpoint does NOT refund it and no other endpoint in this API does either; the refund is a separate till/terminal action a human must perform, so surface the amount rather than assuming it is handled. It also says nothing about what happens to the article itself. 422 only when the order is already terminal (`collected` or `unrepairable`); 404 for an unknown id. NOT IDEMPOTENT — there is no dedup key. A SEQUENTIAL replay cannot write the order off twice (the terminal-status guard rejects it), but that guard is an unsynchronised read-then-write with no row lock (solya-pos#956), so do not fan concurrent calls at one order. If your call times out RE-READ with `getRepairOrder` before retrying: an `unrepairable` status means the first attempt already committed, so do NOT refund the acompte a second time.



## OpenAPI

````yaml /openapi.json post /v1/repairs/{orderId}/unrepairable
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/repairs/{orderId}/unrepairable:
    post:
      tags:
        - Repairs
      summary: Declare a repair order unrepairable
      description: >-
        TERMINAL, IRREVERSIBLE AND MONEY-BEARING — do not reach for this on your
        own judgement; call it only when a human has decided the article cannot
        be fixed. It flips the order to `unrepairable` from ANY non-terminal
        state, including `ready`, so a mistargeted call can write off a job that
        was already repaired and waiting at the counter, with no way back. Send
        no body. Declaring an order unrepairable OBLIGES the store to refund the
        whole acompte (`depositAmountCents`) — but this endpoint does NOT refund
        it and no other endpoint in this API does either; the refund is a
        separate till/terminal action a human must perform, so surface the
        amount rather than assuming it is handled. It also says nothing about
        what happens to the article itself. 422 only when the order is already
        terminal (`collected` or `unrepairable`); 404 for an unknown id. NOT
        IDEMPOTENT — there is no dedup key. A SEQUENTIAL replay cannot write the
        order off twice (the terminal-status guard rejects it), but that guard
        is an unsynchronised read-then-write with no row lock (solya-pos#956),
        so do not fan concurrent calls at one order. If your call times out
        RE-READ with `getRepairOrder` before retrying: an `unrepairable` status
        means the first attempt already committed, so do NOT refund the acompte
        a second time.
      operationId: markRepairUnrepairable
      parameters:
        - schema:
            type: string
            minLength: 1
          in: path
          name: orderId
          required: true
      responses:
        '200':
          description: >-
            The written-off order: `unrepairable`, `updatedAt` re-stamped. The
            full 3600-cent acompte is now owed back to the customer.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  article:
                    type: object
                    properties:
                      label:
                        type: string
                      skuId:
                        type: string
                      serial:
                        type: object
                        properties:
                          kind:
                            type: string
                            enum:
                              - serial
                              - imei
                          value:
                            type: string
                        required:
                          - kind
                          - value
                        additionalProperties: false
                      purchasedAt:
                        type: string
                    required:
                      - label
                    additionalProperties: false
                  issueDescription:
                    type: string
                  quoteAmountCents:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  depositAmountCents:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  expectedPickupDate:
                    type: string
                  customerId:
                    type: string
                  storeId:
                    type: string
                  status:
                    type: string
                    enum:
                      - intake
                      - in_workshop
                      - ready
                      - collected
                      - unrepairable
                  actorId:
                    type: string
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                required:
                  - id
                  - article
                  - issueDescription
                  - quoteAmountCents
                  - depositAmountCents
                  - expectedPickupDate
                  - customerId
                  - storeId
                  - status
                  - actorId
                  - createdAt
                  - updatedAt
                additionalProperties: false
                description: >-
                  The written-off order: `unrepairable`, `updatedAt` re-stamped.
                  The full 3600-cent acompte is now owed back to the customer.
                example:
                  id: rep-2030-000471
                  article:
                    label: Smartphone screen
                    skuId: >-
                      d4c81e5b0f7a396248ce15b7d0a83f62915ce4d7b0863af251d94c7e08b3f6a1
                    purchasedAt: '2029-01-16T09:00:00.000Z'
                  issueDescription: Cracked screen after a drop
                  quoteAmountCents: 12000
                  depositAmountCents: 3600
                  expectedPickupDate: '2030-07-20T18:00:00.000Z'
                  customerId: cust-1
                  storeId: shop-1
                  status: unrepairable
                  actorId: user-associate-1
                  createdAt: '2030-07-10T14:05:00.000Z'
                  updatedAt: '2030-07-15T09:20:00.000Z'
        '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'
        '404':
          description: No resource matches the addressed identifier.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: The request is well-formed but violates a domain rule.
          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.

````