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

# Create counter (POS terminal)

> Create a new POS counter for the authenticated merchant. Returns Keycloak client credentials (`client_id` / `client_secret`) and a `webhook_secret` for HMAC signing — **these are shown only once, store them securely**. The counter's `client_id` can be used to obtain a machine token via Keycloak client credentials flow.



## OpenAPI

````yaml /swagger.json post /merchant/api/v1/counters
openapi: 3.0.3
info:
  title: Merchant API
  description: >-
    API for merchants to create and manage payment intents, POS counters, and
    transaction history in the FEX Payment Platform. Authentication uses
    Keycloak JWT (human merchants via password flow, POS terminals via client
    credentials).
  version: 1.0.0
  contact:
    name: FEX Payments Platform
servers:
  - url: http://api-sandbox.fexpayments.com
    description: Merchant API sandbox server
security: []
tags:
  - name: Payment Intents
    description: Create and manage payment intents
  - name: Counters
    description: >-
      POS terminal (counter) management — each counter has its own Keycloak
      client credentials and webhook endpoint
  - name: Admin
    description: Back-office endpoints restricted to platform admin role (`admin`)
  - name: Public
    description: Public endpoints accessible without authentication (for QR code payments)
  - name: Onboarding
    description: Merchant registration — requires platform admin JWT (realm role `admin`)
paths:
  /merchant/api/v1/counters:
    post:
      tags:
        - Counters
      summary: Create counter (POS terminal)
      description: >-
        Create a new POS counter for the authenticated merchant. Returns
        Keycloak client credentials (`client_id` / `client_secret`) and a
        `webhook_secret` for HMAC signing — **these are shown only once, store
        them securely**. The counter's `client_id` can be used to obtain a
        machine token via Keycloak client credentials flow.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCounterRequest'
            example:
              name: Counter 1
              location: Floor 2, Register 3
              webhook_url: https://pos.example.com/webhooks/payments
      responses:
        '201':
          description: >-
            Counter created. Save `client_secret` and `webhook_secret`
            immediately — they cannot be retrieved again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateCounterResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'
      security:
        - BearerAuth: []
components:
  schemas:
    CreateCounterRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          example: Counter 1
        location:
          type: string
          example: Floor 2, Register 3
        webhook_url:
          type: string
          format: uri
          example: https://pos.example.com/webhooks/payments
    CreateCounterResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        counter:
          $ref: '#/components/schemas/Counter'
        client_id:
          type: string
          description: Keycloak client ID for POS client_credentials flow
          example: counter-c1d2e3f4-a5b6-7890-abcd-ef1234567890
        client_secret:
          type: string
          description: Keycloak client secret — shown only on creation, store securely
          example: abc123secretvalue
        webhook_secret:
          type: string
          description: >-
            HMAC-SHA256 secret for verifying webhook signatures — shown only on
            creation, store securely
          example: a3f8c2e1d4b7a9f0e2c5b8d1a4f7c0e3d6b9a2f5c8e1d4b7a0f3c6e9d2b5a8f1
        note:
          type: string
          example: >-
            Store client_secret and webhook_secret securely — they are only
            shown once.
    Counter:
      type: object
      description: A POS terminal belonging to a merchant
      properties:
        id:
          type: string
          format: uuid
          example: c1d2e3f4-a5b6-7890-abcd-ef1234567890
        merchant_id:
          type: string
          format: uuid
          example: 74815c83-e47b-4dc5-aefe-abe4484dd280
        name:
          type: string
          example: Counter 1
        location:
          type: string
          example: Floor 2, Register 3
        keycloak_client_id:
          type: string
          description: >-
            Keycloak client ID for this counter (for reference; secret not
            shown)
          example: counter-c1d2e3f4-a5b6-7890-abcd-ef1234567890
        webhook_url:
          type: string
          format: uri
          example: https://pos.example.com/webhooks/payments
        status:
          type: string
          enum:
            - active
            - inactive
          example: active
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - merchant_id
        - name
        - status
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          example: Invalid request body
      required:
        - success
        - error
  responses:
    BadRequest:
      description: Bad request — invalid input
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: Amount must be greater than 0
    Unauthorized:
      description: Unauthorized — missing or invalid JWT
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: Unauthorized
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: Failed to create payment intent
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        Keycloak JWT. Roles: `admin` (platform admin — can register merchants,
        act on behalf of any merchant), `merchant` (dashboard user — scoped to
        their own `merchant_id` via user attribute mapper), POS terminals use
        client credentials flow with `merchant_id` injected via protocol mapper.

````