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

# Flex Refund API Guide

**Important IDs to store:**

* `checkout_session_id` - Extract from redirect URL (e.g., `fcs_01kbjcmbt5mhsmaggfqc1a4rns`)
* This ID is required for creating refunds

You can also retrieve the checkout session to get additional details:

```bash theme={null}
GET /v1/checkout/sessions/{checkout_session_id}
```

Response includes:

* `payment_intent_id` - Flex's internal payment ID
* `latest_charge` - Charge ID
* `status` - Should be `complete` for refunds

***

## Primary Refund Endpoint

### POST /v1/checkout/sessions/{checkout_session_id}/refund

This is the **main refund endpoint** for processing refunds on completed checkout sessions.

**Base URL:** `https://api.withflex.com` (or your custom domain)

**Authentication:**

```text theme={null}
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

***

## Request Format

### Option 1: Line Item-Based Refund (Granular Control)

Refund specific products/prices with custom amounts:

```json theme={null}
{
  "checkout_session": {
    "line_items": [
      {
        "product": "fprod_01k3m6j3zqcvy6pbj1a65sszpw",
        "amount_to_refund": 2000
      },
      {
        "product": "fprod_01k3m6r0qj2bpc25q6h56r3jar",
        "amount_to_refund": 1500
      }
    ],
    "amount_tax": 350,
    "amount_shipping": 500,
    "amount_discount": 200,
    "refund_metadata": {
      "OrderNo": "ORDER123",
      "Source": "Customer_Request",
      "Reason": "Damaged_Product"
    }
  }
}
```

**Alternative: Use `price` instead of `product`**

```json theme={null}
{
  "checkout_session": {
    "line_items": [
      {
        "price": "fprice_01k3m6j3zqcvy6pbj1a65sszpw",
        "amount_to_refund": 2000
      }
    ],
    "refund_metadata": {
      "reason": "customer_request"
    }
  }
}
```

***

### Option 2: Simple Amount-Based Refund

Refund a specific total amount without line item breakdown:

```json theme={null}
{
  "checkout_session": {
    "amount": 5000,
    "refund_metadata": {
      "reason": "customer_not_satisfied",
      "order_id": "12345"
    }
  }
}
```

***

### Option 3: Full Refund

Refund the entire checkout session amount:

```json theme={null}
{
  "checkout_session": {
    "refund_metadata": {
      "reason": "order_cancelled"
    }
  }
}
```

Or simply:

```json theme={null}
{
  "checkout_session": {}
}
```

***

## Request Parameters

| Field                           | Type    | Required      | Description                                                  |
| ------------------------------- | ------- | ------------- | ------------------------------------------------------------ |
| `line_items`                    | array   | No            | Specific line items to refund with amounts                   |
| `line_items[].product`          | string  | One of        | Product ID to refund (e.g., `fprod_xxx`)                     |
| `line_items[].price`            | string  | product/price | Price ID to refund (e.g., `fprice_xxx`)                      |
| `line_items[].amount_to_refund` | integer | No            | Amount in cents for this item (omit for full item refund)    |
| `amount`                        | integer | No            | Total amount to refund in cents (alternative to line\_items) |
| `amount_tax`                    | integer | No            | Tax amount to refund in cents                                |
| `amount_shipping`               | integer | No            | Shipping amount to refund in cents                           |
| `amount_discount`               | integer | No            | Discount amount to refund in cents                           |
| `refund_metadata`               | object  | No            | Key-value pairs for tracking (order ID, reason, etc.)        |

***

## Success Response

**Status Code:** `200 OK`

```json theme={null}
{
  "checkout_session": {
    "checkout_session_id": "fcs_01kbjcmbt5mhsmaggfqc1a4rns",
    "status": "complete",
    "amount_total": 10000,
    "amount_subtotal": 9000,
    "currency": "usd",
    "customer": {
      "customer_id": "fcus_xxx",
      "email": "customer@example.com",
      "name": "John Doe"
    },
    "payment_intent": {
      "payment_intent_id": "fpi_xxx",
      "amount": 10000,
      "amount_received": 10000,
      "status": "succeeded"
    },
    "line_items": [...],
    "refunds": [
      {
        "refund_id": "fref_01kbjdxyz123",
        "payment_intent_id": "fpi_xxx",
        "amount": 3500,
        "status": "pending",
        "reason": null,
        "created_at": "2024-12-04T10:30:00Z",
        "test_mode": false,
        "metadata": {
          "OrderNo": "ORDER123",
          "Source": "Customer_Request"
        },
        "reference_id": null,
        "reference_type": "acquirer_reference_number",
        "reference_status": "pending",
        "items": [
          {
            "amount_refunded": 3500,
            "payment_intent": "fpi_xxx",
            "reference_id": null,
            "reference_type": "acquirer_reference_number",
            "reference_status": "pending"
          }
        ]
      }
    ]
  }
}
```

**Key Response Fields:**

* `refunds[].refund_id` - Store this for tracking refund status
* `refunds[].status` - Current refund status (`pending`, `succeeded`, `failed`)
* `refunds[].reference_id` - Acquirer Reference Number (ARN) for bank tracking (populated later)
* `refunds[].reference_status` - Status of reference number (`pending`, `available`, `unavailable`)

***

## Error Responses

### Checkout Session Not Complete (422 Unprocessable Entity)

```json theme={null}
{
  "error": {
    "type": "validation_error",
    "message": "Checkout session must be complete",
    "errors": [
      {
        "loc": ["status"],
        "msg": "Checkout session must be complete",
        "type": "invalid_request_error"
      }
    ]
  }
}
```

**Cause:** Checkout session status is not `complete`

***

### Checkout Session Not Found (404 Not Found)

```json theme={null}
{
  "error": {
    "type": "not_found",
    "message": "checkout session does not exist"
  }
}
```

**Cause:** Invalid `checkout_session_id` or doesn't belong to your account

***

### Unauthorized (401 Unauthorized)

```json theme={null}
{
  "error": {
    "type": "unauthorized",
    "message": "Invalid API key"
  }
}
```

**Cause:** Missing or invalid `Authorization` header

***

## Refund Statuses

| Status            | Description                                                     |
| ----------------- | --------------------------------------------------------------- |
| `pending`         | Refund initiated, processing in progress                        |
| `requires_action` | Additional action required (rare)                               |
| `succeeded`       | Refund completed, funds returned to customer                    |
| `failed`          | Refund failed (usually insufficient funds in connected account) |
| `canceled`        | Refund was canceled                                             |

**Typical Timeline:**

* Refund status updates to `succeeded` within minutes to hours
* Funds appear in customer's account within 5-10 business days (varies by bank)

***

## Reference Numbers for Tracking

Flex provides **reference numbers** that can be used to track refunds with banks and payment networks.

### Reference Types

| Type                         | Description                                             |
| ---------------------------- | ------------------------------------------------------- |
| `acquirer_reference_number`  | ARN - Most common, used for tracking with issuing banks |
| `system_trace_audit_number`  | Alternative tracking number for payment networks        |
| `retrieval_reference_number` | Used for dispute resolution and chargebacks             |

### Reference Status

| Status        | Description                                              |
| ------------- | -------------------------------------------------------- |
| `pending`     | Reference number not yet available from payment network  |
| `available`   | Reference number ready, can be provided to customer/bank |
| `unavailable` | Payment network doesn't provide this reference type      |

**Note:** Reference IDs typically become available within hours after the refund is created.

**Learn More:** [Stripe Reference Numbers Guide](https://support.stripe.com/questions/how-to-trace-a-refund-using-reference-numbers)

***

## Complete Workflow Examples

### Example 1: Full Refund

**Step 1: Customer completes payment**

You receive redirect after successful payment:

```text theme={null}
https://yoursite.com/success?session_id=fcs_01kbjcmbt5mhsmaggfqc1a4rns
```

**Step 2: Customer requests full refund**

```bash theme={null}
curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_01kbjcmbt5mhsmaggfqc1a4rns/refund \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "checkout_session": {
      "refund_metadata": {
        "reason": "customer_cancelled_order",
        "order_id": "ORD-12345",
        "requested_by": "customer_service"
      }
    }
  }'
