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

# Deactivate a customer (reversible)

> Takes one customer OUT OF CIRCULATION without changing a single field of their data. They stop appearing in the till picker, customer search, clienteling and segmentation, while their name, contact details, loyalty balance and entire purchase history stay intact and still resolve by id — so every past sale keeps rendering the buyer it was made to. Fully reversible with `reactivateCustomer`. THIS IS NOT AN ERASURE: it removes nobody's personal data and must never be used to answer a GDPR erasure request — use `eraseCustomerData` for that. Idempotent. 409 when the customer has already been anonymized (that standing is terminal, so there is nothing left to take out of circulation), 404 for an unknown id.



## OpenAPI

````yaml /openapi.json post /v1/customers/{id}/deactivation
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/customers/{id}/deactivation:
    post:
      tags:
        - Customers
      summary: Deactivate a customer (reversible)
      description: >-
        Takes one customer OUT OF CIRCULATION without changing a single field of
        their data. They stop appearing in the till picker, customer search,
        clienteling and segmentation, while their name, contact details, loyalty
        balance and entire purchase history stay intact and still resolve by id
        — so every past sale keeps rendering the buyer it was made to. Fully
        reversible with `reactivateCustomer`. THIS IS NOT AN ERASURE: it removes
        nobody's personal data and must never be used to answer a GDPR erasure
        request — use `eraseCustomerData` for that. Idempotent. 409 when the
        customer has already been anonymized (that standing is terminal, so
        there is nothing left to take out of circulation), 404 for an unknown
        id.
      operationId: deactivateCustomer
      parameters:
        - schema:
            type: string
            minLength: 1
          in: path
          name: id
          required: true
          description: The customer id to act on, e.g. `cust-1` (client `Customer.id`).
      responses:
        '200':
          description: >-
            The new standing: `deactivated`, stamped with the instant it took
            effect.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - active
                      - deactivated
                      - anonymized
                  deactivatedAt:
                    type: string
                    format: date-time
                    pattern: >-
                      ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                  anonymizedAt:
                    type: string
                    format: date-time
                    pattern: >-
                      ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                required:
                  - status
                additionalProperties: false
                description: >-
                  The new standing: `deactivated`, stamped with the instant it
                  took effect.
                example:
                  status: deactivated
                  deactivatedAt: '2026-08-04T09:12: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'
        '409':
          description: >-
            The request conflicts with current state (duplicate id or stale
            version).
          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.

````