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

# Create a refund

> Refunds a payment that has already succeeded. The `amount` is always
required. To refund the full payment, set `amount` to the payment's
remaining refundable amount. The refund uses the same currency as the
original payment; currency is not accepted on the refund request.

A payment can be refunded multiple times as long as the accumulated
refunded amount does not exceed the original.

The refund is accepted for processing and starts with `status: "PENDING"`.
Monitor the refund's progress via the returned Refund object, subsequent
`GET /v1/payments/{id}` calls, or webhooks. The status transitions to
`SUCCEEDED` when the refund is confirmed or `FAILED` when it cannot be
completed.

Every refund is recorded with a mandatory `reason` and is linked to the
original payment via `payment_id`.

## Idempotency

Use the `Idempotency-Key` header to safely retry refund requests. The
idempotency contract is body-hash-based: a retry with the exact same body
returns the original response, while a retry with the same key but any
different field, including a different `reason`, `amount`, or `merchant_reference`, is rejected
with `409` and `error.code: "idempotency_key_reused"`. Pick a
deterministic key tied to the refund operation and resend the same body
on retries.




## OpenAPI

````yaml /api-reference/payments-api.yaml post /v1/refunds
openapi: 3.0.4
info:
  title: Flowlix Payments API
  version: 1.0.0
  description: >
    The Flowlix Payments API is a RESTful API for accepting and managing online
    payments.

    It follows industry-standard conventions: JSON request/response bodies,
    Bearer token authentication,

    standard HTTP verbs, idempotency support, and cursor-based pagination.


    ## Base URL


    All API requests are made to `https://api.flowlix.dev`. Current endpoints

    are versioned under `/v1`, e.g. `https://api.flowlix.dev/v1/payments`.


    ## Amounts


    All monetary amounts are expressed in **minor units** (the smallest currency
    unit).

    For example, `4999` in EUR represents **EUR 49.99**.
  contact:
    name: Flowlix Developer Support
    email: developers@flowlix.eu
    url: https://flowlix.dev/support
  license:
    name: Proprietary
    url: https://flowlix.dev/terms
servers:
  - url: https://api.flowlix.dev
    description: |
      Flowlix API. Test mode and live mode share the same host; the mode is
      selected by the API key prefix (`api_test_sk_*` or `api_live_sk_*`).
security:
  - BearerAuth: []
tags:
  - name: Payments
    description: Create, retrieve, and list payments.
  - name: Refunds
    description: >-
      Create refunds and track their status through the parent payment's
      `refunds` array.
