> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.solvimon.com/platform-guides/products-and-pricing/pricing-plans/on-demand-charges/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: " ``` ```python import requests url = "https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items" headers = {"X-API-KEY": ""} 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': ''}}; 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", "") 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"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items") .header("X-API-KEY", "") .asString(); ``` ```php request('GET', 'https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_jwDeeN0tYSY3F7BkeN1v/on-demand-pricing-items', [ 'headers' => [ 'X-API-KEY' => '', ], ]); 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", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": ""] 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: " \ -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": "", "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': '', '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", "") 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"] = '' 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 response = Unirest.post("http://localhost:10004/v1/invoices/charge-on-demand-pricing-items") .header("X-API-KEY", "") .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 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' => '', ], ]); 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", ""); 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": "", "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).