openapi: 3.1.0
info:
  title: In-Person Payments API
  version: 1.0.0
  description: >-
    API for accepting in-store payments through Kustom. Your backend creates
    payment sessions dispatched to registered devices, and the Kustom POS app
    handles collection. In-Person Payments supports **card payments**
    (tap-to-pay) as well as **alternative payment methods** — Klarna, Swish, and
    Vipps — collected via Scan to pay. Kustom delivers lifecycle callbacks back
    to your system.


    A device is **your own smartphone** (iPhone or Android) running the Kustom
    POS app, installed from the App Store or Google Play. Each device is paired
    to your account using an enrollment code.
servers:
  - url: https://api.kustom.co
    description: Production
  - url: https://api.playground.kustom.co
    description: Playground
tags:
  - name: Testing
    description: >-
      Testing utilities for simulating devices and card taps. Available on
      playground only — not in production.
  - name: Enrollments
    description: Device enrollment code management for pairing new devices.
  - name: Locations
    description: Manage physical locations associated with a merchant.
  - name: Devices
    description: Device registration and management.
  - name: Sessions
    description: Session lifecycle management.
paths:
  /ipp/v1/sessions/{session_id}/cancel:
    put:
      tags:
        - Sessions
      summary: Cancel session
      description: >-
        Cancels a session. Triggers a session.cancelled callback. This is a
        terminal state — no further payment collection is possible.
      operationId: cancelSession
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Session cancelled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionDto'
              examples:
                cancelSessionResponse:
                  summary: Session cancelled successfully
                  description: cancelSessionResponse
                  value:
                    session_id: 660e8400-e29b-41d4-a716-446655440001
                    status: CANCELLED
                    device_id: 550e8400-e29b-41d4-a716-446655440000
                    order_amount: 240000
                    purchase_currency: SEK
                    purchase_started_at: '2024-01-15T10:30:00Z'
                    order_tax_amount: 48000
                    merchant_reference1: ORDER-12345
                    order_items:
                      - type: physical
                        reference: SKU-001
                        name: T-Shirt
                        quantity: 2
                        unit_price: 100000
                        total_amount: 200000
                        tax_rate: 2500
                        total_tax_amount: 40000
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: >-
                      /ipp/v1/sessions/660e8400-e29b-41d4-a716-446655440001/cancel
                    errors:
                      message: Access Denied
        '404':
          description: Resource not found.
          content:
            application/json:
              examples:
                notFoundError:
                  summary: Session not found
                  description: notFoundError
                  value:
                    type: error:resource_not_found
                    title: Resource Not Found
                    status: 404
                    detail: >-
                      Session with id 660e8400-e29b-41d4-a716-446655440001 not
                      found
                    instance: >-
                      /ipp/v1/sessions/660e8400-e29b-41d4-a716-446655440001/cancel
        '409':
          description: Session is already in a terminal state.
          content:
            application/json:
              examples:
                conflictError:
                  summary: Session state conflict
                  description: conflictError
                  value:
                    type: error:session_state_conflict
                    title: Session State Conflict
                    status: 409
                    detail: >-
                      Session is already in terminal state FINALIZED and cannot
                      be cancelled
                    instance: >-
                      /ipp/v1/sessions/660e8400-e29b-41d4-a716-446655440001/cancel
  /ipp/v1/devices/{id}:
    put:
      tags:
        - Devices
      summary: Update device
      description: >-
        Updates device properties such as name, metadata, or location
        assignment.
      operationId: updateDevice
      parameters:
        - name: id
          in: path
          description: Device ID
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDevicePublicRequest'
            examples:
              updateDeviceRequest:
                summary: Update device properties
                description: updateDeviceRequest
                value:
                  name: POS Terminal - Main Entrance
                  metadata:
                    store_id: '456'
                    department: Sales
                  location_id: 660e8400-e29b-41d4-a716-446655440001
        required: true
      responses:
        '200':
          description: Device updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DevicePublicResponse'
              examples:
                updateDeviceResponse:
                  summary: Device updated successfully
                  description: updateDeviceResponse
                  value:
                    id: 550e8400-e29b-41d4-a716-446655440000
                    hardware_id: ABC123XYZ
                    merchant_id: merchant_12345
                    location_id: 660e8400-e29b-41d4-a716-446655440001
                    name: POS Terminal - Main Entrance
                    platform: IOS
                    metadata:
                      store_id: '456'
                      department: Sales
                    created_at: '2024-01-15T10:30:00Z'
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/devices/550e8400-e29b-41d4-a716-446655440000
                    errors:
                      message: Access Denied
        '404':
          description: Device not found.
          content:
            application/json:
              examples:
                notFoundError:
                  summary: Device not found
                  description: notFoundError
                  value:
                    type: error:resource_not_found
                    title: Resource Not Found
                    status: 404
                    detail: >-
                      Device with id 550e8400-e29b-41d4-a716-446655440000 not
                      found
                    instance: /ipp/v1/devices/550e8400-e29b-41d4-a716-446655440000
    delete:
      tags:
        - Devices
      summary: Deregister device
      description: >-
        Removes a device from the merchant's account. The device will no longer
        be able to process payments.
      operationId: deregisterDevice
      parameters:
        - name: id
          in: path
          description: Device ID
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Device deregistered successfully.
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/devices/550e8400-e29b-41d4-a716-446655440000
                    errors:
                      message: Access Denied
        '404':
          description: Device not found.
          content:
            application/json:
              examples:
                notFoundError:
                  summary: Device not found
                  description: notFoundError
                  value:
                    type: error:resource_not_found
                    title: Resource Not Found
                    status: 404
                    detail: >-
                      Device with id 550e8400-e29b-41d4-a716-446655440000 not
                      found
                    instance: /ipp/v1/devices/550e8400-e29b-41d4-a716-446655440000
  /ipp/v1/sessions:
    post:
      tags:
        - Sessions
      summary: Create session
      description: >-
        Creates a session to be collected by an In-Person Payment device.
        Dispatches it to the target device. Lifecycle callbacks are delivered to
        the URL configured in the merchant portal.
      operationId: createSession
      parameters:
        - name: location_id
          in: query
          description: Location ID - Mandatory if merchant has multiple locations
          required: false
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSessionDto'
            examples:
              createSessionRequest:
                summary: Create a payment session
                description: createSessionRequest
                value:
                  device_id: 550e8400-e29b-41d4-a716-446655440000
                  order_amount: 240000
                  purchase_currency: SEK
                  purchase_started_at: '2024-01-15T10:30:00Z'
                  order_tax_amount: 48000
                  merchant_reference1: ORDER-12345
                  order_items:
                    - type: physical
                      reference: SKU-001
                      name: T-Shirt
                      quantity: 2
                      unit_price: 100000
                      total_amount: 200000
                      tax_rate: 2500
                      total_tax_amount: 40000
                    - type: physical
                      reference: SKU-002
                      name: Socks
                      quantity: 1
                      unit_price: 40000
                      total_amount: 40000
                      tax_rate: 2500
                      total_tax_amount: 8000
        required: true
      responses:
        '200':
          description: Session created and dispatched to device.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionDto'
              examples:
                createSessionResponse:
                  summary: Session created successfully
                  description: createSessionResponse
                  value:
                    session_id: 660e8400-e29b-41d4-a716-446655440001
                    status: CREATED
                    device_id: 550e8400-e29b-41d4-a716-446655440000
                    order_amount: 240000
                    purchase_currency: SEK
                    purchase_started_at: '2024-01-15T10:30:00Z'
                    order_tax_amount: 48000
                    merchant_reference1: ORDER-12345
                    order_items:
                      - type: physical
                        reference: SKU-001
                        name: T-Shirt
                        quantity: 2
                        unit_price: 100000
                        total_amount: 200000
                        tax_rate: 2500
                        total_tax_amount: 40000
                      - type: physical
                        reference: SKU-002
                        name: Socks
                        quantity: 1
                        unit_price: 40000
                        total_amount: 40000
                        tax_rate: 2500
                        total_tax_amount: 8000
        '400':
          description: Invalid request body or parameters.
          content:
            application/json:
              examples:
                validationError:
                  summary: Validation failed
                  description: validationError
                  value:
                    type: error:validation_failed
                    title: Validation Failed
                    status: 400
                    detail: One or more fields are invalid
                    instance: /ipp/v1/sessions
                    errors:
                      order_amount: must be greater than or equal to 0
                      purchase_currency: must not be blank
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/sessions
                    errors:
                      message: Access Denied
        '404':
          description: Target device not found or inactive.
          content:
            application/json:
              examples:
                notFoundError:
                  summary: Device not found
                  description: notFoundError
                  value:
                    type: error:resource_not_found
                    title: Resource Not Found
                    status: 404
                    detail: >-
                      Device with id 550e8400-e29b-41d4-a716-446655440000 not
                      found
                    instance: /ipp/v1/sessions
  /ipp/v1/enrollments:
    get:
      tags:
        - Enrollments
      summary: List enrollments
      description: >-
        Returns a paginated list of enrollments for the merchant. Soft-deleted
        (CANCELLED) enrollments are always excluded. Results can be filtered by
        location, by metadata key-value pairs, and by whether consumed
        enrollments are included.
      operationId: listEnrollments
      parameters:
        - name: x-merchant-id
          in: header
          required: true
          schema:
            type: string
            minLength: 1
        - name: location_id
          in: query
          description: Filter by location ID.
          required: false
          schema:
            type: string
            format: uuid
        - name: metadata
          in: query
          description: >-
            Filter by metadata key-value pair. Format: `key:value`. Repeat the
            parameter to apply multiple filters — all must match (AND
            semantics). Maximum 5 entries. Duplicate keys are not allowed.
          required: false
          schema:
            type: array
            items:
              type: string
          example: store_id:123
        - name: include_consumed
          in: query
          description: Whether to include CONSUMED enrollments. Defaults to true.
          required: false
          schema:
            type: boolean
            default: true
        - name: page_number
          in: query
          description: Zero-based page number. Defaults to 0.
          required: false
          schema:
            type: integer
            format: int32
            default: 0
            minimum: 0
        - name: page_size
          in: query
          description: Page size. Defaults to 20.
          required: false
          schema:
            type: integer
            format: int32
            default: 20
            maximum: 100
            minimum: 1
      responses:
        '200':
          description: Paginated list of enrollments.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicPageResponse'
              examples:
                listEnrollmentsResponse:
                  summary: List of enrollment codes
                  description: listEnrollmentsResponse
                  value:
                    content:
                      - enrollment_id: 660e8400-e29b-41d4-a716-446655440001
                        enrollment_code: ABC123XYZ
                        expires_at: '2024-01-15T11:30:00Z'
                        status: PENDING
                        metadata:
                          store_id: '123'
                          terminal_type: checkout
                      - enrollment_id: 660e8400-e29b-41d4-a716-446655440002
                        enrollment_code: DEF456UVW
                        expires_at: '2024-01-15T12:00:00Z'
                        status: CONSUMED
                        metadata: {}
                    total_elements: 2
                    total_pages: 1
                    page_number: 0
                    page_size: 20
                    first: true
                    last: true
        '400':
          description: Invalid request parameters.
          content:
            application/json:
              examples:
                validationError:
                  summary: Validation failed
                  description: validationError
                  value:
                    type: error:validation_failed
                    title: Validation Failed
                    status: 400
                    detail: One or more fields are invalid
                    instance: /ipp/v1/enrollments
                    errors:
                      page_number: must be greater than or equal to 0
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/enrollments
                    errors:
                      message: Access Denied
    post:
      tags:
        - Enrollments
      summary: Create enrollment
      description: Creates a new enrollment code that a device can use to register.
      operationId: createEnrollment
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEnrollmentPublicRequest'
            examples:
              createEnrollmentRequest:
                summary: Create an enrollment code
                description: createEnrollmentRequest
                value:
                  location_id: 550e8400-e29b-41d4-a716-446655440000
                  expires_in: 360000
                  metadata:
                    store_id: '123'
                    terminal_type: checkout
        required: true
      responses:
        '201':
          description: Enrollment created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EnrollmentPublicResponse'
              examples:
                createEnrollmentResponse:
                  summary: Enrollment created successfully
                  description: createEnrollmentResponse
                  value:
                    enrollment_id: 660e8400-e29b-41d4-a716-446655440001
                    enrollment_code: ABC123XYZ
                    expires_at: '2024-01-15T11:30:00Z'
                    status: PENDING
                    metadata:
                      store_id: '123'
                      terminal_type: checkout
        '400':
          description: Invalid request body.
          content:
            application/json:
              examples:
                validationError:
                  summary: Validation failed
                  description: validationError
                  value:
                    type: error:validation_failed
                    title: Validation Failed
                    status: 400
                    detail: One or more fields are invalid
                    instance: /ipp/v1/enrollments
                    errors:
                      location_id: must not be null
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/enrollments
                    errors:
                      message: Access Denied
  /ipp/v1/dev/sessions/{sessionId}/simulate-tap:
    post:
      tags:
        - Testing
      summary: Simulate card tap
      description: >-
        Simulates a physical card tap on the terminal reader, bypassing the need
        for real hardware. Uses Stripe test helpers to simulate a card-present
        payment with the requested outcome. Only available in non-production
        environments.
      operationId: simulateCardTap
      parameters:
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: outcome
          in: query
          required: false
          schema:
            type: string
            default: SUCCESS
            enum:
              - SUCCESS
              - DECLINED_GENERIC
              - INSUFFICIENT_FUNDS
              - LOST_CARD
              - STOLEN_CARD
              - EXPIRED_CARD
              - INCORRECT_CVC
              - PROCESSING_ERROR
              - INCORRECT_NUMBER
              - CARD_VELOCITY_EXCEEDED
      responses:
        '204':
          description: Card tap simulated successfully.
        '400':
          description: Session has no terminal payment attached.
        '404':
          description: Session not found.
  /ipp/v1/dev/devices:
    post:
      tags:
        - Testing
      summary: Create simulated device
      description: >-
        Creates a simulated device by generating an enrollment and immediately
        registering it. Always adds `simulated=true` to the device metadata.
        Only available in non-production environments.
      operationId: createSimulatedDevice
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFakeDeviceRequest'
        required: true
      responses:
        '201':
          description: Simulated device created.
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/CreateFakeDeviceResponse'
        '400':
          description: Invalid request body.
        '401':
          description: Missing or invalid credentials.
  /ipp/v1/sessions/{session_id}:
    get:
      tags:
        - Sessions
      summary: Get session
      description: Retrieves session details by ID.
      operationId: getSession
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Session details.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionDto'
              examples:
                getSessionResponse:
                  summary: Session retrieved successfully
                  description: getSessionResponse
                  value:
                    session_id: 660e8400-e29b-41d4-a716-446655440001
                    status: ACTIVE
                    device_id: 550e8400-e29b-41d4-a716-446655440000
                    order_id: 770e8400-e29b-41d4-a716-446655440002
                    order_amount: 240000
                    purchase_currency: SEK
                    purchase_started_at: '2024-01-15T10:30:00Z'
                    order_tax_amount: 48000
                    merchant_reference1: ORDER-12345
                    order_items:
                      - type: physical
                        reference: SKU-001
                        name: T-Shirt
                        quantity: 2
                        unit_price: 100000
                        total_amount: 200000
                        tax_rate: 2500
                        total_tax_amount: 40000
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/sessions/660e8400-e29b-41d4-a716-446655440001
                    errors:
                      message: Access Denied
        '404':
          description: Resource not found.
          content:
            application/json:
              examples:
                notFoundError:
                  summary: Session not found
                  description: notFoundError
                  value:
                    type: error:resource_not_found
                    title: Resource Not Found
                    status: 404
                    detail: >-
                      Session with id 660e8400-e29b-41d4-a716-446655440001 not
                      found
                    instance: /ipp/v1/sessions/660e8400-e29b-41d4-a716-446655440001
  /ipp/v1/locations:
    get:
      tags:
        - Locations
      summary: List locations
      description: >-
        Returns a paginated list of locations belonging to the authenticated
        merchant.
      operationId: listLocations
      parameters:
        - name: x-merchant-id
          in: header
          description: Merchant ID resolved from the API key.
          required: true
          schema:
            type: string
            minLength: 1
        - name: page_number
          in: query
          description: Zero-based page number. Defaults to 0.
          required: false
          schema:
            type: integer
            format: int32
            default: 0
            minimum: 0
        - name: page_size
          in: query
          description: Page size. Defaults to 20.
          required: false
          schema:
            type: integer
            format: int32
            default: 20
            exclusiveMinimum: 0
      responses:
        '200':
          description: Locations retrieved successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicPageResponse'
              examples:
                listLocationsResponse:
                  summary: List of merchant locations
                  description: listLocationsResponse
                  value:
                    content:
                      - id: 550e8400-e29b-41d4-a716-446655440000
                        merchant_id: merchant_12345
                        name: Downtown Store
                        street_address: 123 Main Street
                        street_address2: Suite 100
                        postal_code: '12345'
                        city: Stockholm
                        country: SE
                        created_at: '2024-01-15T10:30:00Z'
                        updated_at: '2024-01-20T14:45:00Z'
                      - id: 550e8400-e29b-41d4-a716-446655440001
                        merchant_id: merchant_12345
                        name: Mall Location
                        street_address: 456 Shopping Center
                        postal_code: '67890'
                        city: Gothenburg
                        country: SE
                        created_at: '2024-02-01T09:00:00Z'
                        updated_at: '2024-02-01T09:00:00Z'
                    total_elements: 2
                    total_pages: 1
                    page_number: 0
                    page_size: 20
                    first: true
                    last: true
        '400':
          description: Missing or invalid merchant ID header.
          content:
            application/json:
              examples:
                badRequestError:
                  summary: Missing header
                  description: badRequestError
                  value:
                    type: error:missing_param
                    title: Missing Request Header
                    status: 400
                    detail: Required request header 'X-Merchant-Id' is missing
                    instance: /ipp/v1/locations
                    errors:
                      header: X-Merchant-Id
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/locations
                    errors:
                      message: Access Denied
  /ipp/v1/locations/{id}:
    get:
      tags:
        - Locations
      summary: Get location
      description: Returns a single location by ID, scoped to the authenticated merchant.
      operationId: getLocation
      parameters:
        - name: x-merchant-id
          in: header
          description: Merchant ID resolved from the API key.
          required: true
          schema:
            type: string
            minLength: 1
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Location found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocationPublicResponse'
              examples:
                getLocationResponse:
                  summary: Location retrieved successfully
                  description: getLocationResponse
                  value:
                    id: 550e8400-e29b-41d4-a716-446655440000
                    merchant_id: merchant_12345
                    name: Downtown Store
                    street_address: 123 Main Street
                    street_address2: Suite 100
                    postal_code: '12345'
                    city: Stockholm
                    country: SE
                    created_at: '2024-01-15T10:30:00Z'
                    updated_at: '2024-01-20T14:45:00Z'
        '400':
          description: Missing or invalid merchant ID header.
          content:
            application/json:
              examples:
                badRequestError:
                  summary: Missing header
                  description: badRequestError
                  value:
                    type: error:missing_param
                    title: Missing Request Header
                    status: 400
                    detail: Required request header 'X-Merchant-Id' is missing
                    instance: /ipp/v1/locations/550e8400-e29b-41d4-a716-446655440000
                    errors:
                      header: X-Merchant-Id
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/locations/550e8400-e29b-41d4-a716-446655440000
                    errors:
                      message: Access Denied
        '404':
          description: Location not found.
          content:
            application/json:
              examples:
                notFoundError:
                  summary: Location not found
                  description: notFoundError
                  value:
                    type: error:resource_not_found
                    title: Resource Not Found
                    status: 404
                    detail: >-
                      Location with id 550e8400-e29b-41d4-a716-446655440000 not
                      found
                    instance: /ipp/v1/locations/550e8400-e29b-41d4-a716-446655440000
  /ipp/v1/devices:
    get:
      tags:
        - Devices
      summary: List devices
      description: >-
        Lists all registered devices for the merchant, optionally filtered by
        location.
      operationId: listDevices
      parameters:
        - name: x-merchant-id
          in: header
          required: true
          schema:
            type: string
            minLength: 1
        - name: location_id
          in: query
          description: Filter by location ID.
          required: false
          schema:
            type: string
            format: uuid
        - name: metadata
          in: query
          description: >-
            Filter by metadata key-value pair. Format: `key:value`. Repeat the
            parameter to apply multiple filters — all must match (AND
            semantics). Maximum 5 entries. Duplicate keys are not allowed.
          required: false
          schema:
            type: array
            items:
              type: string
          example: store_id:123
        - name: page_number
          in: query
          description: Zero-based page number. Defaults to 0.
          required: false
          schema:
            type: integer
            format: int32
            default: 0
            minimum: 0
        - name: page_size
          in: query
          description: Page size. Defaults to 20.
          required: false
          schema:
            type: integer
            format: int32
            default: 20
            exclusiveMinimum: 0
      responses:
        '200':
          description: List of devices.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicPageResponse'
              examples:
                listDevicesResponse:
                  summary: List of registered devices
                  description: listDevicesResponse
                  value:
                    content:
                      - id: 550e8400-e29b-41d4-a716-446655440000
                        hardware_id: ABC123XYZ
                        merchant_id: merchant_12345
                        location_id: 660e8400-e29b-41d4-a716-446655440001
                        name: POS Terminal 1
                        platform: IOS
                        metadata:
                          store_id: '123'
                          region: EU
                        created_at: '2024-01-15T10:30:00Z'
                        open_cash_register_id: 42
                      - id: 550e8400-e29b-41d4-a716-446655440002
                        hardware_id: DEF456UVW
                        merchant_id: merchant_12345
                        location_id: 660e8400-e29b-41d4-a716-446655440001
                        name: POS Terminal 2
                        platform: ANDROID
                        metadata: {}
                        created_at: '2024-01-16T09:00:00Z'
                    total_elements: 2
                    total_pages: 1
                    page_number: 0
                    page_size: 20
                    first: true
                    last: true
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/devices
                    errors:
                      message: Access Denied
  /ipp/v1/enrollments/{enrollment_id}:
    delete:
      tags:
        - Enrollments
      summary: Delete enrollment
      description: >-
        Soft-deletes an enrollment. The enrollment will no longer appear in list
        results.
      operationId: deleteEnrollments
      parameters:
        - name: enrollment_id
          in: path
          description: Enrollment ID.
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Enrollment deleted.
        '401':
          description: Missing or invalid credentials.
          content:
            application/json:
              examples:
                unauthorizedError:
                  summary: Authentication failed
                  description: unauthorizedError
                  value:
                    type: error:unauthorized
                    title: Unauthorized
                    status: 401
                    detail: Access Denied
                    instance: /ipp/v1/enrollments/660e8400-e29b-41d4-a716-446655440001
                    errors:
                      message: Access Denied
        '404':
          description: Enrollment not found.
          content:
            application/json:
              examples:
                notFoundError:
                  summary: Enrollment not found
                  description: notFoundError
                  value:
                    type: error:resource_not_found
                    title: Resource Not Found
                    status: 404
                    detail: >-
                      Enrollment with id 660e8400-e29b-41d4-a716-446655440001
                      not found
                    instance: /ipp/v1/enrollments/660e8400-e29b-41d4-a716-446655440001