```

**Step 3: Response**

```json theme={null}
{
  "checkout_session": {
    "checkout_session_id": "fcs_01kbjcmbt5mhsmaggfqc1a4rns",
    "status": "complete",
    "amount_total": 15000,
    "refunds": [
      {
        "refund_id": "fref_01kbjdxyz123",
        "amount": 15000,
        "status": "pending",
        "created_at": "2024-12-04T10:30:00Z",
        "metadata": {
          "reason": "customer_cancelled_order",
          "order_id": "ORD-12345"
        }
      }
    ]
  }
}
```

**Step 4: Track refund status (optional)**

```bash theme={null}
curl -X GET https://api.withflex.com/v1/refunds/fref_01kbjdxyz123 \
  -H "Authorization: Bearer sk_live_xxx"
```

***

### Example 2: Partial Refund by Line Items

**Scenario:** Customer returns 1 of 3 items purchased

**Request:**

```bash theme={null}
curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_01kbjcmbt5mhsmaggfqc1a4rns/refund \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "checkout_session": {
      "line_items": [
        {
          "product": "fprod_yoga_mat",
          "amount_to_refund": 3500
        }
      ],
      "amount_tax": 280,
      "amount_shipping": 0,
      "refund_metadata": {
        "reason": "item_returned",
        "rma_number": "RMA-2024-1234",
        "returned_items": "Yoga Mat (Blue)"
      }
    }
  }'
