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

# Place an order

> Creates an order asynchronously. The response returns immediately with an `orderId` and a `processing` status, plus an `estimatedReadyAt` timestamp that indicates when to start polling for codes. Larger orders receive a later `estimatedReadyAt`.

Send the catalog `productCode` and the `price` you expect to pay (used to validate the order). For fixed-price products the price must match the current catalog price; for open-denomination products send the chosen amount within the allowed range.

The `externalOrderCode` is your idempotency key: re-posting the same code returns the existing order instead of creating a duplicate.



## OpenAPI

````yaml /openapi.json post /orders
openapi: 3.1.0
info:
  title: Kalixo Distribution API
  version: 2.1.3
  description: >-
    The Kalixo Distribution API lets partners browse their product catalog,
    place and track digital gift-card orders (single or bulk), and monitor their
    wallet balance. All v2 ordering is asynchronous: place an order, then poll
    it until the codes are delivered.
  contact:
    name: Kalixo Integrations
    email: integrations@kalixo.io
servers:
  - url: https://api.kalixo.io/v2
    description: Production (pre-release placeholder)
  - url: https://sandbox.kalixo.io/v2
    description: Sandbox
security:
  - apiKey: []
tags:
  - name: Ping
    description: >-
      Verify API connectivity once your API key is issued and your IP is
      whitelisted.
  - name: Catalog
    description: >-
      Browse the products available in your catalog, including pricing, your
      discount amount and percentage, and open-denomination ranges.
  - name: Orders
    description: >-
      Place orders and poll them until codes are delivered. Single and bulk
      orders share the same asynchronous lifecycle.
  - name: Wallet
    description: Check your current wallet balances per currency.
paths:
  /orders:
    post:
      tags:
        - Orders
      summary: Place an order
      description: >-
        Creates an order asynchronously. The response returns immediately with
        an `orderId` and a `processing` status, plus an `estimatedReadyAt`
        timestamp that indicates when to start polling for codes. Larger orders
        receive a later `estimatedReadyAt`.


        Send the catalog `productCode` and the `price` you expect to pay (used
        to validate the order). For fixed-price products the price must match
        the current catalog price; for open-denomination products send the
        chosen amount within the allowed range.


        The `externalOrderCode` is your idempotency key: re-posting the same
        code returns the existing order instead of creating a duplicate.
      operationId: placeOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlaceOrderRequest'
      responses:
        '202':
          description: Order accepted and now processing.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderAccepted'
        '400':
          description: >-
            Validation or order rejection. See the Errors guide for the full
            message catalogue.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                priceMismatch:
                  $ref: '#/components/examples/ErrorPriceMismatch'
                orderTotalMismatch:
                  $ref: '#/components/examples/ErrorOrderTotalMismatch'
                openDenomRequiresPrice:
                  $ref: '#/components/examples/ErrorOpenDenomRequiresPrice'
                productNotOrderable:
                  $ref: '#/components/examples/ErrorProductNotOrderable'
                orderCouldNotBePlaced:
                  $ref: '#/components/examples/ErrorOrderCouldNotBePlaced'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: API ordering disabled for your account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                apiOrderingDisabled:
                  $ref: '#/components/examples/ErrorApiOrderingDisabled'
        '404':
          $ref: '#/components/responses/CatalogProductNotFound'
        '409':
          description: An order with the same externalOrderCode already exists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                duplicateExternalOrderCode:
                  $ref: '#/components/examples/ErrorDuplicateExternalOrderCode'
        '422':
          description: >-
            Order limit exceeded, insufficient wallet funds, or product not
            available for purchase.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                orderLimitExceeded:
                  $ref: '#/components/examples/ErrorOrderLimitExceeded'
                insufficientFunds:
                  $ref: '#/components/examples/ErrorInsufficientFunds'
                productNotAvailableForPurchase:
                  $ref: '#/components/examples/ErrorProductNotAvailableForPurchase'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          description: Order could not be created or is temporarily unavailable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                orderUnavailable:
                  $ref: '#/components/examples/ErrorOrderUnavailable'
                orderNotCreated:
                  $ref: '#/components/examples/ErrorOrderNotCreated'
