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

# Cancel Gift

> Cancel a purchased-but-unredeemed gift, crediting the buyer if they paid or voiding the open purchase invoice if they did not.

Cancels a gift that was [purchased](/api-reference/gifts/purchase) but not
yet redeemed, and settles the buyer's money according to the state of the
purchase invoice after the cancel:

* **Invoice paid** — a spendable adjustment credit note for the purchase
  amount is issued to the buyer through the normal credit-note path, so
  approval governance and ledger postings apply. The note is returned as
  `credit_note` and can be tracked with
  [Get Credit Note](/api-reference/credit-notes/get).
* **Invoice still open** — nothing was collected, so the invoice is voided
  instead and `invoice_voided` is `true`. No credit is issued.
* **Invoice already void or canceled** (for example, voided by hand earlier)
  — nothing is owed, so nothing is credited or voided: `credit_note` is
  omitted and `invoice_voided` is `false`. This is a normal `200`, not an
  error.

The gift moves to `status: "canceled"` and its code can no longer be
redeemed. The transition from `purchased` to `canceled` is atomic and happens
exactly once: a redeemed gift cannot be canceled, and a second cancel is
refused, so the buyer credit can never issue twice. Gifts purchased before
invoice linking existed carry no `invoice_id`; cancelling one changes the
status but neither credits nor voids anything.

## Parameters

| Parameter | Type          | Required | Description                                                                              |
| --------- | ------------- | -------- | ---------------------------------------------------------------------------------------- |
| `id`      | string (UUID) | Yes      | Gift ID (path). Must belong to your tenant — see [List Gifts](/api-reference/gifts/list) |

No request body.

## Example Request

```bash theme={null}
curl -X POST https://api.recurso.dev/v1/gifts/6f1c2a9e-8b4d-4e7f-a3c5-9d0e1f2a3b4c/cancel \
  -H "Authorization: Bearer $API_KEY"
```

## Response

A paid gift returns the canceled gift and the buyer's credit note. For an
open (unpaid) gift `credit_note` is omitted and `invoice_voided` is `true`.
When the purchase invoice was already void or canceled, or the gift has no
`invoice_id`, both are absent/`false` and only `gift` carries information.

```json theme={null}
{
  "data": {
    "gift": {
      "id": "6f1c2a9e-8b4d-4e7f-a3c5-9d0e1f2a3b4c",
      "tenant_id": "2b9e1c44-7d3a-4f6b-8e21-5c0d9a7f1b33",
      "code": "GIFT-QN7XL3MTKW",
      "plan_id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
      "buyer_customer_id": "a8b9c0d1-e2f3-4a5b-6c7d-8e9f0a1b2c3d",
      "recipient_email": "priya.sharma@example.com",
      "status": "canceled",
      "redeemed_by_customer_id": null,
      "redeemed_at": null,
      "invoice_id": "d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f60",
      "duration_months": 12,
      "created_at": "2026-06-20T09:15:00Z",
      "updated_at": "2026-09-04T11:42:07Z"
    },
    "credit_note": {
      "id": "c7d8e9f0-a1b2-4c3d-8e4f-5a6b7c8d9e01",
      "tenant_id": "2b9e1c44-7d3a-4f6b-8e21-5c0d9a7f1b33",
      "customer_id": "a8b9c0d1-e2f3-4a5b-6c7d-8e9f0a1b2c3d",
      "invoice_id": "d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f60",
      "amount": 5880000,
      "balance": 5880000,
      "currency": "INR",
      "status": "issued",
      "reason": "Gift GIFT-QN7XL3MTKW canceled — purchase credited",
      "type": "adjustment",
      "refund_status": "none",
      "created_at": "2026-09-04T11:42:07Z",
      "updated_at": "2026-09-04T11:42:07Z"
    },
    "invoice_voided": false
  }
}
```

## Fields

| Field                                               | Type              | Description                                                                                                                                                                                                                                                                                       |
| --------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `gift`                                              | object            | The gift after cancellation                                                                                                                                                                                                                                                                       |
| `gift.id`                                           | string (UUID)     | Gift ID                                                                                                                                                                                                                                                                                           |
| `gift.tenant_id`                                    | string (UUID)     | Owning tenant                                                                                                                                                                                                                                                                                     |
| `gift.code`                                         | string            | Redemption code; no longer redeemable                                                                                                                                                                                                                                                             |
| `gift.plan_id`                                      | string (UUID)     | The plan the gift would have activated                                                                                                                                                                                                                                                            |
| `gift.buyer_customer_id`                            | string (UUID)     | The customer who bought the gift and receives the credit                                                                                                                                                                                                                                          |
| `gift.recipient_email`                              | string            | Intended recipient                                                                                                                                                                                                                                                                                |
| `gift.status`                                       | string            | Always `canceled` after this call. Other gift states are `purchased` and `redeemed`                                                                                                                                                                                                               |
| `gift.redeemed_by_customer_id` / `gift.redeemed_at` | string, null      | Always `null` — only unredeemed gifts can be canceled                                                                                                                                                                                                                                             |
| `gift.invoice_id`                                   | string (UUID)     | The buyer's purchase invoice the credit or void was applied against; omitted for gifts purchased before invoice linking                                                                                                                                                                           |
| `gift.duration_months`                              | integer           | Length of the gifted subscription                                                                                                                                                                                                                                                                 |
| `gift.created_at` / `gift.updated_at`               | string (RFC 3339) | Timestamps                                                                                                                                                                                                                                                                                        |
| `credit_note`                                       | object            | The spendable adjustment credit issued to the buyer when the purchase invoice was paid. Omitted otherwise. Same shape as [Get Credit Note](/api-reference/credit-notes/get): `amount` and `balance` in minor units, `status` may be `pending_approval` when credit-note approval governance is on |
| `invoice_voided`                                    | boolean           | `true` when the purchase invoice was still open and was voided instead of credited. `false` when a credit note was issued, when the invoice was already void or canceled, or when the gift has no linked invoice                                                                                  |

