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

# Create Payment Link

> Create a payment link for collecting payments from customers (one-time or recurring).

## Overview

Create a payment link for collecting payments from customers. The link can be **one-time** (default) or **recurring**. For `POST /v1/ext/collections/payment-link`, `itemDeliveryAddress` and `cartItems` are required, and each cart item must include at least `name` and `description`.

**Endpoint:** `POST /v1/ext/collections/payment-link`

**Auth:** Bearer token with scope `external.apis` (authorizer: `walletos-account-service-{stage}-authorizer`).

## Default vs recurring

| Mode                   | How to request                                                                | Result                                                                                                           |
| ---------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| **One-time (default)** | Omit `checkoutType` and `recurringCharge`, or send `checkoutType: "generic"`. | Single-use or multi-use payment link; customer pays once (or multiple times if `isOpenLink` is true).            |
| **Recurring**          | Send `checkoutType: "recurring"` and a valid `recurringCharge` object.        | Payment link that creates a subscription (fixed, consumption, or seating) after the customer completes checkout. |

**Recurring compatibility:** Existing callers that do not send `checkoutType` or `recurringCharge` still get one-time payment links.

## Request

### URL

```http theme={null}
POST {{LIQUIDITY_URL}}/v1/ext/collections/payment-link
```

### Headers

| Header          | Required | Description                                  |
| --------------- | -------- | -------------------------------------------- |
| `Authorization` | Yes      | `Bearer <token>` with scope `external.apis`. |
| `Content-Type`  | Yes      | `application/json`.                          |

### Body (JSON) – common fields

