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

# Refund payment transaction

> <Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Refunds all or part of a payment through its payment provider, then returns the payment's details after the refund.

<Warning>In live mode this sends real money back to the customer, and a refund can't be undone.</Warning>

Send `amount` in the smallest unit of the payment's currency. Refunding less than the full amount is a partial refund, and you can refund again later up to what's left. The provider checks the amount, so one above the refundable balance is rejected.

Send an idempotency key that stays the same across retries of one refund attempt, and a new one for each new refund. Put it in the `Idempotency-Key` header or in `idempotency_key` in the body. A retry with the same key after the refund went through returns the payment without refunding again, and a retry with the same key but a different amount is rejected. For Stripe, the key is also passed to Stripe as its own idempotency key.

Wix has no idempotency key of its own. When a Wix refund ends without a clear answer, its key stays reserved and a retry with it is rejected, so check `refunds` with [Get payment transaction](/api-reference/get-payment-transaction) before trying again with a new key.

Each refund is recorded in the workspace's audit log as a `payments.transaction.refunded` [event](/developers/references/audit-logs-api/get-started/event-types).

This is limited to 30 requests an hour per user. Some workspaces have a different limit.

<Note>This endpoint accepts a personal API key belonging to a user with write access to the app. A read-only key is refused, and so is a viewer.</Note>



## OpenAPI

````yaml /developers/references/app-management/app-management-openapi.json post /api/apps/{app_id}/payments/analytics/transactions/{transaction_id}/refund
openapi: 3.1.0
info:
  title: Base44 App Management API
  version: 1.0.0
servers:
  - url: https://app.base44.com
security:
  - PersonalAccessTokenAuth: []