## Errors

| Status | Code                | When                                                                                                                                                                                                                                            | Fix                                                                                                                                                                                                                                  |
| ------ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `400`  | `validation_failed` | `id` is not a valid UUID                                                                                                                                                                                                                        | Pass the gift's UUID                                                                                                                                                                                                                 |
| `401`  | `unauthorized`      | Missing or invalid API key / session                                                                                                                                                                                                            | Send `Authorization: Bearer $API_KEY`                                                                                                                                                                                                |
| `404`  | `not_found`         | No gift with that ID in your tenant                                                                                                                                                                                                             | Check the ID against [List Gifts](/api-reference/gifts/list)                                                                                                                                                                         |
| `409`  | `conflict`          | The gift is already redeemed, or already canceled                                                                                                                                                                                               | Nothing to do — a redeemed gift stays with its recipient; a canceled gift has already been settled                                                                                                                                   |
| `424`  | `internal_error`    | The gift was canceled but issuing the buyer's credit failed                                                                                                                                                                                     | Issue a manual credit note to the buyer for the purchase amount — the cancel has taken effect and will not be retried                                                                                                                |
| `500`  | `internal_error`    | Loading the gift, flipping its status, or resolving its purchase invoice after the flip failed. The body is always the fixed message `internal error` — it does not say whether the status flip took effect; the detail is only in the API logs | Re-read the gift via [List Gifts](/api-reference/gifts/list): if it is still `purchased`, retry; if it is already `canceled`, the cancel took effect but the purchase invoice may still be open or paid — void or credit it manually |
| `503`  | `internal_error`    | Credit issuance is not wired on this deployment, so a paid gift cannot be canceled                                                                                                                                                              | Enable the credit-note service on the API                                                                                                                                                                                            |

Errors use the standard envelope — see [Errors](/api-reference/errors).


## OpenAPI

````yaml POST /v1/gifts/{id}/cancel
openapi: 3.1.0
info:
  title: Recurso API
  version: 1.0.0
  description: |
    The Recurso billing engine REST API.

    Authenticate by passing your API key as a bearer token:

        Authorization: Bearer <api_key>

    Obtain an API key by registering a tenant via `POST /auth/register`.
    All authenticated endpoints live under the `/v1` prefix. Mutating
    endpoints support idempotency via the `Idempotency-Key` header.
  license:
    name: MIT
    identifier: MIT
servers:
  - url: https://billing.example.com
    description: >-
      Example deployment — substitute the base URL of your own Recurso
      deployment.
security:
  - bearerAuth: []
tags:
  - name: System
    description: Health, version, and API metadata
  - name: Auth
    description: Tenant registration
  - name: Plans
    description: Product catalog plans
  - name: Customers
    description: Customer management
  - name: Subscriptions
    description: Subscription lifecycle
  - name: Invoices
    description: Invoices, PDFs, and Indian GST e-invoicing
  - name: Coupons
    description: Discounts
  - name: Usage
    description: Metered usage events
  - name: Credit Notes
    description: Customer credits
  - name: Quotes
    description: Quote-to-invoice lifecycle
  - name: Webhooks
    description: Webhook endpoint management and event feed
  - name: Analytics
    description: Revenue analytics
  - name: Checkout
    description: Public hosted checkout for invoices
  - name: Payments
    description: Payment order creation
  - name: Inbound Webhooks
    description: Receivers for payment-gateway callbacks (Razorpay, Stripe)
  - name: Customer Portal
    description: Customer-facing portal — magic-link auth and session-scoped data
  - name: Developer
    description: API key management
  - name: Account
    description: Tenant account settings
  - name: Finance
    description: Ledger accounts, entries, reconciliation, and revenue recognition
  - name: Settings
    description: Tax (GST) and e-invoicing (IRP) configuration
  - name: Consents
    description: Consent records for RBI-compliant recurring billing
  - name: Referrals
    description: Customer referral program
  - name: Gifts
    description: Gift subscriptions
  - name: Mandates
    description: UPI Autopay mandates
  - name: Offline Payments
    description: Virtual accounts and manually recorded payments
  - name: Organizations
    description: Multi-entity organizations grouping several tenants
  - name: Accounting
    description: QuickBooks / Xero accounting integrations
  - name: Churn
    description: Churn risk scoring and alerts
  - name: Cancel Flows
    description: Configurable retention flows shown at cancellation time
  - name: Dunning
    description: Dunning analytics and multi-channel dunning campaigns