components:
  schemas:
    LineItemDto:
      type: object
      description: Line item in a session order.
      properties:
        type:
          type: string
          description: Type of the line item.
          enum:
            - physical
            - discount
          example: physical
        reference:
          type: string
          description: External reference ID for the item (e.g., SKU).
          example: SKU-12345
        name:
          type: string
          description: Name or description of the item.
          example: T-Shirt
          minLength: 1
        quantity:
          type: integer
          format: int32
          description: Quantity of the item.
          example: 2
          minimum: 1
        unit_price:
          type: integer
          format: int64
          description: Unit price in minor units (e.g., cents).
          example: 1000
        total_amount:
          type: integer
          format: int64
          description: Total amount for this line in minor units.
          example: 2000
        tax_rate:
          type: integer
          format: int32
          description: Tax rate in basis points (e.g., 2500 for 25%).
          example: 2500
        total_discount_amount:
          type: integer
          format: int64
          description: Total discount amount in minor units.
          example: 200
        total_tax_amount:
          type: integer
          format: int64
          description: Total tax amount for this line in minor units.
          example: 400
      required:
        - name
        - tax_rate
        - total_amount
        - total_tax_amount
        - unit_price
    SessionDto:
      type: object
      description: Session response containing order and payment details.
      properties:
        session_id:
          type: string
          format: uuid
          description: Unique session identifier.
          example: 550e8400-e29b-41d4-a716-446655440000
        status:
          $ref: '#/components/schemas/SessionStatusDto'
          description: Current session status.
        device_id:
          type: string
          format: uuid
          description: Device ID handling the session.
          example: 660e8400-e29b-41d4-a716-446655440001
        order_id:
          type: string
          description: Order ID once created.
          example: 770e8400-e29b-41d4-a716-446655440002
        order_amount:
          type: integer
          format: int64
          description: Total order amount in minor units.
          example: 2000
        purchase_currency:
          type: string
          description: Currency code (ISO 4217).
          example: SEK
        purchase_started_at:
          type: string
          format: date-time
          description: Timestamp when the purchase started.
          example: '2024-01-15T10:30:00Z'
        purchase_completed_at:
          type: string
          format: date-time
          description: Timestamp when the purchase was completed.
          example: '2024-01-15T10:35:00Z'
        order_items:
          type: array
          description: List of line items in the order.
          items:
            $ref: '#/components/schemas/LineItemDto'
        merchant_reference1:
          type: string
          description: First merchant reference.
          example: ORDER-12345
        merchant_reference2:
          type: string
          description: Second merchant reference.
          example: REF-67890
        order_tax_amount:
          type: integer
          format: int32
          description: Total tax amount in minor units.
          example: 400
        merchant_data:
          type: string
          description: Arbitrary merchant data.
          example:
            customer_id: C123
        created_at:
          type: string
          format: date-time
          description: Timestamp when the session was created.
          example: '2024-01-15T10:30:00Z'
        expires_at:
          type: string
          format: date-time
          description: Timestamp when the session expires.
          example: '2024-01-15T11:30:00Z'
        updated_at:
          type: string
          format: date-time
          description: Timestamp when the session was last updated.
          example: '2024-01-15T10:35:00Z'
    SessionStatusDto:
      type: string
      description: Possible session statuses.
      enum:
        - CREATED
        - ACTIVE
        - FINALIZED
        - CANCELLED
        - EXPIRED
        - FAILED
    UpdateDevicePublicRequest:
      type: object
      description: Request payload to update device properties.
      properties:
        name:
          type: string
          description: Display name of the device.
          example: POS Terminal 1
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Custom metadata key-value pairs for the device.
          example:
            store_id: '123'
            region: EU
        location_id:
          type: string
          format: uuid
          description: Location identifier to assign the device to.
          example: 550e8400-e29b-41d4-a716-446655440000
    DeviceCredentialsPublicResponse:
      type: object
      description: Device authentication credentials.
      properties:
        bearer_token:
          type: string
          description: Bearer token for device authentication.
          example: eyJhbGciOiJSUzI1NiIsInR5cCI...
        issuedAt:
          type: string
          format: date-time
          description: Timestamp when the token was issued.
          example: '2024-01-15T10:30:00Z'
    DevicePublicResponse:
      type: object
      description: Registered device information.
      properties:
        id:
          type: string
          description: Unique device identifier.
          example: 550e8400-e29b-41d4-a716-446655440000
        unique_id:
          type: string
          deprecated: true
          description: Hardware identifier (deprecated, use hardwareId).
          example: ABC123XYZ
        hardware_id:
          type: string
          description: Hardware identifier of the device.
          example: ABC123XYZ
        merchant_id:
          type: string
          description: Merchant identifier.
          example: merchant_12345
        location_id:
          type: string
          description: Location identifier where the device is registered.
          example: 550e8400-e29b-41d4-a716-446655440000
        name:
          type: string
          description: Display name of the device.
          example: POS Terminal 1
        platform:
          type: string
          description: Device platform.
          enum:
            - IOS
            - ANDROID
            - TERMINAL
          example: IOS
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Custom metadata key-value pairs for the device.
          example:
            store_id: '123'
            region: EU
        created_at:
          type: string
          format: date-time
          description: Timestamp when the device was created.
          example: '2024-01-15T10:30:00Z'
        open_cash_register_id:
          type: integer
          format: int32
          description: ID of the currently open cash register on this device.
          example: 42
        credentials:
          $ref: '#/components/schemas/DeviceCredentialsPublicResponse'
          description: Device credentials (only returned during registration).
    CreateSessionDto:
      type: object
      description: Request payload to create a new payment session.
      properties:
        device_id:
          type: string
          format: uuid
          description: Target device ID.
          example: 550e8400-e29b-41d4-a716-446655440000
        order_amount:
          type: integer
          format: int64
          description: Total order amount in minor units (e.g., cents).
          example: 2000
        purchase_currency:
          type: string
          description: Currency code (ISO 4217).
          example: SEK
          maxLength: 3
          minLength: 3
        purchase_started_at:
          type: string
          format: date-time
          description: Timestamp when the purchase started.
          example: '2024-01-15T10:30:00Z'
        purchase_completed_at:
          type: string
          format: date-time
          description: Timestamp when the purchase was completed.
          example: '2024-01-15T10:35:00Z'
        order_items:
          type: array
          description: List of line items in the order.
          items:
            $ref: '#/components/schemas/LineItemDto'
          minItems: 1
        merchant_reference1:
          type: string
          description: First merchant reference for tracking purposes.
          example: ORDER-12345
        merchant_reference2:
          type: string
          description: Second merchant reference for tracking purposes.
          example: REF-67890
        order_tax_amount:
          type: integer
          format: int32
          description: Total tax amount for the order in minor units.
          example: 400
        merchant_data:
          type: string
          description: Arbitrary merchant data (JSON string).
          example:
            customer_id: C123
      required:
        - device_id
        - order_amount
        - order_items
        - order_tax_amount
        - purchase_currency
        - purchase_started_at
    CreateEnrollmentPublicRequest:
      type: object
      description: Request payload to create a new device enrollment code.
      properties:
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Custom metadata key-value pairs for the enrollment.
          example:
            store_id: '123'
            terminal_type: checkout
        expires_in:
          type: integer
          format: int64
          description: >-
            Time in milliseconds until the enrollment code expires. Defaults to
            7200000 (2 hours).
          example: 360000
        location_id:
          type: string
          format: uuid
          description: Location ID to associate the enrolled device with.
          example: 550e8400-e29b-41d4-a716-446655440000
    EnrollmentPublicResponse:
      type: object
      description: Enrollment code information for device pairing.
      properties:
        enrollment_id:
          type: string
          format: uuid
          description: Unique enrollment identifier.
          example: 550e8400-e29b-41d4-a716-446655440000
        enrollment_code:
          type: string
          description: The enrollment code to enter on the device.
          example: ABC123XYZ
        expires_at:
          type: string
          format: date-time
          description: Timestamp when the enrollment code expires.
          example: '2024-01-15T11:30:00Z'
        status:
          type: string
          description: Current status of the enrollment.
          enum:
            - PENDING
            - CONSUMED
            - EXPIRED
            - CANCELLED
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Custom metadata key-value pairs for the enrollment.
          example:
            store_id: '123'
            terminal_type: checkout
    CreateFakeDeviceRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
        location_id:
          type: string
          format: uuid
        metadata:
          type: object
          additionalProperties:
            type: string
      required:
        - name
    CreateFakeDeviceResponse:
      type: object
      properties:
        device_id:
          type: string
          format: uuid
    PublicPageResponse:
      type: object
      properties:
        content:
          type: array
          items: {}
        total_elements:
          type: integer
          format: int64
        total_pages:
          type: integer
          format: int32
        page_number:
          type: integer
          format: int32
        page_size:
          type: integer
          format: int32
        first:
          type: boolean
        last:
          type: boolean
    LocationPublicResponse:
      type: object
      description: Physical location information associated with a merchant.
      properties:
        id:
          type: string
          format: uuid
          description: Unique location identifier.
          example: 550e8400-e29b-41d4-a716-446655440000
        merchant_id:
          type: string
          description: Merchant identifier.
          example: merchant_12345
        name:
          type: string
          description: Display name of the location.
          example: Downtown Store
        street_address:
          type: string
          description: Primary street address.
          example: 123
        street_address2:
          type: string
          description: Secondary address line (suite, apartment, etc.).
          example: Suite 100
        postal_code:
          type: string
          description: Postal or ZIP code.
          example: 12345
        city:
          type: string
          description: City name.
          example: Stockholm
        country:
          type: string
          description: Country code (ISO 3166-1 alpha-2).
          example: SE
        created_at:
          type: string
          format: date-time
          description: Timestamp when the location was created.
          example: '2024-01-15T10:30:00Z'
        updated_at:
          type: string
          format: date-time
          description: Timestamp when the location was last updated.
          example: '2024-01-20T14:45:00Z'