paths:
  /api/apps/{app_id}/payments/analytics/transactions/{transaction_id}/refund:
    post:
      summary: Refund payment transaction
      description: >-
        <Info>This API is in beta. Endpoints, fields, and behavior may still
        change, so avoid depending on it in production.</Info>


        Refunds all or part of a payment through its payment provider, then
        returns the payment's details after the refund.


        <Warning>In live mode this sends real money back to the customer, and a
        refund can't be undone.</Warning>


        Send `amount` in the smallest unit of the payment's currency. Refunding
        less than the full amount is a partial refund, and you can refund again
        later up to what's left. The provider checks the amount, so one above
        the refundable balance is rejected.


        Send an idempotency key that stays the same across retries of one refund
        attempt, and a new one for each new refund. Put it in the
        `Idempotency-Key` header or in `idempotency_key` in the body. A retry
        with the same key after the refund went through returns the payment
        without refunding again, and a retry with the same key but a different
        amount is rejected. For Stripe, the key is also passed to Stripe as its
        own idempotency key.


        Wix has no idempotency key of its own. When a Wix refund ends without a
        clear answer, its key stays reserved and a retry with it is rejected, so
        check `refunds` with [Get payment
        transaction](/api-reference/get-payment-transaction) before trying again
        with a new key.


        Each refund is recorded in the workspace's audit log as a
        `payments.transaction.refunded`
        [event](/developers/references/audit-logs-api/get-started/event-types).


        This is limited to 30 requests an hour per user. Some workspaces have a
        different limit.


        <Note>This endpoint accepts a personal API key belonging to a user with
        write access to the app. A read-only key is refused, and so is a
        viewer.</Note>
      operationId: >-
        refund_transaction_api_apps__app_id__payments_analytics_transactions__transaction_id__refund_post
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
            description: ID of the Base44 app.
            title: App Id
          description: ID of the Base44 app.
          example: 6820f3a4e7b91d003c45a1f2
        - name: transaction_id
          in: path
          required: true
          schema:
            type: string
            description: >-
              ID of the transaction, taken from `transaction_id` on a row from
              [List payment
              transactions](/api-reference/list-payment-transactions).
            title: Transaction Id
          description: >-
            ID of the transaction, taken from `transaction_id` on a row from
            [List payment
            transactions](/api-reference/list-payment-transactions).
          example: pi_T7mK2p9Q4r6S8v
        - name: provider
          in: query
          required: true
          schema:
            enum:
              - stripe
              - wix
            type: string
            description: >-
              Payment provider whose transactions to use. Either `stripe` or
              `wix`. `stripe` covers payments taken through the app's Stripe
              integration, and `wix` covers payments taken through Wix Payments
              (Base44 Payments). The provider has to be connected to the app.
            title: Provider
          description: >-
            Payment provider whose transactions to use. Either `stripe` or
            `wix`. `stripe` covers payments taken through the app's Stripe
            integration, and `wix` covers payments taken through Wix Payments
            (Base44 Payments). The provider has to be connected to the app.
          example: stripe
        - name: timestamp
          in: query
          required: false
          schema:
            anyOf:
              - type: string
                format: date-time
              - type: 'null'
            description: >-
              The row's own `timestamp` from [List payment
              transactions](/api-reference/list-payment-transactions), which
              makes the lookup faster. Leave it out rather than guess, since a
              wrong value can make the transaction look like it isn't the app's.
            title: Timestamp
          description: >-
            The row's own `timestamp` from [List payment
            transactions](/api-reference/list-payment-transactions), which makes
            the lookup faster. Leave it out rather than guess, since a wrong
            value can make the transaction look like it isn't the app's.
          example: '2026-08-25T10:00:00Z'
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            anyOf:
              - type: string
                maxLength: 100
                minLength: 1
              - type: 'null'
            description: >-
              Key for this refund attempt, the same as `idempotency_key` in the
              body. Send it in either place, or in both with the same value.
            title: Idempotency-Key
          description: >-
            Key for this refund attempt, the same as `idempotency_key` in the
            body. Send it in either place, or in both with the same value.
          example: refund-2026-08-25-7f3c9a
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefundTransactionRequest'
      responses:
        '200':
          description: >-
            The provider accepted the refund. The body is the payment after it,
            or the payment from before it when the details can't be read again
            right away.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionDetails'
        '400':
          description: >-
            The provider rejected the refund, for example because `amount` is
            above the refundable balance or the payment is disputed.
            `error.code` is `refund_rejected`. For Stripe,
            `error.details.reason` is `amount_exceeds_refundable_balance`,
            `already_refunded`, `charge_disputed`, `provider_not_configured`, or
            `rejected_by_provider`. Also returned with `error.code`
            `idempotency_key_required` when no idempotency key is sent, and
            `idempotency_key_mismatch` when the header and the body carry
            different ones.
        '401':
          description: Missing or invalid credentials.
        '403':
          description: >-
            You don't have write access to the app, it doesn't exist, or your
            API key is read-only. A missing app and an app you cannot reach are
            deliberately the same answer.
        '404':
          description: >-
            The app is outside the credential grant, or `provider` isn't
            connected to the app. Also returned with `error.code`
            `transaction_not_found` when the transaction isn't one of the app's
            in the current mode, or the provider doesn't have it.
        '409':
          description: >-
            A refund with this `idempotency_key` is still being processed
            (`refund_in_progress`), or it already went through with a different
            amount (`refund_amount_mismatch`). Also returned when the workspace
            requires an unlocked SSO session.
        '422':
          description: >-
            `amount` isn't a positive integer, an idempotency key is empty or
            longer than 100 characters, `note` is longer than 500 characters,
            the body has other fields, or `provider` is missing or isn't
            `stripe` or `wix`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '429':
          description: Rate limit reached. Retry later.