paths:
  /v1/gifts/{id}/cancel:
    post:
      tags:
        - Gifts
      summary: Cancel an unredeemed gift (buyer gets account credit)
      description: >-
        Cancels a purchased-but-unredeemed gift. If the buyer's purchase invoice
        was PAID, a spendable adjustment credit note for the amount is issued to
        the buyer (through the normal credit-note path, so approval governance
        and ledger postings apply). If the invoice is still open, it is voided
        instead — no money arrived, nothing is credited. A redeemed gift cannot
        be canceled (409); a second cancel is refused (409) so the credit can
        never issue twice.
      operationId: cancelGift
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: The canceled gift and what happened to the buyer's money.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      gift:
                        $ref: '#/components/schemas/Gift'
                      credit_note:
                        $ref: '#/components/schemas/CreditNote'
                      invoice_voided:
                        type: boolean
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
components:
  schemas:
    Gift:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        code:
          type: string
          description: Redemption code.
        plan_id:
          type: string
          format: uuid
        buyer_customer_id:
          type: string
          format: uuid
        recipient_email:
          type: string
        status:
          type: string
          enum:
            - purchased
            - redeemed
        redeemed_by_customer_id:
          type:
            - string
            - 'null'
          format: uuid
        redeemed_at:
          type:
            - string
            - 'null'
          format: date-time
        duration_months:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CreditNote:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        customer_id:
          type: string
          format: uuid
        invoice_id:
          type:
            - string
            - 'null'
          format: uuid
        entity_id:
          type:
            - string
            - 'null'
          format: uuid
          description: >-
            Legal entity that issued the credit note (Multi-Entity Books);
            inherits the referenced invoice's entity.
        reference:
          type:
            - string
            - 'null'
        amount:
          type: integer
          format: int64
        subtotal:
          type: integer
          format: int64
          description: >-
            Taxable (net-of-tax) value of the credit, minor units. Present
            (non-zero) when the note recorded a tax breakdown at creation —
            invoice-linked credits slice the invoice's tax proportionally,
            downgrade credits carry the reversed proration tax. 0 on legacy rows
            and standalone goodwill credits (gross-only).
        tax_amount:
          type: integer
          format: int64
          description: Tax reversed by this credit, minor units.
        igst_amount:
          type: integer
          format: int64
        cgst_amount:
          type: integer
          format: int64
        sgst_amount:
          type: integer
          format: int64
        tax_type:
          type: string
          description: >-
            Tax regime of the breakdown (e.g. inter_state, intra_state); empty
            when none recorded.
        hsn_code:
          type: string
        balance:
          type: integer
          format: int64
          description: Remaining unapplied credit.
        currency:
          type: string
        status:
          type: string
          enum:
            - issued
            - used
            - void
            - pending_approval
            - rejected
            - expired
        reason:
          type: string
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When a dated adjustment credit lapses; null = never expires.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        customer:
          $ref: '#/components/schemas/Customer'
    Customer:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenant_id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        name:
          type:
            - string
            - 'null'
        phone:
          type: string
        tax_id:
          type:
            - string
            - 'null'
        billing_address:
          $ref: '#/components/schemas/BillingAddress'
        ledger_account_id:
          type: string
          format: uuid
        gstin:
          type:
            - string
            - 'null'
        tax_type:
          type: string
        place_of_supply:
          type:
            - string
            - 'null'
        tax_exempt:
          type: boolean
        tax_exemption_number:
          type: string
        tax_exemption_code:
          type: string
        tax_exemption_expires_at:
          type:
            - string
            - 'null'
          format: date
        referral_code:
          type:
            - string
            - 'null'
        risk_score:
          type: integer
          minimum: 0
          maximum: 100
        risk_factors:
          type:
            - object
            - 'null'
        card_brand:
          type: string
        card_last4:
          type: string
        card_exp_month:
          type: integer
        card_exp_year:
          type: integer
        card_token_id:
          type: string
        card_fingerprint:
          type: string
        created_at:
          type: string
          format: date-time
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          description: Structured error detail.
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Stable machine-readable error code.
              examples:
                - validation_failed
                - unauthorized
                - forbidden
                - not_found
                - conflict
                - rate_limited
                - internal_error
                - invalid_api_key
                - key_mode_mismatch
                - over_refund
                - invoice_not_paid
                - invoice_already_paid
            message:
              type: string
              description: Human-readable explanation.
    BillingAddress:
      type: object
      properties:
        line1:
          type: string
        line2:
          type: string
        postal_code:
          type: string
          description: Synonym for zip.
        city:
          type: string
        state:
          type: string
        zip:
          type: string
        country:
          type: string
  responses:
    Unauthorized:
      description: Missing or invalid credentials (API key or session cookie).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: The requested resource does not exist.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: >-
        The request conflicts with current state (e.g. duplicate email, last
        owner).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: >-
        Tenant API key obtained from `POST /auth/register` or `POST
        /v1/developer/keys`.

````