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

# Recurring Mode

> Set up subscription and membership payments with automated billing schedules

Recurring mode lets you create a checkout session that, on successful payment, automatically enrolls the customer in a billing plan. The customer fills in their card details once; OzuraPay handles every subsequent charge on the schedule you define.

<Note>
  **How it works under the hood:** When the customer pays, Checkout calls OzuraPay API's `createRecurringPlan` endpoint rather than the standard `cardSale` endpoint. The customer's card is tokenized by the vault and never stored by your server.
</Note>

## Enable Recurring Mode

Set `checkoutMode` to `"recurring"` and include a `recurringConfig` object:

```json theme={null}
{
  "merchantId": "your_merchant_id",
  "merchantName": "My App",
  "amount": "29.99",
  "currency": "USD",
  "checkoutMode": "recurring",
  "recurringConfig": {
    "planName": "Monthly Pro",
    "interval": "monthly",
    "startDate": "2026-06-01"
  },
  "successUrl": "https://yoursite.com/welcome",
  "cancelUrl": "https://yoursite.com/pricing",
  "errorUrl": "https://yoursite.com/error"
}
```

<Warning>
  `recurringConfig` is **required** when `checkoutMode` is `"recurring"`. Omitting it returns a `400` validation error.
</Warning>

## `recurringConfig` Fields

### Required

| Field       | Type   | Constraints                                    | Description                                                                                                                                                                               |
| :---------- | :----- | :--------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `planName`  | string | Max 50 chars                                   | Label for this subscription plan, shown to the customer in the checkout UI                                                                                                                |
| `interval`  | string | `"daily"`, `"weekly"`, `"monthly"`, `"yearly"` | Billing frequency keyword. Combine with `intervalCount` for non-standard periods (e.g. every 3 months)                                                                                    |
| `startDate` | string | `YYYY-MM-DD`                                   | First billing date shown to the customer for legal disclosure. **Not forwarded to the payment processor** ? the processor starts billing based on when the plan is created, not this date |

### Optional

All fields below are optional. When omitted, they are not stored on the session and not sent to OzuraPay API ? any defaults listed are applied by the **OzuraPay API**, not by Checkout.

<Note>
  **Type note:** `intervalCount`, `initialCycles`, `maxCycles`, and `maxAttempts` must be JSON **numbers** (not strings). `initialAmount` and `setupFee` must be JSON **strings** (quoted decimals like `"9.99"`). This matches how OzuraPay API expects them.
</Note>