components:
  schemas:
    PlaceOrderRequest:
      type: object
      required:
        - externalOrderCode
        - currency
        - orderProducts
      properties:
        externalOrderCode:
          type: string
          description: Your unique reference for this order. Acts as the idempotency key.
          example: O-12345
        currency:
          type: string
          example: GBP
        price:
          type: integer
          description: >-
            Optional expected order total in minor units. If sent, must equal
            the sum of (line unit price × quantity).
          example: 10000
        orderProducts:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/OrderLine'
    OrderAccepted:
      type: object
      properties:
        orderId:
          type: string
          format: uuid
          example: b9f6f9a2-4d0a-4f5c-9a1e-7c2d8f3b6e10
        externalOrderCode:
          type: string
          example: O-12345
        status:
          type: string
          enum:
            - processing
          example: processing
        estimatedReadyAt:
          type: string
          format: date-time
          description: >-
            Approximate time the codes should be ready. Poll the order after
            this time.
          example: '2026-06-02T11:05:00Z'
        wallet:
          $ref: '#/components/schemas/WalletSnapshot'
    Error:
      type: object
      description: >-
        Standard error response. See the Errors concept page for the complete
        message catalogue.
      required:
        - statusCode
        - message
      properties:
        statusCode:
          type: integer
          example: 400
        error:
          type: string
          description: >-
            Short label for the status (for example `Bad Request`, `Not Found`).
            Omitted on some `500` responses.
          example: Bad Request
        message:
          type: string
          description: Human-readable explanation. May include the productCode you sent.
          example: 'Product 10020000213575 price mismatch: expected 10000'
    OrderLine:
      type: object
      required:
        - productCode
        - quantity
      properties:
        productCode:
          type: string
          description: The catalog product code.
          example: '10020000213575'
        price:
          type: integer
          description: >-
            Expected unit price in minor units. For fixed products this must
            match the catalog price; for open-denomination products this is the
            chosen amount within the allowed range.
          example: 10000
        quantity:
          type: integer
          minimum: 1
          example: 1
        currency:
          type: string
          description: Optional line currency. Defaults to the order-level currency.
          example: GBP
        sku:
          type: string
          description: Optional locale SKU from the catalog (`{CC}-{LL}-{productCode}`).
          example: GB-EN-10020000213575
    WalletSnapshot:
      type: object
      properties:
        balances:
          type: object
          additionalProperties:
            type: number
          example:
            GBP: 196.01
            EUR: 1234.56
        lowBalance:
          type: boolean
          example: false
  headers:
    X-RateLimit-Limit:
      description: Maximum requests allowed in the current 60-second window.
      schema:
        type: integer
        example: 60
    X-RateLimit-Remaining:
      description: Requests remaining in the current window.
      schema:
        type: integer
        example: 59
    X-RateLimit-Reset:
      description: Unix timestamp (seconds) when the current window resets.
      schema:
        type: integer
        example: 1717315200
  examples:
    ErrorPriceMismatch:
      summary: Price mismatch
      value:
        statusCode: 400
        error: Bad Request
        message: 'Product 10020000213575 price mismatch: expected 10000'
    ErrorOrderTotalMismatch:
      summary: Order total mismatch
      value:
        statusCode: 400
        error: Bad Request
        message: 'Order price mismatch: expected total 15000'
    ErrorOpenDenomRequiresPrice:
      summary: Open denomination requires price
      value:
        statusCode: 400
        error: Bad Request
        message: Product 10001000000814 is open-denomination and requires a price
    ErrorProductNotOrderable:
      summary: Product not orderable for country/currency
      value:
        statusCode: 400
        error: Bad Request
        message: >-
          Product 10001000000405 is not available for ordering with the supplied
          country or currency.
    ErrorOrderCouldNotBePlaced:
      summary: Generic order rejection
      value:
        statusCode: 400
        error: Bad Request
        message: The order could not be placed. Check your request and try again.
    ErrorApiOrderingDisabled:
      summary: API ordering disabled
      value:
        statusCode: 403
        error: Forbidden
        message: >-
          API ordering is disabled for your account. Contact support for
          assistance.
    ErrorDuplicateExternalOrderCode:
      summary: Duplicate externalOrderCode
      value:
        statusCode: 409
        error: Conflict
        message: An order with this external order code already exists.
    ErrorOrderLimitExceeded:
      summary: Order limit exceeded
      value:
        statusCode: 422
        error: Unprocessable Entity
        message: >-
          Your account order limit has been exceeded. Contact support for
          assistance.
    ErrorInsufficientFunds:
      summary: Insufficient wallet funds
      value:
        statusCode: 422
        error: Unprocessable Entity
        message: Insufficient funds. Please top up your wallet and try again.
    ErrorProductNotAvailableForPurchase:
      summary: Product not available for purchase
      value:
        statusCode: 422
        error: Unprocessable Entity
        message: Product 10020000213575 is not available for purchase.
    ErrorOrderUnavailable:
      summary: Order service unavailable
      value:
        statusCode: 502
        error: Bad Gateway
        message: Order processing is temporarily unavailable. Please try again later.
    ErrorOrderNotCreated:
      summary: Order not created
      value:
        statusCode: 502
        error: Bad Gateway
        message: Order could not be created. Please try again later.
    ErrorMissingApiKey:
      summary: Missing API key
      value:
        statusCode: 401
        error: Unauthorized
        message: Missing API key
    ErrorInvalidApiKey:
      summary: Invalid API key
      value:
        statusCode: 401
        error: Unauthorized
        message: Invalid API key
    ErrorCatalogProductNotFound:
      summary: Catalog product not found
      value:
        statusCode: 404
        error: Not Found
        message: Product 10001000000999 was not found in your catalog
    ErrorInternalServerError:
      summary: Unexpected server error
      value:
        statusCode: 500
        message: Internal server error
  responses:
    Unauthorized:
      description: Missing or invalid `x-api-key`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missingApiKey:
              $ref: '#/components/examples/ErrorMissingApiKey'
            invalidApiKey:
              $ref: '#/components/examples/ErrorInvalidApiKey'
    CatalogProductNotFound:
      description: Product not found in your active catalog.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            productNotFound:
              $ref: '#/components/examples/ErrorCatalogProductNotFound'
    InternalServerError:
      description: Unexpected server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            internalServerError:
              $ref: '#/components/examples/ErrorInternalServerError'
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: Your Kalixo API key. Send it in the `x-api-key` header on every request.
      x-default: kal_live_xxxxxxxxxxxxxxxxxxxxxxxx

````