> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.solvimon.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.solvimon.com/_mcp/server.

# On-demand charges

Charge selected one-off product items on top of an existing subscription whenever you need to: a one-time setup fee, an extra package, or an add-on bought at the click of a button.

---

## Why this matters

Some product items aren't billed on a recurring schedule; they're charged when the customer asks for them. Flagging a [one-off](/platform-guides/products-and-pricing/pricing-plans/one-off-pricing) product item pricing as **on-demand** (the `on_demand` flag on its pricing item config) lets you retrieve the on-demand items available for a subscription and charge one or more of them on a separate invoice, with an optional preview first.

## Listing the available on-demand items

To know which on-demand items can be charged for a subscription, fetch them per pricing plan schedule with [GET /v1/pricing-plan-schedules/\{id}/on-demand-pricing-items](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-schedules/get-pricing-plan-schedules-by-resource-id-on-demand-pricing-items). Pass the optional `timestamp` query parameter to evaluate the schedule's pricing at a specific point in time; it defaults to now.

### Request

GET [https://test.api.solvimon.com/v\{version}/pricing-plan-schedules/\{resourceId}/on-demand-pricing-items](https://test.api.solvimon.com/v\{version}/pricing-plan-schedules/\{resourceId}/on-demand-pricing-items)

```curl
curl https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items \
     -H "X-API-KEY: <apiKey>"
```

```python
import requests

url = "https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items"

headers = {"X-API-KEY": "<apiKey>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items';
const options = {method: 'GET', headers: {'X-API-KEY': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("X-API-KEY", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["X-API-KEY"] = '<apiKey>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items")
  .header("X-API-KEY", "<apiKey>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items', [
  'headers' => [
    'X-API-KEY' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items");
var request = new RestRequest(Method.GET);
request.AddHeader("X-API-KEY", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["X-API-KEY": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

The response mirrors a pricing plan version, filtered to the product item pricings that are `ONE_OFF` and flagged on-demand:

### Response (200)

```json
{
  "pricing_plan_subscription_id": "ppsu_ZwDeeN0vcSMXaMAFhN19",
  "pricing_plan_schedule_id": "ppsc_jwDeeN0tYSY3F7BkeN1v",
  "pricing_plan_version_id": "ppve_pwDeeN0vcYJaBLAc0C1U",
  "pricing_categories": [
    {
      "object_type": "PRICING_CATEGORY",
      "id": "prct_gwDeeN0vcYJaBLAc0C1V",
      "pricings": [
        {
          "object_type": "PRICING",
          "id": "pric_hwDeeN0vcYJaBLAc0C1W",
          "name": "Setup fee",
          "product_type": "ADDON",
          "items": [
            {
              "object_type": "PRICING_ITEM",
              "id": "prii_ewDeeN0vcZ0ioTAmib1d",
              "configs": [
                {
                  "object_type": "PRICING_ITEM_CONFIG",
                  "id": "pico_iwDeeN0vcYJaBLAc0C1X",
                  "on_demand": true,
                  "type": "FLAT"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

> **Info**
>
> The same endpoint is available in the Customer Portal API, so you can present on-demand items to your customers and let them buy directly from the portal.

## Charging an on-demand item

Charge one or more on-demand items with [POST /v1/invoices/charge-on-demand-pricing-items](https://docs.solvimon.com/api-docs/transaction-api/invoices/post-invoices-charge-on-demand-pricing-items):

### Request

POST [http://localhost:10004/v\{version}/invoices/charge-on-demand-pricing-items](http://localhost:10004/v\{version}/invoices/charge-on-demand-pricing-items)

```curl
curl -X POST http://localhost:10004/v1/invoices/charge-on-demand-pricing-items \
     -H "X-API-KEY: <apiKey>" \
     -H "Content-Type: application/json" \
     -d '{
  "pricing_plan_schedule_id": "ppsc_jwDeeN0tYSY3F7BkeN1v",
  "pricing_items": [
    {
      "pricing_item_id": "prii_ewDeeN0vcZ0ioTAmib1d"
    }
  ],
  "start_at": "2026-07-01T00:00:00Z",
  "finalize_immediately": true,
  "payment_method_id": "pmet_fwDeeN0vhphnhMAcBp1G",
  "preview": false
}'
```

```python
import requests

url = "http://localhost:10004/v1/invoices/charge-on-demand-pricing-items"

payload = {
    "pricing_plan_schedule_id": "ppsc_jwDeeN0tYSY3F7BkeN1v",
    "pricing_items": [{ "pricing_item_id": "prii_ewDeeN0vcZ0ioTAmib1d" }],
    "start_at": "2026-07-01T00:00:00Z",
    "finalize_immediately": True,
    "payment_method_id": "pmet_fwDeeN0vhphnhMAcBp1G",
    "preview": False
}
headers = {
    "X-API-KEY": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'http://localhost:10004/v1/invoices/charge-on-demand-pricing-items';
const options = {
  method: 'POST',
  headers: {'X-API-KEY': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"pricing_plan_schedule_id":"ppsc_jwDeeN0tYSY3F7BkeN1v","pricing_items":[{"pricing_item_id":"prii_ewDeeN0vcZ0ioTAmib1d"}],"start_at":"2026-07-01T00:00:00Z","finalize_immediately":true,"payment_method_id":"pmet_fwDeeN0vhphnhMAcBp1G","preview":false}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "http://localhost:10004/v1/invoices/charge-on-demand-pricing-items"

	payload := strings.NewReader("{\n  \"pricing_plan_schedule_id\": \"ppsc_jwDeeN0tYSY3F7BkeN1v\",\n  \"pricing_items\": [\n    {\n      \"pricing_item_id\": \"prii_ewDeeN0vcZ0ioTAmib1d\"\n    }\n  ],\n  \"start_at\": \"2026-07-01T00:00:00Z\",\n  \"finalize_immediately\": true,\n  \"payment_method_id\": \"pmet_fwDeeN0vhphnhMAcBp1G\",\n  \"preview\": false\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("X-API-KEY", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("http://localhost:10004/v1/invoices/charge-on-demand-pricing-items")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"pricing_plan_schedule_id\": \"ppsc_jwDeeN0tYSY3F7BkeN1v\",\n  \"pricing_items\": [\n    {\n      \"pricing_item_id\": \"prii_ewDeeN0vcZ0ioTAmib1d\"\n    }\n  ],\n  \"start_at\": \"2026-07-01T00:00:00Z\",\n  \"finalize_immediately\": true,\n  \"payment_method_id\": \"pmet_fwDeeN0vhphnhMAcBp1G\",\n  \"preview\": false\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("http://localhost:10004/v1/invoices/charge-on-demand-pricing-items")
  .header("X-API-KEY", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"pricing_plan_schedule_id\": \"ppsc_jwDeeN0tYSY3F7BkeN1v\",\n  \"pricing_items\": [\n    {\n      \"pricing_item_id\": \"prii_ewDeeN0vcZ0ioTAmib1d\"\n    }\n  ],\n  \"start_at\": \"2026-07-01T00:00:00Z\",\n  \"finalize_immediately\": true,\n  \"payment_method_id\": \"pmet_fwDeeN0vhphnhMAcBp1G\",\n  \"preview\": false\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'http://localhost:10004/v1/invoices/charge-on-demand-pricing-items', [
  'body' => '{
  "pricing_plan_schedule_id": "ppsc_jwDeeN0tYSY3F7BkeN1v",
  "pricing_items": [
    {
      "pricing_item_id": "prii_ewDeeN0vcZ0ioTAmib1d"
    }
  ],
  "start_at": "2026-07-01T00:00:00Z",
  "finalize_immediately": true,
  "payment_method_id": "pmet_fwDeeN0vhphnhMAcBp1G",
  "preview": false
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-API-KEY' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("http://localhost:10004/v1/invoices/charge-on-demand-pricing-items");
var request = new RestRequest(Method.POST);
request.AddHeader("X-API-KEY", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"pricing_plan_schedule_id\": \"ppsc_jwDeeN0tYSY3F7BkeN1v\",\n  \"pricing_items\": [\n    {\n      \"pricing_item_id\": \"prii_ewDeeN0vcZ0ioTAmib1d\"\n    }\n  ],\n  \"start_at\": \"2026-07-01T00:00:00Z\",\n  \"finalize_immediately\": true,\n  \"payment_method_id\": \"pmet_fwDeeN0vhphnhMAcBp1G\",\n  \"preview\": false\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "X-API-KEY": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "pricing_plan_schedule_id": "ppsc_jwDeeN0tYSY3F7BkeN1v",
  "pricing_items": [["pricing_item_id": "prii_ewDeeN0vcZ0ioTAmib1d"]],
  "start_at": "2026-07-01T00:00:00Z",
  "finalize_immediately": true,
  "payment_method_id": "pmet_fwDeeN0vhphnhMAcBp1G",
  "preview": false
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:10004/v1/invoices/charge-on-demand-pricing-items")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

| Field                      | Required               | Description                                                                                                                 |
| -------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `pricing_plan_schedule_id` | yes                    | The schedule the on-demand items belong to.                                                                                 |
| `pricing_items`            | yes                    | The on-demand items to charge, each referenced by `pricing_item_id`.                                                        |
| `pricing_items[].units`    | yes (for `FLAT` items) | The number of units to charge for a one-off item priced `FLAT`. Must be positive.                                           |
| `start_at`                 | no                     | When the charge applies. Must be in the future; defaults to now.                                                            |
| `finalize_immediately`     | no                     | When `true`, the invoice moves straight from `OPEN` → `DRAFT` → `FINAL` in one call.                                        |
| `payment_method_id`        | no                     | The payment method to use. Defaults to the subscription's payment method.                                                   |
| `preview`                  | no                     | When `true`, returns the resulting invoice **without** persisting it, so you can show the customer what they'll be charged. |

`pricing_items` accepts the same `pricing_item_id` more than once. Each occurrence charges its own line and carries its own invoice date, and both lines land in the same invoice group. For example, an item priced at 100 USD granting a fixed 500 credits, listed twice, charges 200 USD and grants 1000 credits.

A one-off item priced `FLAT` bills a unit price times a count, so the request states the count in `units`. It is required for such an item and must be positive. There is no fallback to the item's `default_units`: that value prefills your ordering screen, while the request states what the customer actually ordered. Ordering zero units is rejected. See [One-off pricing](/platform-guides/products-and-pricing/pricing-plans/one-off-pricing).

The response is an [invoice](/platform-guides/invoicing/invoices) containing the charged on-demand lines:

### Response (201)

```json
{
  "object_type": "INVOICE",
  "id": "invo_kwDeeN0vhq0hnMAcBq2H",
  "invoice_number": "INV-2026-07-00042",
  "customer_id": "cust_AbD3DqausjOYiMNDZY11F",
  "status": "FINAL",
  "type": "ONE_OFF",
  "created_at": "2026-07-01T00:00:00Z",
  "updated_at": "2026-07-01T00:00:00Z",
  "invoice_date": "2026-07-01T00:00:00Z",
  "billing_currency": "EUR",
  "invoice_amount_including_tax": {
    "quantity": "250.00",
    "currency": "EUR"
  },
  "pricing_plan_subscription_ids": [
    "ppsu_ZwDeeN0vcSMXaMAFhN19"
  ],
  "payment_status": "UNPAID",
  "paid": false
}
```

> **Warning**
>
> Use `preview: true` to show the customer what the charge will look like before they confirm. A preview is never persisted and never finalized, regardless of `finalize_immediately`.

## Typical flow

#### List the available items

Call [GET /v1/pricing-plan-schedules/\{id}/on-demand-pricing-items](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-schedules/get-pricing-plan-schedules-by-resource-id-on-demand-pricing-items) and show the available items to the customer.

#### Let the customer pick

The customer selects one or more items to buy.

#### Preview the charge (optional)

Call the charge endpoint with `preview: true` to show the expected invoice before the customer confirms.

#### Create the invoice

Call the charge endpoint with `preview: false` to create the invoice, optionally with `finalize_immediately: true` to finalize it in the same call.

---

See also: [Wallet top-ups](/platform-guides/wallets-credits/wallet-top-ups), which uses on-demand items to sell credits, [One-off pricing](/platform-guides/products-and-pricing/pricing-plans/one-off-pricing), [Add-ons](/platform-guides/products-and-pricing/pricing-plans/add-ons), [Invoices](/platform-guides/invoicing/invoices) and [One-off invoices](/platform-guides/invoicing/invoices/one-off-invoices).