paths:
  /v1/refunds:
    post:
      tags:
        - Refunds
      summary: Create a refund
      description: >
        Refunds a payment that has already succeeded. The `amount` is always

        required. To refund the full payment, set `amount` to the payment's

        remaining refundable amount. The refund uses the same currency as the

        original payment; currency is not accepted on the refund request.


        A payment can be refunded multiple times as long as the accumulated

        refunded amount does not exceed the original.


        The refund is accepted for processing and starts with `status:
        "PENDING"`.

        Monitor the refund's progress via the returned Refund object, subsequent

        `GET /v1/payments/{id}` calls, or webhooks. The status transitions to

        `SUCCEEDED` when the refund is confirmed or `FAILED` when it cannot be

        completed.


        Every refund is recorded with a mandatory `reason` and is linked to the

        original payment via `payment_id`.


        ## Idempotency


        Use the `Idempotency-Key` header to safely retry refund requests. The

        idempotency contract is body-hash-based: a retry with the exact same
        body

        returns the original response, while a retry with the same key but any

        different field, including a different `reason`, `amount`, or
        `merchant_reference`, is rejected

        with `409` and `error.code: "idempotency_key_reused"`. Pick a

        deterministic key tied to the refund operation and resend the same body

        on retries.
      operationId: createRefund
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateRefundRequest'
            examples:
              partial:
                summary: Refund part of a payment
                value:
                  payment_id: pay_q7Mk2Np8Vr4Xt6Yz9Ab3Cd5E
                  amount: 1500
                  merchant_reference: 3456789012
                  reason: Customer requested a refund
      responses:
        '201':
          description: |
            Refund accepted and a Refund object was created. The refund may
            still be `PENDING` or `PROCESSING`; use the returned `status`,
            subsequent payment retrievals, or webhooks to track completion.
          headers:
            Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Refund'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: >
        A unique key to ensure the request is processed only once. If a request

        with the same idempotency key and the same body has already been

        processed successfully, the original response is returned. Non-2xx

        responses are not cached, so callers may retry after errors. Keys expire

        after 24 hours.


        For one-shot requests, such as creating a payment from a checkout
        button,

        a random UUIDv4 is fine. For requests tied to a specific business

        operation, such as refunding an order, a deterministic key is preferred

        so retries collapse correctly across processes.
      schema:
        type: string
        maxLength: 255
      example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
  schemas:
    CreateRefundRequest:
      type: object
      required:
        - payment_id
        - amount
        - reason
      properties:
        payment_id:
          $ref: '#/components/schemas/PaymentId'
        amount:
          type: integer
          format: int64
          minimum: 1
          description: >-
            Refund amount in minor units, in the same currency as the original
            payment. Use the remaining refundable amount for a full refund.
          example: 1500
        merchant_reference:
          $ref: '#/components/schemas/MerchantReference'
        reason:
          type: string
          minLength: 1
          maxLength: 50
          description: >-
            Merchant-provided refund reason passed to the payment provider as
            the refund comment. Limited to 50 characters by WhiteLark.
          example: Full refund verification
    Refund:
      type: object
      description: A refund against a payment.
      required:
        - id
        - payment_id
        - amount
        - currency
        - reason
        - refund_type
        - status
        - created_at
        - updated_at
        - livemode
      properties:
        id:
          $ref: '#/components/schemas/RefundId'
        payment_id:
          $ref: '#/components/schemas/PaymentId'
        amount:
          type: integer
          format: int64
          minimum: 1
          description: Refund amount in minor units.
          example: 1500
        currency:
          $ref: '#/components/schemas/Currency'
        merchant_reference:
          type: integer
          allOf:
            - $ref: '#/components/schemas/MerchantReference'
          nullable: true
          description: Merchant-side reconciliation reference for this refund, if provided.
        reason:
          type: string
          maxLength: 50
          description: Merchant-provided refund reason stored with the refund.
          example: Full refund verification
        refund_type:
          $ref: '#/components/schemas/RefundType'
        status:
          $ref: '#/components/schemas/RefundStatus'
        failure:
          $ref: '#/components/schemas/RefundFailure'
        created_at:
          type: integer
          format: int64
          description: Unix timestamp when the refund was created.
          example: 1719795600
        updated_at:
          type: integer
          format: int64
          description: Unix timestamp when the refund was last updated.
          example: 1719795660
        completed_at:
          type: integer
          nullable: true
          format: int64
          description: Unix timestamp when the refund reached a terminal status.
          example: 1719799200
        livemode:
          type: boolean
          description: Whether this refund was created using a live API key.
          example: false
    PaymentId:
      type: string
      pattern: ^pay_[A-Za-z0-9]{24}$
      description: >-
        Unique opaque identifier for a payment (`pay_` prefix + random
        alphanumeric suffix).
      example: pay_q7Mk2Np8Vr4Xt6Yz9Ab3Cd5E
    MerchantReference:
      type: integer
      format: int64
      minimum: 1000000000
      maximum: 9999999999
      description: |
        Optional merchant-side reconciliation reference. The value must contain
        exactly 10 decimal digits and does not provide idempotency by itself.
      example: 1234567890
    RefundId:
      type: string
      pattern: ^ref_[A-Za-z0-9]{24}$
      description: >-
        Unique opaque identifier for a refund (`ref_` prefix + random
        alphanumeric suffix).
      example: ref_L9xQ4wE2rT8yU6iO3pA7sD1f
    Currency:
      type: string
      enum:
        - EUR
      description: |
        Three-letter ISO 4217 currency code. Currently supported for card
        acquiring: `EUR`.
      example: EUR
    RefundType:
      type: string
      enum:
        - FULL
        - PARTIAL
      description: |
        Whether the refund covers the full original payment amount or only part
        of it.
      example: FULL
    RefundStatus:
      type: string
      enum:
        - PENDING
        - PROCESSING
        - SUCCEEDED
        - FAILED
      description: |
        Current refund lifecycle status. Refunds normally move from `PENDING`
        to `PROCESSING`, then to `SUCCEEDED` or `FAILED`.
      example: PENDING
    RefundFailure:
      type: object
      nullable: true
      description: Refund failure details when the refund reaches `FAILED`.
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Machine-readable Flowlix error code.
          example: fraud_filter
        message:
          type: string
          description: Human-readable Flowlix error explanation.
          example: The payment was declined by fraud controls.
    ApiError:
      type: object
      description: Error response wrapper.
      properties:
        error:
          $ref: '#/components/schemas/ApiErrorBody'
    ApiErrorBody:
      type: object
      description: Detailed error information.
      required:
        - message
      properties:
        code:
          type: string
          nullable: true
          description: Short machine-readable error code.
          example: parameter_invalid
        message:
          type: string
          description: Human-readable message explaining the error.
          example: One or more request parameters failed validation.
        param:
          type: string
          nullable: true
          description: Request parameter that caused the error, if applicable.
          example: payment_method_data[card][number]
        doc_url:
          type: string
          nullable: true
          format: uri
          description: URL to documentation for this error.
          example: https://docs.flowlix.dev
        request_id:
          type: string
          nullable: true
          description: Request ID matching the `Request-Id` response header.
          example: req_abc123def456
  headers:
    RequestId:
      description: >-
        A unique identifier for this API request. Include it when contacting
        support.
      schema:
        type: string
        maxLength: 255
      example: req_abc123def456
    RetryAfter:
      description: Number of seconds to wait before retrying.
      schema:
        type: integer
      example: 5
  responses:
    BadRequest:
      description: |
        The request could not be parsed or failed request validation. Check the
        JSON body, required headers, required fields, field formats, and
        `error.param` when it is present.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    Unauthorized:
      description: |
        The API key is missing, invalid, expired, or revoked. Send your secret
        key in the `Authorization: Bearer <key>` header and verify that the key
        has the expected `api_test_sk_` or `api_live_sk_` prefix.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    Forbidden:
      description: |
        The API key is valid, but it is not allowed to perform this operation.
        Check that the key belongs to the correct merchant account, has access
        to this endpoint, and matches the resource's test or live mode.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    NotFound:
      description: |
        No payment was found for the supplied ID, or it is
        not accessible with this API key. Check the ID prefix, merchant account,
        and test or live mode.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    IdempotencyConflict:
      description: |
        The `Idempotency-Key` is still being processed, or it was reused for a
        different request. Retry the original request unchanged, or use a new
        idempotency key for a new payment or refund operation.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    UnprocessableEntity:
      description: |
        The request passed basic validation, but the operation cannot be
        completed in the resource's current state — for example, a refund
        amount that exceeds the payment's remaining refundable amount. Check
        `error.code`, `error.message`, and `error.param` when present to decide
        how to fix the request. Note that card declines are not reported through
        this status: a declined payment returns `201` with `status: "FAILED"`
        and a `failure_code` on the Payment object.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    InternalError:
      description: >
        Flowlix encountered an unexpected server error before the operation
        could

        be completed. Retry safely with the same idempotency key when retrying a

        POST request, and include the `Request-Id` if you contact support.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    ServiceUnavailable:
      description: |
        Flowlix is temporarily unable to process the request. Wait for
        `Retry-After` when present, then retry with exponential backoff. Reuse
        the same idempotency key when retrying a POST request.
      headers:
        Request-Id:
          $ref: '#/components/headers/RequestId'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: |
        Use your secret API key as the Bearer token. Test mode keys start with
        `api_test_sk_` and live mode keys start with `api_live_sk_`. Both modes
        use the same API host; the mode is determined by the key.

        ```
        Authorization: Bearer api_test_sk_abc123def456
        ```

````