---
title: "Refund all or part of a sale (admin only)"
method: POST
path: "/v1/billing/refunds"
---

# Refund all or part of a sale (admin only)

`POST /v1/billing/refunds`

## Request

- Base URL: `https://api.clichefactory.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.clichefactory.com/v1/billing/refunds`
- Auth: none declared

Record a Paddle refund or an invoice credit note (dobropis).

Writes a negative ``credit_grants`` row linked to the sale it reverses: the
balance and ``total_credits_purchased`` go down, revenue reporting nets the
money off, and ``total_credits_used`` is untouched because nothing was
consumed. An admin clawback cannot do this — it books as consumption.

Idempotent on ``(provider, provider_txn_id)`` like ``/v1/billing/credits``:
a retry returns the original refund with ``credited: false``.

Rejections: ``404 original_grant_not_found``; ``422 refund_mismatch``
(other tenant or currency), ``refund_exceeds_original`` (cumulative
refunds would pass the sale's credits or money), ``idempotency_key_reused``;
``409 insufficient_balance`` when the tenant has already burned the credits
— resend with fewer ``credits`` and refund the rest as money only.

## Headers

- `X-API-KEY` string, nullable

## Request body

- RefundRequest: Refund all or part of a sale: a Paddle refund or an invoice credit note. Amounts are positive — what goes back to the customer. Either ``credits`` or ``gross_amount_minor`` may be zero, not both:: {"tenant_id": "acme", "credits": 10000, "currency": "EUR", "gross_amount_minor": 2000, "provider": "invoice", "provider_txn_id": "DOB-2026-001", "reverses_provider_txn_id": "RAC-2026-004"}
  - `tenant_id` string, required
  - `credits` integer, required: Credits taken back from the balance.
  - `currency` string, required: Must match the sale's currency.
  - `gross_amount_minor` integer: Money refunded, incl. tax.
  - `tax_amount_minor` integer
  - `provider` string, required: Same provider as the sale: 'invoice' | 'paddle'.
  - `provider_txn_id` string, required: The refund's own id (dobropis number); the idempotency key.
  - `reverses_provider_txn_id` string, required: provider_txn_id of the sale being refunded (the invoice number).
  - `reason` string

## Response `200`

Successful Response

- RefundResponse: ``credited`` is False when ``provider_txn_id`` was already recorded: nothing moved, and the body describes the original refund. Treat it as success.
  - `tenant_id` string, required
  - `grant_id` integer, required
  - `reverses_grant_id` integer, required
  - `credits_refunded` integer, required
  - `currency` string, required
  - `gross_amount_minor` integer, required
  - `new_balance_credits` integer, required
  - `credited` boolean, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/clichefactory/apis/aio-server.md) · [All operations](https://skmtc.dev/clichefactory/apis/aio-server/llms.txt) · [OpenAPI document](https://skmtc.dev/clichefactory/apis/aio-server/revisions/9bd59f9bbb31?raw)