| Field                        | Type                                | OzuraPay API default | Description                                                                                                                                                                                                                                          |
| :--------------------------- | :---------------------------------- | :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `planDescription`            | string (max 100 chars)              | ?                    | Short description shown below the plan name in the checkout UI                                                                                                                                                                                       |
| `intervalCount`              | integer ? 1                         | `1`                  | **Billing period multiplier.** Combined with `interval` to define the billing period. `interval: "monthly"` + `intervalCount: 3` = billed every 3 months (quarterly). `interval: "weekly"` + `intervalCount: 2` = every 2 weeks                      |
| `endDate`                    | string (`YYYY-MM-DD`)               | indefinite           | Date after which the processor stops billing automatically. Omit for an ongoing plan                                                                                                                                                                 |
| `maxCycles`                  | integer ? 1                         | indefinite           | Maximum number of billing cycles before the plan completes automatically. Omit for an ongoing plan. Can be set alongside `endDate` ? whichever limit is hit first stops billing                                                                      |
| `maxAttempts`                | integer ? 0                         | `3`                  | How many times OzuraPay API retries a failed charge before marking the attempt failed. Pass `0` to disable retries                                                                                                                                   |
| `setupFee`                   | **string** (decimal, e.g. `"9.99"`) | ?                    | One-time fee charged on the first billing cycle only, in addition to the regular `amount`. Does not repeat                                                                                                                                           |
| `initialAmount`              | **string** (decimal, e.g. `"9.99"`) | ?                    | Override the billing `amount` for the first N cycles (introductory / trial pricing). **Must always be provided together with `initialCycles`** ? sending one without the other returns a `400` validation error. See [Trial Pricing](#trial-pricing) |
| `initialCycles`              | integer ? 1                         | ?                    | Number of cycles at the `initialAmount` before switching to the standard session `amount`. **Must always be provided together with `initialAmount`** ? sending one without the other returns a `400` validation error                                |
| `merchantRecurringReference` | string (max 50 chars)               | ?                    | Your own subscription or customer ID forwarded to OzuraPay API. Use this to correlate the plan back to your records ? it is the recommended way to look up a plan ID after enrollment                                                                |

## `amount` Field

The `amount` on the session is the **standard recurring charge** ? what the customer is billed on each normal billing cycle.

```json theme={null}
{
  "amount": "29.99",
  "checkoutMode": "recurring",
  "recurringConfig": { ... }
}
```

**What gets charged on the first cycle?**

Checkout forwards `amount`, `setupFee`, `initialAmount`, and `initialCycles` as separate fields to OzuraPay API. OzuraPay API combines them and calculates the actual charge ? Checkout does not compute a combined total locally.

| Configuration                     | Effective first cycle charge                          | Subsequent cycles                       |
| :-------------------------------- | :---------------------------------------------------- | :-------------------------------------- |
| No trial fields                   | `amount`                                              | `amount`                                |
| `setupFee` only                   | `setupFee` + `amount` (OzuraPay API adds them)        | `amount`                                |
| `initialAmount` + `initialCycles` | `initialAmount` (repeated for N cycles)               | `amount`                                |
| All three                         | `setupFee` + `initialAmount` (OzuraPay API adds them) | `amount` (after `initialCycles` expire) |

<Note>
  There is no field called `intervalAmount`. The field that overrides the billing amount for the first N cycles is `initialAmount`.
</Note>

<Warning>
  `items` arrays are **not** supported for recurring mode. Recurring charges are a fixed scalar amount, not a cart total. Providing `items` returns a `400` error.
</Warning>

## Surcharge

Add a credit card surcharge to the recurring charge using `surchargePercent`. The surcharge is shown as a line item in the order summary and forwarded to the payment processor.

```json theme={null}
{
  "amount": "29.99",
  "checkoutMode": "recurring",
  "surchargePercent": "2.00",
  "recurringConfig": {
    "planName": "Monthly Pro",
    "interval": "monthly",
    "startDate": "2026-06-01"
  }
}
```

* Accepts a string (`"2.00"`) or number (`2.0`)
* Maximum `"3.00"` (Florida 3% cap)
* Displayed as a separate line item: `Surcharge (2.00%)`
* Applied to the base `amount` before tax

See [Surcharge](#surcharge-reference) below for the full reference.

## Trial Pricing

Use `setupFee`, `initialAmount`, and `initialCycles` together to offer introductory pricing.

### Setup Fee Only

A one-time fee added to the first billing cycle:

```json theme={null}
{
  "amount": "29.99",
  "recurringConfig": {
    "planName": "Pro Monthly",
    "interval": "monthly",
    "startDate": "2026-06-01",
    "setupFee": "9.99"
  }
}
```

| Cycle    | Charged                   |
| :------- | :------------------------ |
| Cycle 1  | `$9.99 + $29.99 = $39.98` |
| Cycle 2+ | `$29.99`                  |

### Introductory Rate

Override the amount for the first N cycles:

```json theme={null}
{
  "amount": "29.99",
  "recurringConfig": {
    "planName": "Pro Monthly",
    "interval": "monthly",
    "startDate": "2026-06-01",
    "initialAmount": "9.99",
    "initialCycles": 2
  }
}
```

| Cycle     | Charged  |
| :-------- | :------- |
| Cycle 1?2 | `$9.99`  |
| Cycle 3+  | `$29.99` |

### Combined (Setup Fee + Trial)

```json theme={null}
{
  "amount": "49.99",
  "recurringConfig": {
    "planName": "Annual Premium",
    "interval": "yearly",
    "startDate": "2026-07-01",
    "setupFee": "10.00",
    "initialAmount": "19.99",
    "initialCycles": 1
  }
}
```

| Cycle    | Charged                                    |
| :------- | :----------------------------------------- |
| Cycle 1  | `$10.00 (setup) + $19.99 (trial) = $29.99` |
| Cycle 2+ | `$49.99`                                   |

## Interval Examples

Use `interval` alone for standard periods. Add `intervalCount` to create any custom period ? the two fields multiply together.

```json theme={null}
{ "interval": "daily" }                             // Every day
{ "interval": "weekly" }                            // Every week
{ "interval": "monthly" }                           // Every month
{ "interval": "yearly" }                            // Every year (annual)
{ "interval": "weekly",  "intervalCount": 2 }       // Every 2 weeks (biweekly)
{ "interval": "monthly", "intervalCount": 3 }       // Every 3 months (quarterly)
{ "interval": "monthly", "intervalCount": 6 }       // Every 6 months (semi-annual)
{ "interval": "daily",   "intervalCount": 30 }      // Every 30 days
```

<Note>
  `intervalCount` is a JSON number (not a string). `"intervalCount": "3"` will return a 400 validation error.
</Note>

## Capped Plans

Use `endDate` or `maxCycles` to automatically stop billing:

```json theme={null}
{
  "amount": "9.99",
  "checkoutMode": "recurring",
  "recurringConfig": {
    "planName": "12-Month Plan",
    "interval": "monthly",
    "startDate": "2026-06-01",
    "maxCycles": 12,
    "endDate": "2027-06-01"
  }
}
```

Either field alone is sufficient; you can set both for belt-and-suspenders control.

## Full Example

Monthly subscription with a 3% surcharge, 1-month trial, and a 12-cycle cap:

```bash theme={null}
curl -X POST https://checkout.ozura.com/api/sessions/create \
  -H "X-API-KEY: your_vault_key" \
  -H "X-OZURA-API-KEY: your_merchant_key" \
  -H "Content-Type: application/json" \
  -d '{
    "merchantId": "your_merchant_id",
    "merchantName": "My SaaS",
    "amount": "49.99",
    "currency": "USD",
    "checkoutMode": "recurring",
    "surchargePercent": "3.00",
    "successUrl": "https://yoursite.com/welcome",
    "cancelUrl": "https://yoursite.com/pricing",
    "errorUrl": "https://yoursite.com/error",
    "recurringConfig": {
      "planName": "Pro Monthly",
      "planDescription": "Unlimited access to all Pro features",
      "interval": "monthly",
      "startDate": "2026-06-01",
      "initialAmount": "0.00",
      "initialCycles": 1,
      "setupFee": "5.00",
      "maxCycles": 12,
      "maxAttempts": 3,
      "merchantRecurringReference": "cus_abc123"
    }
  }'
```

## What the Customer Sees

The checkout page displays a **Subscription Summary** block above the payment form showing:

* Plan name and description
* Billing interval and start date
* Surcharge row (if `surchargePercent` is set)
* End date and cycle cap (if configured)
* Setup fee and trial pricing (if applicable)

The payment form and button are identical to a standard payment session.

## Success Redirect

After a successful recurring signup, the customer is redirected to your `successUrl` with the standard transaction parameters:

```
https://yoursite.com/welcome?success=true&sessionId=session_xxx&checkoutMode=recurring&transactionId=250107000019C4E1B&transactionType=sale&amount=49.99&currency=USD&ozuraMerchantId=ozu_xxx&surchargeAmount=1.50&cardLastFour=1111&cardBrand=VISA&transDate=2026-06-01T12:00:00.000Z
```

| Parameter                   | Description                                                                                                          |
| :-------------------------- | :------------------------------------------------------------------------------------------------------------------- |
| `checkoutMode`              | `"recurring"`                                                                                                        |
| `transactionType`           | `"sale"` (the initial CIT charge that enrolled the customer)                                                         |
| `transactionId`             | The initial charge transaction ID from the payment processor                                                         |
| `surchargeAmount`           | Calculated surcharge amount in the session currency                                                                  |
| (all other standard params) | Same as a standard payment ? see [Handle the Result](/guides/payments/checkout/handle-result#success-url-parameters) |

<Warning>
  **`planId` is not included in the success URL redirect.** The OzuraPay API plan ID is part of the raw payment processor response but is not appended to the redirect URL. To retrieve the plan ID for subscription management (pause, cancel, lookup), query the OzuraPay API recurring plan endpoints using your merchant credentials ? OzuraPay API associates plans with the `merchantRecurringReference` or by listing plans for your merchant account.
</Warning>

## Combine with Payment Links

Create a shareable recurring subscription link:

```json theme={null}
{
  "merchantId": "your_merchant_id",
  "merchantName": "My SaaS",
  "amount": "29.99",
  "currency": "USD",
  "checkoutMode": "recurring",
  "recurringConfig": {
    "planName": "Monthly Pro",
    "interval": "monthly",
    "startDate": "2026-06-01"
  },
  "successUrl": "https://yoursite.com/welcome",
  "cancelUrl": "https://yoursite.com/pricing",
  "errorUrl": "https://yoursite.com/error"
}
```

Post this to `POST /api/payment-links/create` and share the returned `url`. Each customer who visits the link and pays is enrolled in their own plan. See [Payment Links](/guides/payments/checkout/payment-links) for full details.

***

## Surcharge Reference

`surchargePercent` is independent of `checkoutMode` ? it works on standard `payment` sessions too.

| Field              | Type             | Required | Behaviour when omitted                                                                                | Constraints                                                                                         |
| :----------------- | :--------------- | :------: | :---------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- |
| `surchargePercent` | string or number |    No    | Treated as `0%` at charge time ? no surcharge line item shown, `"0.00"` sent to the payment processor | `0` ? `3.00` (Florida maximum). Normalised internally to two decimal places (e.g. `2.5` ? `"2.50"`) |

The surcharge is:

* Displayed as a line item in the order summary: `Surcharge (X.XX%)`
* Applied to the base `amount` (before tax)
* Forwarded to the payment processor on every charge (including recurring cycles)
* Applied to debit cards at the processor's discretion ? OzuraPay API performs a BIN lookup and excludes debit cards automatically where required

```json theme={null}
{
  "amount": "100.00",
  "surchargePercent": "3.00"
}
```

Order summary: `Subtotal $100.00 + Surcharge (3.00%) $3.00 = Total $103.00`