| Field                    | Type    | Required | Description                                                                                                                                   |
| ------------------------ | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `currency`               | string  | Yes      | Currency code. Supported: `USD`, `NGN`, `CAD`, `GBP`, `EUR`.                                                                                  |
| `name`                   | string  | No       | Display name for the link (default: `"page"`).                                                                                                |
| `amount`                 | number  | Yes\*    | Amount per payment. \*For one-time: min 0.5. For recurring `subscription`: required (min 0.5). For `consumption`/`seating`: optional (min 0). |
| `customUrlPath`          | string  | No       | Slug for the URL (default: `"page"`).                                                                                                         |
| `redirectTo`             | string  | No       | URI to redirect after payment (default: `"https://www.centryos.xyz"`).                                                                        |
| `expiredAt`              | string  | No       | ISO date when the link expires (default: one year from now).                                                                                  |
| `checkoutType`           | string  | No       | `"generic"` (default), `"ecommerce"`, or `"recurring"`. Use `"recurring"` for recurring payment links.                                        |
| `recurringCharge`        | object  | No\*\*   | Required when `checkoutType === "recurring"`. Forbidden otherwise. See [Recurring body](#recurring-body).                                     |
| `isOpenLink`             | boolean | No       | If true, link can be used multiple times (default: true).                                                                                     |
| `customerPays`           | boolean | No       | If true, customer pays fees (default: true).                                                                                                  |
| `orderId`                | string  | No       | Your order/reference id.                                                                                                                      |
| `dataCollections`        | array   | No       | Extra data to collect (e.g. `["Phone number"]`). Base set always includes Email, First name, Last name, Phone number.                         |
| `customFields`           | array   | No       | Custom field names.                                                                                                                           |
| `acceptedPaymentOptions` | array   | No       | e.g. `["card"]` (default).                                                                                                                    |
| `notifyPayee`            | boolean | No       | Notify payee (default: false).                                                                                                                |
| `agent`                  | object  | No       | Agent details (type, email, name, etc.) for notifications.                                                                                    |
| `brandingConfig`         | object  | No       | Logo, colors, etc.                                                                                                                            |
| `advancedConfig`         | object  | No       | `websiteUrl`, `webhookPath`, `webhookSecret` for external webhook registration when using `/ext` path.                                        |
| `customData`             | object  | No       | Key-value metadata (string values only).                                                                                                      |
| `itemDeliveryAddress`    | string  | Yes      | Merchant-supplied delivery address saved with the payment link and copied into transaction metadata when the customer pays.                   |
| `cartItems`              | array   | Yes      | Cart items for the payment link. Send at least one item, and each item must include `name` and `description`.                                 |
| `externalId`             | string  | No       | Your external reference.                                                                                                                      |

### Cart items

Each `cartItems[]` entry must include:

| Field         | Type   | Required | Description                           |
| ------------- | ------ | -------- | ------------------------------------- |
| `name`        | string | Yes      | Item name shown for the order.        |
| `description` | string | Yes      | Item description shown for the order. |
| `qty`         | number | No       | Quantity of the item.                 |
| `price`       | number | No       | Item price.                           |
| `currency`    | string | No       | Item currency code.                   |
| `productId`   | string | No       | Your product identifier.              |
| `imageUrl`    | string | No       | Public image URL for the item.        |

### Recurring body

When `checkoutType` is `"recurring"`, you **must** send `recurringCharge` with this shape:

| Field                | Type   | Required                     | Description                                                           |
| -------------------- | ------ | ---------------------------- | --------------------------------------------------------------------- |
| `type`               | string | Yes                          | `"subscription"`, `"consumption"`, or `"seating"`.                    |
| `startDate`          | string | Yes                          | ISO date when billing starts (trial end). Must be before `expiredAt`. |
| `interval`           | object | Yes                          | Billing interval.                                                     |
| `interval.type`      | string | Yes                          | `"day"`, `"week"`, `"month"`, or `"year"`.                            |
| `interval.count`     | number | Yes                          | e.g. 1 for monthly.                                                   |
| `productId`          | string | No                           | Product ID for recurring billing (optional).                          |
| `consumptionDetails` | object | Yes if type is `consumption` | See below.                                                            |
| `seatDetails`        | object | Yes if type is `seating`     | See below.                                                            |

**When `type === "consumption"`:**

| Field          | Type   | Required    | Description                          |
| -------------- | ------ | ----------- | ------------------------------------ |
| `unit`         | string | Yes         | e.g. `"API call"`, `"GB"`.           |
| `costPerUnit`  | number | Yes         | Price per unit.                      |
| `minimumUnits` | number | No          | Included units before charges apply. |
| `baseFee`      | number | Conditional | Required if `minimumUnits` is set.   |

**When `type === "seating"`:**

| Field                | Type   | Required | Description                                    |
| -------------------- | ------ | -------- | ---------------------------------------------- |
| `baseFee`            | number | Yes      | Fixed fee per period.                          |
| `costPerSeat`        | number | Yes      | Price per seat.                                |
| `newSeatBillingType` | string | Yes      | `"prorate"`, `"next_cycle"`, or `"immediate"`. |

## Examples

All `POST /v1/ext/collections/payment-link` examples below include the required `itemDeliveryAddress` and `cartItems` fields.

### One-time payment link (default)

```bash theme={null}
curl -X POST "{{LIQUIDITY_URL}}/v1/ext/collections/payment-link" \
  -H "Authorization: Bearer {{accessToken}}" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "USD",
    "name": "One-time donation",
    "amount": 25.00,
    "customUrlPath": "donate",
    "redirectTo": "https://example.com/thanks",
    "itemDeliveryAddress": "123 Main St, Austin, TX, 78701, US",
    "cartItems": [
      {
        "name": "Hourly service",
        "description": "One hour of service",
        "price": 25.00,
        "currency": "USD"
      }
    ]
  }'
```

Omit `checkoutType` and `recurringCharge` to get the existing one-time behavior.

### Recurring – fixed subscription

```json theme={null}
{
  "currency": "USD",
  "name": "Monthly plan",
  "amount": 29.99,
  "customUrlPath": "monthly-plan",
  "redirectTo": "https://example.com/thanks",
  "itemDeliveryAddress": "123 Main St, Austin, TX, 78701, US",
  "cartItems": [
    {
      "name": "Monthly plan seat",
      "description": "Monthly subscription access",
      "price": 29.99,
      "currency": "USD"
    }
  ],
  "checkoutType": "recurring",
  "recurringCharge": {
    "type": "subscription",
    "startDate": "2026-02-01",
    "interval": { "type": "month", "count": 1 }
  }
}
```

### Recurring – consumption (usage-based)

```json theme={null}
{
  "currency": "USD",
  "name": "API usage",
  "amount": 0,
  "customUrlPath": "api-usage",
  "redirectTo": "https://example.com/thanks",
  "itemDeliveryAddress": "123 Main St, Austin, TX, 78701, US",
  "cartItems": [
    {
      "name": "API usage plan",
      "description": "Usage-based billing plan",
      "price": 0,
      "currency": "USD"
    }
  ],
  "checkoutType": "recurring",
  "recurringCharge": {
    "type": "consumption",
    "startDate": "2026-02-01",
    "interval": { "type": "month", "count": 1 },
    "consumptionDetails": {
      "unit": "API call",
      "costPerUnit": 0.10,
      "minimumUnits": 1000,
      "baseFee": 20
    }
  }
}
```

### Recurring – seating (per-seat)

```json theme={null}
{
  "currency": "USD",
  "name": "Team plan",
  "amount": 0,
  "customUrlPath": "team-plan",
  "redirectTo": "https://example.com/thanks",
  "itemDeliveryAddress": "123 Main St, Austin, TX, 78701, US",
  "cartItems": [
    {
      "name": "Team plan base seat",
      "description": "Seat-based team subscription",
      "price": 0,
      "currency": "USD"
    }
  ],
  "checkoutType": "recurring",
  "recurringCharge": {
    "type": "seating",
    "startDate": "2026-02-01",
    "interval": { "type": "month", "count": 1 },
    "seatDetails": {
      "baseFee": 20,
      "costPerSeat": 5,
      "newSeatBillingType": "prorate"
    }
  }
}
```

## Response

### Success (200)

```json theme={null}
{
  "success": true,
  "data": {
    "url": "<checkout-widget-url>",
    "application": {
      "id": "<payment-link-uuid>",
      "token": "<token>",
      "tokenType": "<type>",
      "expiredAt": "<ISO-date>",
      "valid": true
    }
  }
}
```

* `data.url` is the payment link URL to share with customers.
* For recurring links, after the customer completes checkout (payment method saved), a subscription is created and billing follows the configured interval and type.

### Error (4xx / 5xx)

```json theme={null}
{
  "success": false,
  "message": "<error detail or validation errors>",
  "fatal": true
}
```

| Situation                          | HTTP | Notes                                                                                                                                    |
| ---------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Unauthorized                       | 401  | Invalid or expired token, or missing `external.apis` scope.                                                                              |
| Validation error                   | 400  | Missing/invalid fields (e.g. amount, currency, `itemDeliveryAddress`, `cartItems`, or `recurringCharge` when checkoutType is recurring). |
| recurringCharge when not recurring | 400  | `recurringCharge` is only allowed when `checkoutType` is `"recurring"`.                                                                  |
| Missing recurringCharge            | 400  | When `checkoutType` is `"recurring"`, `recurringCharge` is required with valid type, startDate, interval, and type-specific details.     |
| Collection account not found       | 400  | No collection account for the business/currency.                                                                                         |
| Server error                       | 500  | e.g. database or downstream failure.                                                                                                     |

## Validation summary

* **currency** – Required, one of supported types.
* **amount** – Required for one-time and for recurring `subscription` (min 0.5). Optional for `consumption`/`seating` (min 0).
* **itemDeliveryAddress** – Required on `POST /v1/ext/collections/payment-link`.
* **cartItems** – Required on `POST /v1/ext/collections/payment-link`; send at least one item, and each item must include `name` and `description`.
* **checkoutType** – Optional. Default: `"generic"`. Use `"recurring"` for recurring payment links.
* **recurringCharge** – Allowed only when `checkoutType === "recurring"`. Required then, with `type`, `startDate`, `interval`; plus `consumptionDetails` or `seatDetails` depending on `type`.
* **startDate** – Must be before `expiredAt`.

## Recurring billing behavior

For recurring links, the customer is first asked to save a payment method (no charge at link use). When the setup succeeds, a subscription is created and charges follow the configured model (subscription, consumption, or seating). Billing cycles, seat changes, and usage reporting follow the platform’s recurring payments behavior.

## Changelog

* **External API request fields:** `itemDeliveryAddress` and `cartItems` are required on `POST /v1/ext/collections/payment-link`. Each cart item must include at least `name` and `description`.
* **Recurring via External API:** Create payment link supports `checkoutType: "recurring"` and `recurringCharge` on `POST /v1/ext/collections/payment-link`. Default remains one-time when these fields are omitted (no breaking changes).