```

**Explanation:**

* Refunding only the yoga mat: \$35.00
* Including proportional tax: \$2.80
* Not refunding shipping (customer kept other items)
* Total refund: \$37.80

***

### Example 3: Partial Refund by Amount

**Scenario:** Price adjustment without item-level detail

**Request:**

```bash theme={null}
curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_01kbjcmbt5mhsmaggfqc1a4rns/refund \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "checkout_session": {
      "amount": 2000,
      "refund_metadata": {
        "reason": "price_match_guarantee",
        "original_amount": 15000,
        "adjusted_amount": 13000,
        "competitor": "CompetitorStore"
      }
    }
  }'
```

**Explanation:**

* Refunding \$20.00 difference due to price match
* No line item breakdown needed
* Metadata tracks the reason

***

### Example 4: Multiple Partial Refunds

You can create multiple refunds for the same checkout session until the full amount is refunded.

**First refund:** \$50

```bash theme={null}
curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_xxx/refund \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"checkout_session": {"amount": 5000}}'
```

**Second refund:** \$30

```bash theme={null}
curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_xxx/refund \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"checkout_session": {"amount": 3000}}'
```

**Important:** Track the total refunded amount to avoid over-refunding.

***

## Secondary Refund API

### Tracking and Listing Refunds

After creating a refund via the checkout session endpoint, use the secondary refund API to track status:

#### GET /v1/refunds/{refund_id}

```bash theme={null}
curl -X GET https://api.withflex.com/v1/refunds/fref_01kbjdxyz123 \
  -H "Authorization: Bearer sk_live_xxx"
```

**Response:**

```json theme={null}
{
  "refund": {
    "refund_id": "fref_01kbjdxyz123",
    "payment_intent_id": "fpi_xxx",
    "amount": 5000,
    "status": "succeeded",
    "reason": null,
    "created_at": "2024-12-04T10:30:00Z",
    "test_mode": false,
    "metadata": {
      "order_id": "12345"
    },
    "reference_id": "ARN8493274932",
    "reference_type": "acquirer_reference_number",
    "reference_status": "available",
    "items": [...]
  }
}
```

***

#### GET /v1/refunds (List Refunds)

**Query Parameters:**

* `payment_intent` - Filter by payment intent ID
* `checkout_session` - Filter by checkout session ID
* `status` - Filter by status

**Examples:**

```bash theme={null}
# All refunds
curl -X GET https://api.withflex.com/v1/refunds \
  -H "Authorization: Bearer sk_live_xxx"