components:
  schemas:
    RefundTransactionRequest:
      properties:
        amount:
          type: integer
          exclusiveMinimum: 0
          title: Amount
          description: >-
            Amount to refund, in the smallest unit of the payment's own
            currency. Must be above 0.
          example: 1000
        note:
          anyOf:
            - type: string
              maxLength: 500
            - type: 'null'
          title: Note
          description: >-
            Private note stored with the refund, up to 500 characters. Defaults
            to `null`.
          example: Customer returned one item.
        idempotency_key:
          anyOf:
            - type: string
              maxLength: 100
              minLength: 1
            - type: 'null'
          title: Idempotency Key
          description: >-
            Key you generate for this refund attempt, up to 100 characters. Keep
            it the same across retries of one attempt, and use a new one for
            each new refund. Required unless you send it in the
            `Idempotency-Key` header instead.
          example: refund-2026-08-25-7f3c9a
      additionalProperties: false
      type: object
      required:
        - amount
      title: RefundTransactionRequest
      description: A refund of all or part of one payment.
    TransactionDetails:
      properties:
        transaction_id:
          type: string
          title: Transaction Id
          description: >-
            Provider ID of the payment. For Stripe this is the charge, which can
            differ from the ID you looked it up by.
          example: ch_T7mK2p9Q4r6S8v
        status:
          type: string
          title: Status
          description: >-
            Where the payment stands. Either `succeeded`, `pending`, `failed`,
            `refunded`, `partially_refunded`, `pending_refund`, or `chargeback`.
            Other provider values come through in lowercase.
          default: ''
          example: partially_refunded
        payment_type:
          type: string
          title: Payment Type
          description: >-
            Either `one_time` or `recurring`, or an empty string when the
            provider doesn't say.
          default: ''
          example: one_time
        created_date:
          type: string
          title: Created Date
          description: >-
            When the payment was made, as an ISO 8601 timestamp. Empty when the
            provider doesn't say.
          default: ''
          example: '2026-08-25T10:00:00Z'
        currency:
          type: string
          title: Currency
          description: >-
            Three-letter ISO 4217 code of the payment's currency, in uppercase.
            Defaults to `USD`.
          default: USD
          example: USD
        amount:
          type: integer
          title: Amount
          description: Amount paid, in the smallest unit of the payment's currency.
          default: 0
          example: 2500
        fee:
          anyOf:
            - type: integer
            - type: 'null'
          title: Fee
          description: >-
            Processing fee the provider took, in the smallest unit of the
            payment's currency, or `null` when none was taken, as on a declined
            payment.
          example: 103
        net:
          anyOf:
            - type: integer
            - type: 'null'
          title: Net
          description: >-
            What the merchant kept after the fee, in the smallest unit of the
            payment's currency, or `null` when no money moved.
          example: 2397
        service_fee:
          anyOf:
            - type: integer
            - type: 'null'
          title: Service Fee
          description: >-
            Application fee taken on top of the processing fee, in the smallest
            unit of the payment's currency, or `null` when there is none.
          example: 50
        refunded_amount:
          type: integer
          title: Refunded Amount
          description: >-
            Total refunded so far, in the smallest unit of the payment's
            currency.
          default: 0
          example: 1000
        refunds:
          items:
            $ref: '#/components/schemas/TransactionRefund'
          type: array
          title: Refunds
          description: Refunds against the payment. Empty when there are none.
          default: []
          example:
            - amount: 1000
              created_date: '2026-08-26T09:15:00Z'
              id: re_T7mK2p9Q4r6S8v
              status: succeeded
        method_kind:
          type: string
          title: Method Kind
          description: >-
            Payment method type as the provider names it, for example `card`.
            Empty when the provider doesn't say.
          default: ''
          example: card
        method_moto:
          type: boolean
          title: Method Moto
          description: >-
            `true` when the merchant entered the card for the customer, as in a
            phone or mail order, and `false` otherwise. Always `false` for
            Stripe.
          default: false
          example: false
        card_network:
          anyOf:
            - type: string
            - type: 'null'
          title: Card Network
          description: Card network, or `null` when the payment wasn't by card.
          example: visa
        card_masked:
          anyOf:
            - type: string
            - type: 'null'
          title: Card Masked
          description: >-
            Masked card number, or `null` when the payment wasn't by card.
            Stripe gives only the last four digits.
          example: '4242'
        card_holder:
          anyOf:
            - type: string
            - type: 'null'
          title: Card Holder
          description: Cardholder name, or `null` when the provider doesn't have it.
          example: Jane Doe
        provider_id:
          type: string
          title: Provider Id
          description: >-
            Processor that handled the payment. Always `stripe` for Stripe. For
            Wix, the processor Wix routed the payment through.
          default: ''
          example: stripe
        merchant_account_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Merchant Account Id
          description: >-
            Merchant account at the processor, or `null` when unknown. Always
            `null` for Stripe.
          example: 4f1c2a7e-9b3d-4e8f-a6c5-2d7e9f0b1a3c
        display_order_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Display Order Id
          description: >-
            Order number shown to the customer, or `null` when there is none.
            Always `null` for Stripe.
          example: '10042'
        items:
          items:
            $ref: '#/components/schemas/TransactionOrderItem'
          type: array
          title: Items
          description: Items bought. Empty when the provider doesn't list them.
          default: []
          example:
            - amount: 2500
              name: Oak side table
              quantity: 1
        billing:
          anyOf:
            - $ref: '#/components/schemas/TransactionAddress'
            - type: 'null'
          description: Billing details the customer gave, or `null` when there are none.
          example:
            address: 500 Terry Francine St
            city: San Francisco
            country_code: US
            email: jane@example.com
            name: Jane Doe
            phone: '+14155550123'
            postal_code: '94158'
            state: CA
        shipping:
          anyOf:
            - $ref: '#/components/schemas/TransactionAddress'
            - type: 'null'
          description: Shipping details, or `null` when the payment has none.
          example:
            address: 500 Terry Francine St
            city: San Francisco
            country_code: US
            name: Jane Doe
            phone: '+14155550123'
            postal_code: '94158'
            state: CA
        disputes:
          items:
            $ref: '#/components/schemas/TransactionDispute'
          type: array
          title: Disputes
          description: Disputes against the payment. Empty when there are none.
          default: []
          example: []
        history:
          items:
            $ref: '#/components/schemas/TransactionHistoryEvent'
          type: array
          title: History
          description: >-
            Timeline of the payment, its refunds, and its disputes, newest
            first.
          default: []
          example:
            - date: '2026-08-26T09:15:00Z'
              kind: refund
              lines:
                - amount: 1000
                  label: refunded_amount
              status: succeeded
            - date: '2026-08-25T10:00:00Z'
              kind: payment
              lines:
                - amount: 2500
                  label: amount
                - amount: -103
                  label: processing_fee
                - amount: 2397
                  label: net
              status: succeeded
        decline_reason:
          anyOf:
            - type: string
            - type: 'null'
          title: Decline Reason
          description: >-
            Why the payment was declined, as the provider words it, or `null`
            when it wasn't declined.
          example: Your card has insufficient funds.
      type: object
      required:
        - transaction_id
      title: TransactionDetails
      description: One payment in full, as its payment provider reports it.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    TransactionRefund:
      properties:
        id:
          type: string
          title: Id
          description: Provider ID of the refund.
          default: ''
          example: re_T7mK2p9Q4r6S8v
        amount:
          type: integer
          title: Amount
          description: Amount refunded, in the smallest unit of the payment's currency.
          default: 0
          example: 1000
        status:
          type: string
          title: Status
          description: >-
            Where the refund stands. Either `succeeded`, `pending`, or `failed`.
            Other provider values come through in lowercase.
          default: ''
          example: succeeded
        arn:
          anyOf:
            - type: string
            - type: 'null'
          title: Arn
          description: >-
            Acquirer reference number, which the customer's bank can use to
            trace the refund, or `null` until one is assigned. Always `null` for
            Stripe.
          example: '74537606238000012345678'
        note:
          anyOf:
            - type: string
            - type: 'null'
          title: Note
          description: >-
            Private note stored with the refund, or `null` when there is none.
            Always `null` for Stripe.
          example: Customer returned one item.
        created_date:
          type: string
          title: Created Date
          description: >-
            When the refund was created, as an ISO 8601 timestamp. Empty when
            the provider doesn't say.
          default: ''
          example: '2026-08-26T09:15:00Z'
      type: object
      title: TransactionRefund
      description: One refund against the payment.
    TransactionOrderItem:
      properties:
        name:
          type: string
          title: Name
          description: Name of the item. Empty when the provider doesn't say.
          default: ''
          example: Oak side table
        quantity:
          type: integer
          title: Quantity
          description: Number of units bought. Defaults to 1.
          default: 1
          example: 1
        amount:
          type: integer
          title: Amount
          description: >-
            Price of one unit, in the smallest unit of the payment's currency,
            rather than the line total.
          default: 0
          example: 2500
      type: object
      title: TransactionOrderItem
      description: A purchased line item.
    TransactionAddress:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Full name, or `null` when not provided.
          example: Jane Doe
        address:
          anyOf:
            - type: string
            - type: 'null'
          title: Address
          description: Street address, or `null` when not provided.
          example: 500 Terry Francine St
        city:
          anyOf:
            - type: string
            - type: 'null'
          title: City
          description: City, or `null` when not provided.
          example: San Francisco
        state:
          anyOf:
            - type: string
            - type: 'null'
          title: State
          description: State or region, or `null` when not provided.
          example: CA
        postal_code:
          anyOf:
            - type: string
            - type: 'null'
          title: Postal Code
          description: Postal code, or `null` when not provided.
          example: '94158'
        country_code:
          anyOf:
            - type: string
            - type: 'null'
          title: Country Code
          description: Two-letter country code, or `null` when not provided.
          example: US
        email:
          anyOf:
            - type: string
            - type: 'null'
          title: Email
          description: Email address, or `null` when not provided.
          example: jane@example.com
        phone:
          anyOf:
            - type: string
            - type: 'null'
          title: Phone
          description: Phone number, or `null` when not provided.
          example: '+14155550123'
      type: object
      title: TransactionAddress
      description: >-
        Billing or shipping details. Each provider fills a different subset of
        the fields.
    TransactionDispute:
      properties:
        id:
          type: string
          title: Id
          description: Provider ID of the dispute.
          default: ''
          example: dp_T7mK2p9Q4r6S8v
        state:
          type: string
          title: State
          description: >-
            Where the dispute stands. Either `open`, `won`, `lost`, or
            `accepted`. An `open` dispute needs a response by `deadline`. Other
            provider values come through in lowercase.
          default: ''
          example: open
        amount:
          type: integer
          title: Amount
          description: Amount disputed, in the smallest unit of the payment's currency.
          default: 0
          example: 2500
        fee:
          type: integer
          title: Fee
          description: >-
            Processing fee returned with the dispute, in the smallest unit of
            the payment's currency. Always 0 for Stripe.
          default: 0
          example: 0
        chargeback_fee:
          type: integer
          title: Chargeback Fee
          description: >-
            Fee charged for the dispute, in the smallest unit of the payment's
            currency. Always 0 for Stripe.
          default: 0
          example: 1500
        deadline:
          anyOf:
            - type: string
            - type: 'null'
          title: Deadline
          description: >-
            Deadline to respond to the dispute, as an ISO 8601 timestamp, or
            `null` when there is none.
          example: '2026-09-08T23:59:59Z'
        reason:
          anyOf:
            - type: string
            - type: 'null'
          title: Reason
          description: >-
            Reason the provider gives for the dispute, or `null` when it gives
            none.
          example: fraudulent
        created_date:
          type: string
          title: Created Date
          description: >-
            When the dispute was opened, as an ISO 8601 timestamp. Empty when
            the provider doesn't say.
          default: ''
          example: '2026-08-28T14:02:11Z'
      type: object
      title: TransactionDispute
      description: One dispute against the payment.
    TransactionHistoryEvent:
      properties:
        date:
          type: string
          title: Date
          description: >-
            When it happened, as an ISO 8601 timestamp. Empty when the provider
            doesn't say.
          default: ''
          example: '2026-08-25T10:00:00Z'
        kind:
          type: string
          title: Kind
          description: What happened. Either `payment`, `refund`, or `chargeback`.
          default: ''
          example: payment
        status:
          type: string
          title: Status
          description: >-
            Status of that step, using the same values as the matching payment,
            refund, or dispute status.
          default: ''
          example: succeeded
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: >-
            Text from the provider, such as a decline reason or dispute reason,
            or `null` when there is none.
          example: Your card has insufficient funds.
        lines:
          items:
            $ref: '#/components/schemas/TransactionHistoryLine'
          type: array
          title: Lines
          description: Money lines for this entry, such as the amount, fee, and net.
          default: []
          example:
            - amount: 2500
              label: amount
            - amount: -103
              label: processing_fee
            - amount: 2397
              label: net
      type: object
      title: TransactionHistoryEvent
      description: One entry in the payment's timeline.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    TransactionHistoryLine:
      properties:
        label:
          type: string
          title: Label
          description: >-
            What the line is. Either `amount`, `processing_fee`, `net`,
            `refunded_amount`, `chargeback_amount`, or `chargeback_fee`.
          example: processing_fee
        amount:
          type: integer
          title: Amount
          description: >-
            Amount of the line, in the smallest unit of the payment's currency.
            Money that left the merchant, such as a fee, is negative.
          example: -103
      type: object
      required:
        - label
        - amount
      title: TransactionHistoryLine
      description: One money line under a timeline entry.
  securitySchemes:
    PersonalAccessTokenAuth:
      type: http
      scheme: bearer
      description: 'Personal access token, sent as `Authorization: Bearer <token>`.'

````