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

# API Response Schemas

> Complete reference for all Khaime API response structures

# API Response Schemas

This document provides the exact structure of API responses. All monetary values are in **cents** (smallest currency unit).

<Warning>
  **Critical: Currency Conversion**

  Some fields are returned **already converted** to the customer's target currency. Others are in the merchant's base currency. The tables below indicate which is which. **Never double-convert.**
</Warning>

***

## Products API

### GET /products

```typescript theme={null}
interface ProductsResponse {
  status: boolean;
  message: string;
  data: {
    products: Product[];
    pagination: {
      page: number;
      limit: number;
      total: number;
    };
  };
}
```

### Product Object

| Field                        | Type                     | Description                                                |
| ---------------------------- | ------------------------ | ---------------------------------------------------------- |
| `id`                         | number                   | Unique product identifier                                  |
| `title`                      | string                   | Product name                                               |
| `description`                | string                   | Product description (may contain HTML)                     |
| `price`                      | number                   | Base price in **cents** (merchant's base currency)         |
| `compare_at_price`           | number \| null           | Original price for sale items                              |
| `images`                     | string\[]                | Array of image URLs                                        |
| `has_variation`              | boolean                  | Whether product has variants                               |
| `product_type`               | string                   | `"physical_product"` \| `"digital_product"` \| `"service"` |
| `shipping_rule_destinations` | ShippingRuleDestinations | Available shipping options                                 |

### ShippingRuleDestinations Object

Returned on physical products. Use this to build shipping selectors.

```typescript theme={null}
interface ShippingRuleDestinations {
  enabled: boolean;
  options: ShippingDestination[];
}

interface ShippingDestination {
  rule_id: number;
  rule_name: string;
  destination_type: "nationwide" | "specific_states" | "specific_locations" | "international";
  destination_country_code: string | null;
  destination_states: string[];
  destination_locations: string[];
  destination_countries: Array<{ code: string; name: string }>;
  international_any_country: boolean;
}
```

***

## Cart Validation API

### POST /cart/validate

Validates cart contents and calculates shipping based on delivery address.

### Request Body

```typescript theme={null}
interface CartValidationRequest {
  cart: CartItem[];
  currency: string;              // e.g., "USD", "NGN"
  discount_code?: string;
  delivery_details: DeliveryDetails;
}
```

### CartItem Object

| Field                  | Type    | Required                      | Description                                       |
| ---------------------- | ------- | ----------------------------- | ------------------------------------------------- |
| `product_id`           | number  | Yes                           | Must be number, not string                        |
| `quantity`             | number  | Yes                           | Item quantity                                     |
| `price`                | number  | Yes                           | Unit price in cents                               |
| `has_variation`        | boolean | Yes                           | Whether item has variant                          |
| `product_image`        | string  | Yes                           | First product image URL                           |
| `product_variant_data` | string  | Yes                           | Variant name OR `"none"` for non-variant products |
| `main_variant`         | string  | Only if `has_variation: true` | Variant name                                      |
| `least_sub_variant_id` | string  | Only if `has_variation: true` | Variant ID                                        |

<Error>
  **Common Validation Errors**

  | Error                                     | Cause                                       | Fix                                    |
  | ----------------------------------------- | ------------------------------------------- | -------------------------------------- |
  | `main_variant is not allowed to be empty` | Sent `main_variant` for non-variant product | Only include for `has_variation: true` |
  | `product_variant_data is required`        | Missing field                               | Use `"none"` for non-variant products  |
  | `least_sub_variant_id is not allowed`     | Sent variant ID for non-variant product     | Only include for `has_variation: true` |
</Error>

### DeliveryDetails Object

```typescript theme={null}
interface DeliveryDetails {
  save_for_future_checkout: boolean;
  store_pick_up: boolean;
  national_delivery: boolean;
  international_delivery: boolean;
  address: [{
    name: string;
    email: string;           // Must be lowercase
    address: string;
    city: string;
    location?: string;       // For specific_locations shipping rules
    state: string;
    zip_code: string;
    country: string;         // Full country name
    country_code: string;    // ISO 2-letter code
    phone_number?: string;
  }];
}
```

<Tip>
  **The `location` field**

  For `specific_locations` shipping rules, pass the merchant-defined area in `location`. This is separate from `city`:

  * `city`: Customer's actual city (for receipts, orders)
  * `location`: Merchant-defined area for shipping rule matching (e.g., "Ikeja", "Dallas")
</Tip>

### Response

```typescript theme={null}
interface CartValidationResponse {
  status: boolean;
  message: string;
  data: {
    cart_unique_id: string;
    total_sum: number;
    shipping_fee: number;
    total_sum_with_shipping_fee: number;
    cart_details: CartDetailItem[];
    pricing_summary: PricingSummary;
  };
}
```

### Response Fields (Money)

| Field                         | Type   | Already Converted? | Description                   |
| ----------------------------- | ------ | :----------------: | ----------------------------- |
| `total_sum`                   | number |       **YES**      | Cart subtotal (products only) |
| `shipping_fee`                | number |       **YES**      | Shipping cost                 |
| `total_sum_with_shipping_fee` | number |       **YES**      | Order total                   |

<Error>
  **DO NOT** apply frontend exchange rate conversion to these fields. They are already in the customer's target currency.

  ```typescript theme={null}
  // WRONG - causes double conversion
  const displayShipping = shippingFee * exchangeRate;

  // CORRECT - already converted
  const displayShipping = shippingFee;
  formatPrice(shippingFee, { skipConversion: true });
  ```
</Error>

### CartDetailItem Object

Line items with accurate backend-calculated prices.

| Field           | Type           | Already Converted? | Description                   |
| --------------- | -------------- | :----------------: | ----------------------------- |
| `product_title` | string         |          -         | Product name                  |
| `product_price` | number         |       **YES**      | Unit price                    |
| `quantity`      | number         |          -         | Quantity                      |
| `total_amount`  | number         |       **YES**      | Line total (price × quantity) |
| `variant_label` | string \| null |          -         | Variant name if applicable    |

### PricingSummary Object

```typescript theme={null}
interface PricingSummary {
  cart_subtotal: number;
  shipping_cost: number;
  cart_final_total: number;
  shipping_details: ShippingDetails;
}
```

### ShippingDetails Object

```typescript theme={null}
interface ShippingDetails {
  source: "shipping_rules" | "easypost" | "uber_direct" | "standard";
  can_switch_rates: boolean;
  matched_rules: MatchedShippingRule[];
  available_destinations: ShippingDestination[];
}
```

### MatchedShippingRule Object

Rules that matched the customer's address, with calculated fees.

| Field              | Type   | Description                                                                          |
| ------------------ | ------ | ------------------------------------------------------------------------------------ |
| `rule_id`          | number | Unique rule identifier                                                               |
| `rule_name`        | string | Display name (e.g., "Lagos Rate")                                                    |
| `destination_type` | string | `"nationwide"` \| `"specific_states"` \| `"specific_locations"` \| `"international"` |
| `charge_type`      | string | `"flat_rate"` \| `"order_amount_based"` \| `"product_based"` \| `"free_shipping"`    |
| `calculated_fee`   | number | Shipping fee in cents (**already converted**)                                        |

### Shipping Rule Priority

When multiple rules could match, the backend selects by priority:

| Priority | Type                 | Description                     |
| :------: | -------------------- | ------------------------------- |
|     1    | `specific_locations` | Most specific - city/area level |
|     2    | `specific_states`    | State level                     |
|     3    | `nationwide`         | Domestic fallback               |
|     4    | `international`      | International fallback          |

***

## Payment Intent API

### POST /payment/intent

Creates a payment intent for processing payment.

### Request Body

```typescript theme={null}
interface PaymentIntentRequest {
  cart_unique_id: string;        // From cart validation
  cart: CartItem[];
  email: string;
  first_name: string;
  last_name: string;
  currency: string;
  payment_type: "one" | "subscription";
  payment_by: "customer";
  product_type: string;
  product_title: string;
  is_coupon_used: boolean;
  is_second_time_payment: boolean;
  order_total_charge: number;    // Subtotal in cents
  delivery_details: DeliveryDetails;
  // Note: Shipping selection is done via `location` field in delivery_details during cart validation
}
```

### Response

```typescript theme={null}
interface PaymentIntentResponse {
  status: boolean;
  message: string;
  data: {
    secret: string;              // Stripe client secret
    publishable_key: string;     // Stripe publishable key
    amount_to_pay: number;       // Final amount in cents
    currency: string;
    shipping_fee?: number;
    data?: {
      stripe_account_id?: string;
    };
  };
}
```

### Response Fields (Money)

| Field           | Type   | Already Converted? | Description            |
| --------------- | ------ | :----------------: | ---------------------- |
| `amount_to_pay` | number |       **YES**      | Final charge amount    |
| `shipping_fee`  | number |       **YES**      | Shipping (if returned) |

***

## Pricing Calculation API

### GET /pricing/calculate

Converts amounts to customer's currency with live exchange rates.

### Query Parameters

| Parameter          | Type   | Required | Description                                             |
| ------------------ | ------ | -------- | ------------------------------------------------------- |
| `amount`           | number | Yes      | Amount in cents (base currency)                         |
| `target_currency`  | string | No       | Target currency code. If omitted, auto-detects from IP. |
| `customer_country` | string | No       | ISO country code for regional pricing                   |

### Response

```typescript theme={null}
interface PricingResponse {
  status: boolean;
  data: {
    customer: {
      currency: { code: string; symbol: string };
      location: { country: string; country_code: string };
    };
    pricing: {
      original: { amount: number; currency: string; formatted: string };
      local: { amount: number; currency: string; formatted: string };
      conversion: { rate: number; applied: boolean };
    };
  };
}
```

***

## SDK Methods

### Khaime.confirmPayment()

Renders the payment form and processes payment.

```typescript theme={null}
await Khaime.confirmPayment({
  // Required
  secret: string;              // From payment intent
  publishable_key: string;     // From payment intent

  // Recommended
  stripe_account_id?: string;  // For Connect accounts
  amount?: number;             // Display amount in cents
  currency?: string;           // Currency code

  // Display mode
  display: "modal" | "inline";
  container?: HTMLElement;     // Required for inline mode

  // Callbacks
  onReady?: () => void;
  onSuccess?: (result: { paymentIntent: unknown }) => void;
  onError?: (error: Error) => void;
});
```

<Warning>
  **React Timing Issue**

  The container element must exist in the DOM before calling `confirmPayment`. Use `useEffect` to ensure the container is rendered:

  ```typescript theme={null}
  useEffect(() => {
    if (!showPaymentForm || !credentials || !containerRef.current) return;

    Khaime.confirmPayment({
      container: containerRef.current,  // Pass element, not ID
      // ...
    });
  }, [showPaymentForm, credentials]);
  ```
</Warning>

***

## Error Responses

All endpoints return errors in this format:

```typescript theme={null}
interface ErrorResponse {
  status: false;
  message: string;
  errors?: Record<string, string[]>;
}
```

### Common HTTP Status Codes

| Code | Meaning                                   |
| ---- | ----------------------------------------- |
| 400  | Validation error (check `errors` field)   |
| 401  | Invalid or missing API key                |
| 404  | Resource not found                        |
| 422  | Business logic error (e.g., out of stock) |
| 500  | Server error                              |