# Refunds for specific checkout session
curl -X GET https://api.withflex.com/v1/refunds?checkout_session=fcs_01kbjcmbt5mhsmaggfqc1a4rns \
  -H "Authorization: Bearer sk_live_xxx"

# Succeeded refunds only
curl -X GET https://api.withflex.com/v1/refunds?status=succeeded \
  -H "Authorization: Bearer sk_live_xxx"
```

**Response:**

```json theme={null}
{
  "refunds": [
    {
      "refund_id": "fref_01kbjdxyz123",
      "payment_intent_id": "fpi_xxx",
      "amount": 5000,
      "status": "succeeded",
      "created_at": "2024-12-04T10:30:00Z",
      ...
    },
    {
      "refund_id": "fref_01kbjdabc456",
      "payment_intent_id": "fpi_yyy",
      "amount": 3000,
      "status": "pending",
      "created_at": "2024-12-03T15:20:00Z",
      ...
    }
  ]
}
```

***

#### PATCH /v1/refunds/{refund_id} (Update Refund Metadata)

Update refund metadata or reason (does not change amount or status):

```bash theme={null}
curl -X PATCH https://api.withflex.com/v1/refunds/fref_01kbjdxyz123 \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "refund": {
      "reason": "fraudulent",
      "metadata": {
        "updated_by": "admin",
        "investigation_id": "INV-2024-001"
      }
    }
  }'
```

***

## Implementation Details

### Automatic Handling by Flex

Flex automatically handles:

✅ **Reverse Transfer** - Funds are clawed back from the connected account (partner) ✅ **Application Fee Retention** - Flex keeps the platform fee (not refunded) ✅ **Webhook Processing** - Status updates via Stripe webhooks ✅ **Charge Tracking** - Records which charges were refunded in `refund_charge` table ✅ **Reference Number Retrieval** - ARN and other tracking numbers populated when available

### Partial Refund Behavior

* Can refund any amount up to the original charge amount
* Multiple partial refunds allowed until full amount refunded
* Track `amount_received` vs total refunded to calculate remaining refundable amount

### Tax, Shipping, and Discount Refunds

When refunding line items, you can specify component amounts:

```json theme={null}
{
  "checkout_session": {
    "line_items": [
      {
        "product": "fprod_xxx",
        "amount_to_refund": 5000
      }
    ],
    "amount_tax": 400,        // Proportional tax for refunded items
    "amount_shipping": 500,    // Shipping refund
    "amount_discount": 250     // Discount adjustment
  }
}
```

**Best Practice:** Calculate proportional tax when refunding partial line items.

***

## Best Practices

### 1. Store Checkout Session ID

Always save the `checkout_session_id` after payment completion:

```javascript theme={null}
// After payment redirect
const urlParams = new URLSearchParams(window.location.search);
const sessionId = urlParams.get('session_id');

// Store in your database
await db.orders.update({
  orderId: orderId,
  flexCheckoutSessionId: sessionId
});
```

***

### 2. Verify Checkout Session Status

Before attempting refund, confirm the session is complete:

```javascript theme={null}
const session = await fetch(
  `https://api.withflex.com/v1/checkout/sessions/${sessionId}`,
  {
    headers: {
      'Authorization': `Bearer ${apiKey}`
    }
  }
).then(r => r.json());

if (session.checkout_session.status !== 'complete') {
  throw new Error('Cannot refund incomplete checkout session');
}
```

***

### 3. Use Metadata for Tracking

Always include metadata to track refund context:

```json theme={null}
{
  "checkout_session": {
    "amount": 5000,
    "refund_metadata": {
      "order_id": "ORD-12345",
      "reason": "customer_request",
      "requested_by": "customer_service_agent_42",
      "ticket_id": "TICKET-789",
      "timestamp": "2024-12-04T10:30:00Z"
    }
  }
}
```

***

### 4. Handle Refund Webhooks

Subscribe to Flex webhooks for automatic refund status updates:

**Event Types:**

* `refund.created` - Refund initiated
* `refund.succeeded` - Refund completed successfully
* `refund.failed` - Refund failed

**Webhook Payload Example:**

```json theme={null}
{
  "type": "refund.succeeded",
  "data": {
    "object": {
      "refund_id": "fref_xxx",
      "payment_intent_id": "fpi_xxx",
      "amount": 5000,
      "status": "succeeded",
      "reference_id": "ARN123456789"
    }
  }
}
```

***

### 5. Track Remaining Refundable Amount

For multiple partial refunds, track the remaining amount:

```javascript theme={null}
const session = await getCheckoutSession(sessionId);
const totalCharged = session.amount_total;
const totalRefunded = session.refunds.reduce((sum, r) => sum + r.amount, 0);
const remainingRefundable = totalCharged - totalRefunded;

console.log(`Can still refund: $${remainingRefundable / 100}`);
```

***

### 6. Save Reference Numbers

Store ARNs for customer support inquiries:

```javascript theme={null}
const refund = await getRefund(refundId);

if (refund.reference_status === 'available') {
  await db.refunds.update({
    refundId: refundId,
    arn: refund.reference_id,
    referenceType: refund.reference_type
  });

  // Provide to customer for bank tracking
  console.log(`ARN: ${refund.reference_id}`);
}
```

***

### 7. Handle Errors Gracefully

```javascript theme={null}
try {
  const response = await fetch(refundUrl, {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${apiKey}` },
    body: JSON.stringify(refundRequest)
  });

  if (!response.ok) {
    const error = await response.json();

    if (error.error.type === 'validation_error') {
      console.error('Validation error:', error.error.message);
      // Handle validation errors
    } else if (response.status === 404) {
      console.error('Checkout session not found');
      // Handle not found
    } else {
      console.error('Unexpected error:', error);
      // Handle other errors
    }
  }

  const result = await response.json();
  console.log('Refund created:', result.checkout_session.refunds[0].refund_id);

} catch (error) {
  console.error('Network error:', error);
  // Handle network errors
}
```

***

## Testing

### Test Mode

Use test API keys (starting with `sk_test_`) to test refunds without affecting real money:

```bash theme={null}
curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_test_xxx/refund \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{"checkout_session": {}}'
```

**Test Mode Behavior:**

* Refunds complete instantly with `status: "succeeded"`
* No actual money movement
* Reference IDs still generated for testing
* Full webhook flow works

### Test Credentials

For testing payments → refunds flow:

**Email:** `miguel@homefit.com` **Password:** `password`

Create test API keys at: `http://localhost:3001/partners/dashboard/developers`

***

## Troubleshooting

### Problem: "Checkout session must be complete"

**Cause:** Trying to refund a checkout session that hasn't been paid or is in progress

**Solution:**

1. Check session status: `GET /v1/checkout/sessions/{id}`
2. Ensure `status === "complete"`
3. Wait for payment to finish if still processing

***

### Problem: "checkout session does not exist"

**Cause:** Invalid checkout session ID or doesn't belong to your account

**Solution:**

1. Verify the `checkout_session_id` is correct
2. Ensure you're using the right API key (test vs. production)
3. Check that the session belongs to your partner account

***

### Problem: Refund shows "pending" for too long

**Cause:** Stripe processing delay or connected account issues

**Solution:**

1. Wait up to 24 hours for normal processing
2. Check Stripe dashboard for errors
3. Verify connected account has sufficient balance
4. Contact support if still pending after 24 hours

***

### Problem: "Failed to create refund"

**Cause:** Stripe API error (usually insufficient funds in connected account)

**Solution:**

1. Check connected account balance in Stripe dashboard
2. Ensure partner account has funds to cover refund
3. Review Stripe logs for specific error details
4. Contact Flex support with `checkout_session_id`

***

### Problem: Reference ID not available

**Cause:** Payment network hasn't provided ARN yet

**Solution:**

1. Reference IDs typically appear within hours
2. Check `reference_status` - if `pending`, wait
3. If `unavailable`, payment network doesn't provide this type
4. Poll `/v1/refunds/{refund_id}` periodically for updates

***

## API Endpoint Summary

| Endpoint                            | Method | Purpose                                          |
| ----------------------------------- | ------ | ------------------------------------------------ |
| `/v1/checkout/sessions/{id}/refund` | POST   | **Primary** - Create refund for checkout session |
| `/v1/refunds`                       | GET    | List all refunds with filters                    |
| `/v1/refunds/{refund_id}`           | GET    | Get single refund details                        |
| `/v1/refunds/{refund_id}`           | PATCH  | Update refund metadata                           |
| `/v1/checkout/sessions/{id}`        | GET    | Get checkout session details                     |

***

## Common Use Cases

### Use Case 1: Customer Service Full Refund

```javascript theme={null}
async function processFullRefund(checkoutSessionId, reason) {
  const response = await fetch(
    `https://api.withflex.com/v1/checkout/sessions/${checkoutSessionId}/refund`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.FLEX_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        checkout_session: {
          refund_metadata: {
            reason: reason,
            processed_by: 'customer_service',
            timestamp: new Date().toISOString()
          }
        }
      })
    }
  );

  const result = await response.json();
  const refund = result.checkout_session.refunds[0];

  console.log(`Refund created: ${refund.refund_id}`);
  return refund;
}
```

***

### Use Case 2: Return Processing with Line Items

```javascript theme={null}
async function processReturn(checkoutSessionId, returnedItems) {
  // returnedItems = [{ productId, amount }, ...]

  const lineItems = returnedItems.map(item => ({
    product: item.productId,
    amount_to_refund: item.amount
  }));

  const response = await fetch(
    `https://api.withflex.com/v1/checkout/sessions/${checkoutSessionId}/refund`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.FLEX_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        checkout_session: {
          line_items: lineItems,
          amount_tax: calculateProportionalTax(returnedItems),
          refund_metadata: {
            reason: 'items_returned',
            rma_number: generateRmaNumber()
          }
        }
      })
    }
  );

  return await response.json();
}
```

***

### Use Case 3: Price Adjustment

```javascript theme={null}
async function applyPriceAdjustment(checkoutSessionId, adjustmentAmount, reason) {
  const response = await fetch(
    `https://api.withflex.com/v1/checkout/sessions/${checkoutSessionId}/refund`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.FLEX_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        checkout_session: {
          amount: adjustmentAmount,
          refund_metadata: {
            reason: 'price_adjustment',
            adjustment_type: reason,
            original_ticket: 'TICKET-123'
          }
        }
      })
    }
  );

  return await response.json();
}
```

***

## Additional Resources

* **Flex API Documentation:** Contact your Flex account manager
* **Stripe Refund Guide:** [https://stripe.com/docs/refunds](https://stripe.com/docs/refunds)
* **Reference Numbers:** [https://support.stripe.com/questions/how-to-trace-a-refund-using-reference-numbers](https://support.stripe.com/questions/how-to-trace-a-refund-using-reference-numbers)
* **Webhooks Setup:** Contact Flex support for webhook configuration

***

## Support

For issues or questions about refunds:

1. Check this guide for common solutions
2. Review Stripe dashboard for payment details
3. Contact Flex support with:
   * `checkout_session_id`
   * `refund_id` (if refund was created)
   * Error messages
   * Timestamp of the issue

***
