> 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.

## Summary

A one-off item priced `FLAT` can now bill a unit price and a quantity, not only a single lump sum. A subscription can move between the plans of a pricing plan group in one API call. Credits can now belong to one seat instead of a shared pool. An invoice that spent an amount-denominated wallet now books that spend out of the wallet when it goes final. E-invoicing has a new downloadable report. An invoice's payment status can now be set directly. Workflows can export invoices to an ERP and tag the emails they send. An integration can now be addressed by its reference.

## Pricing

**A one-off item can bill a unit price and a quantity**
A one-off charge used to carry only a single lump sum. To sell four products at 100 EUR, you had to calculate 400 EUR and enter that amount yourself. A [one-off](/platform-guides/products-and-pricing/pricing-plans/one-off-pricing) pricing item config priced `FLAT` now carries a unit count, the same way a per-seat item carries seats. The band's `amount` becomes the unit price. Its `fixed_amount` stays a flat fee, charged once. The line bills `fixed_amount + amount × units`. `FIXED` is unchanged: it still bills a single lump sum.

You can set the count in three places. `default_units` on the pricing item config sets the plan-level default. When absent, it defaults to 1. `units` on a [pricing plan schedule](/platform-guides/customers-and-billing/subscriptions/subscription-schedules) overrides the default per subscription. It is keyed on the pricing item config, the same way `seats_values` is:

**`Units on a schedule`**

```json Units on a schedule
"units": [
  { "pricing_item_config_id": "pico_5RmXe2QbtKfYuA9DcNH3P", "number": "4" }
]
```

On an [on-demand charge](/platform-guides/products-and-pricing/pricing-plans/on-demand-charges), you state the quantity yourself. `units` is required for a `FLAT` item, and it must be positive. It does not fall back to `default_units`. `default_units` is a prefill for an ordering flow. It is not a quantity you confirmed.

**`Order three more units`**

```bash Order three more units
curl -X POST https://test.api.solvimon.com/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": "proi_hwDeeN0vcYJaBLAc0C1W", "units": { "number": "3" } }
    ]
  }'
```

> **Note**
>
> A unit count is always a number. `default_units.amount` and `default_units.count` are rejected. Use `number` instead. Both fields are accepted only on a one-off item priced `FLAT`.

A count of zero is valid on a schedule. It bills no line. Ordering zero units on demand is rejected: it is not an order. A fixed wallet grant on such an item scales by the unit count. A top-up product that grants 500 credits per unit grants 1500 credits for three units. A grant that converts from the paid amount, or takes a percentage of it, cannot scale this way. It is rejected at configuration time with `credits_grant.conversion and amount_grant.percentage are not supported on a ONE_OFF pricing item priced FLAT`. Copying or migrating a schedule carries its units across.

**Move a subscription between the plans of a pricing plan group**
[Pricing plan groups](/platform-guides/products-and-pricing/pricing-plans) have held the rules for moving between related plans since they were introduced. Until now, there was no way to execute the move. [POST /v1/pricing-plan-subscriptions/\{id}/change-plan](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-subscriptions/post-pricing-plan-subscriptions-by-resource-id-change-plan) performs it:

**`Upgrade a subscription to another plan in its group`**

```bash Upgrade a subscription to another plan in its group
curl -X POST https://test.api.solvimon.com/v1/pricing-plan-subscriptions/ppsu_jwDeeN0tYSY3F7BkeN1v/change-plan \
  -H "X-API-KEY: <apiKey>" \
  -H "Content-Type: application/json" \
  -d '{
    "pricing_plan_id": "ppla_QwDeeN0vcYJaBLAc0C29"
  }'
```

The target plan must belong to the same group as the subscription's current plan. That group must be `ACTIVE`. Solvimon infers the direction from the members' `order`. A move from a lower order to a higher order is an upgrade. It follows the group's `upgrade_type`. A move from a higher order to a lower order is a downgrade. It follows the group's `downgrade_type`. That policy sets the timing:

| Policy                | Timing                                                                |
| --------------------- | --------------------------------------------------------------------- |
| `IMMEDIATE`           | Starts now, or at a `start_at` you supply                             |
| `IMMEDIATE_PRO_RATA`  | Starts now, or at a `start_at` you supply                             |
| `NEXT_BILLING_PERIOD` | Starts at the next invoice date; rejects a caller-supplied `start_at` |
| `NOT_ALLOWED`         | Rejects the transition                                                |

You can also move a plan sold in several billing periods between those periods. Pass `billing_period` with the same `pricing_plan_id`. The direction then follows the length of the period instead of the order. The response is the new schedule. See [Plan changes in a pricing plan group](/platform-guides/customers-and-billing/subscriptions/plan-changes).

The move builds a new schedule on the target plan. It does not carry over the optional pricings, commitments, discounts, or seats configured on the schedule it replaces. It works whether the current schedule follows a pricing plan version or a rate card. Solvimon validates the currency against the target version's settings. For the customer portal, `GET /v1/portal/pricing-plan-subscriptions/{id}/pricing-plan-group` returns the group a subscription's plan belongs to. A self-service plan picker needs this to know which plans it may offer.

**A credits-priced config takes its periods from the credits item**
A pricing item config that prices usage in credits used to declare its own `billing_period` and `pricing_period`. These could disagree with the `CREDITS` item that settles the credit type it consumes. Where they disagreed, each pricing period's overage landed on its own invoice instead of the one invoice for the billing period. For example, August's usage was invoiced on 1 September instead of 1 October. Both periods are now inherited from the credits item. This keeps the overage and the credits it draws on the same invoice.

> **Warning**
>
> Three configurations that used to be accepted are now rejected: a credits-priced config with its own periods, credits items that disagree on a cadence, and a pricing period wider than its billing period.

See [Credits in pricing plans](/platform-guides/wallets-credits/credits-in-pricing-plans).

**A draft schedule can be corrected, and a draft subscription can be removed**
Two validations used to treat a draft as though it were already billing. A draft schedule's start date could not move into the past, even though a contract being prepared often starts earlier than the day you configure it. A draft subscription could not be deleted while it still carried draft schedules, which is every draft subscription. Solvimon now allows both. See [Draft schedules](/platform-guides/customers-and-billing/subscriptions/draft-schedules).

### Fixes

bug-fix

* You can now add a discount to a product item that a coupon already discounts. The request used to fail with `start_at before previous, needs to start after`, regardless of the dates you sent. The index named in the error pointed at a clause the caller never sent: the coupon's own discount, appended to the list during the merge and out of date order. Both clauses were always valid. Only their order was not.
* Migrating a pricing plan schedule that carries override pricings now succeeds. `POST /v1/pricing-plan-schedules/{id}/migrate` used to fail with `Pricing ... not in the pricing plan version ...` whenever the schedule had any override, even though the request never touches overrides. Solvimon was resolving the override against the version being migrated away from.
* Activating a draft schedule beside an existing one no longer fails on its seats. Ending the adjacent schedule used to pass the draft's own override pricings on as if they belonged to the active schedule. That schedule then rejected them as pricings its version does not contain.

## Wallets and credits

**Seat-owned credits**
`SEAT_POOL` grants credits per seat into one shared customer pool. Any seat can spend any credit in that pool. `PER_SEAT` is now the third [wallet grant source](/platform-guides/wallets-credits/seat-based-credits). It keeps each seat's credits to that seat: one grant per active [seat assignment](/platform-guides/customers-and-billing/customer/seat-assignments), restricted to the customer who occupies it. A seat can spend its own grant plus any unrestricted grant in the same wallet. A personal allowance and a shared pool can sit side by side.

Solvimon prorates the quantity by how much of the period the assignment covers. A seat handed over mid-month grants the incoming occupant only the remainder, not a full allowance. Assignments are not capped against the seats you paid for: credits follow the assignments. A period with a handover, or with more seats assigned than paid for, therefore grants more than the paid seats account for. Solvimon reports this excess rather than withholding it, because capping would make one seat's credits depend on another's. It is recorded at issuance and read back as `over_granted`: on the grant, on the wallet balance status, and per customer in `customer_details`.

**`A grant issued to a seat`**

```json A grant issued to a seat
{
  "source": "PER_SEAT",
  "seat": {
    "customer_id": "cust_5RmXe2QbtKfYuA9DcNH3P",
    "product_item_id": "proi_2PnBx7VdtRgLmE4WcQJ8K"
  },
  "grant_credits": { "quantity": "250", "credit_type_id": "ctyp_7KdRa4NvpWzYbT6FgXH2M" },
  "over_granted": { "credits": { "quantity": "50", "credit_type_id": "ctyp_7KdRa4NvpWzYbT6FgXH2M" } }
}
```

A seat-owned grant must be attributable to one seat. `PER_SEAT` therefore requires a fixed `credits_grant.credits` on a pricing item priced `PER_SEAT`. Solvimon rejects an amount grant with `PER_SEAT requires a fixed credits_grant.credits; a seat-owned amount grant is not supported`. A wallet type granted from `PER_SEAT` cannot carry a second grant rule on the same wallet type: two rules would produce the same grant key, and Solvimon would drop the second one without a trace.

**An invoice that spent an amount-denominated wallet books that money out at final**
A `CREDIT` wallet type funded with an amount keeps its ledger in a currency. Usage covered by such a wallet used to be held only as a reservation. The available balance already reflected it, so a second invoice could not spend the same money, but no money had actually left the wallet. When the invoice goes `FINAL`, the covered amount now leaves the wallet, oldest-expiring grant first. Solvimon recognises it as revenue with no tax, because it already charged tax on the top-up that funded the balance. Voiding or recalculating the invoice before it is final releases the reservation, and the balance becomes spendable again. Re-running a final invoice does not book the spend twice. See [Credit types & wallets](/platform-guides/wallets-credits/credit-types-wallets).

## Quotes

**Pricing defined directly on a schedule appears on the quote**
A quote whose schedule carries its own pricing, instead of pricing from a pricing plan version, used to show that pricing nowhere: not on the quote overview, and not on the generated PDF. Both now show it. The quote overview also gained an "expand all" action. You can read a quote with several products in one pass, instead of opening each section separately.

**Additional settings on a new quote are collapsed**
The quote creation form now groups its secondary fields into the same collapsible "Additional settings" section the subscription form uses. Billing currency overrides the pricing plan's currency. Left open, the field invited a sales user to fill it in as though it were required. Collapsed, Solvimon sets it only when you are genuinely overriding the currency.

## Invoices

**Set an invoice's payment status directly**
`payment_status` on an invoice used to be read-only. You could change it only by recording a payment. You can now patch it directly on a `FINAL` invoice, for settlement that happened outside Solvimon:

**`Mark a final invoice as paid`**

```bash Mark a final invoice as paid
curl -X PATCH https://test.api.solvimon.com/v1/invoices/invo_jwDeeN0tYSY3F7BkeN1v \
  -H "X-API-KEY: <apiKey>" \
  -H "Content-Type: application/json" \
  -d '{ "payment_status": "PAID" }'
```

> **Note**
>
> You can only set `PAID` or `UNPAID`. `PARTIALLY_PAID` and `OVERPAID` follow from the paid and open amounts, so Solvimon rejects them. Solvimon also rejects a request that sets the deprecated `paid` flag to a value the status contradicts. You cannot set the field while creating an invoice.

See [Invoice payment status](/platform-guides/invoicing/invoices/invoice-payment-status).

**Avalara e-invoicing moves to ELR 1.6**
The Avalara integration now runs on E-Invoicing and Live Reporting 1.6. It reports a business status and an authority status alongside the document status. Two refresh behaviours come with this. Statuses stamped on the same millisecond now sort by their message instead of arbitrarily. Follow-up now stops once the most recently submitted e-invoice reaches a final state. Earlier failed attempts no longer keep follow-up alive. See [E-invoicing](/platform-guides/invoicing/e-invoicing).

**The new invoice PDF renderer is enabled per platform**
The [rendering service](/platform-guides/invoicing/invoices) that already produces quote PDFs was introduced for invoices as an opt-in preview. An entitlement on your platform now selects it. It covers every invoice PDF, not only a download from Desk. The copy attached when an invoice is finalised or emailed goes through the same renderer, so one invoice cannot look two different ways. The download button is translated into the languages the customer portal supports. The renderer is still opt-in. Contact your account manager to have it switched on.

**Search a full invoice history by date**
A date range on `POST /v1/invoices/search` now returns its page correctly on a platform with years of invoices behind it. A wide range used to risk running past the request timeout and returning a 500. You can now ask for a quarter or a year in one request, for a reporting or reconciliation run. Payment, payment request, bulk action, and subscription search filter on dates the same way, and answer faster.

> **Note**
>
> This change also fixes a related bug: a bound given with sub-second precision now matches the second that contains it. It previously matched nothing at all.

**Filter invoices by the workflow that acted on them**
You can now filter invoices on the status of the workflow actions that ran against them. Use `workflow_action_execution_statuses[]` when listing, and `workflow_action_execution_status` in a search body. You can now pull invoices whose [workflow actions](/platform-guides/workflows/monitoring-and-audit) failed in one request. Before this, you had to read the execution log and look each invoice up yourself.

### Fixes

bug-fix

* You can now finalise a credit note against a one-off invoice without a manual refresh. The `PATCH` to `FINAL` used to return a 500, and the validator reported a total of 0. Solvimon was not reloading the sales tax rates of a one-off product for a credit note the way it does for the one-off invoice itself.
* AvaTax rates are now loaded for the next schedule as well as the previous one. An invoice spanning two schedules, where the second schedule prices products the first does not, used to be taxed without rates for those products.

## Reporting

**An e-invoicing report**
E-invoicing status used to be visible only per invoice, never in aggregate. To answer "which invoices have not been accepted yet," you had to open them one at a time. A new `E_INVOICING` report joins the [report downloads](/platform-guides/analytics-reporting/report-downloads), with one line per e-invoice. Each line carries the invoice it belongs to (number, date, billing period, status, type, currency, amount, billing entity, and customer) and the e-invoicing side of it (external reference, processor, applied mandate, integration, current status and message, submitted at, last status at, and how many statuses it has been through). An invoice submitted more than once gets a line per submission, so a failure and the resubmission that followed it both appear in the extract. See [E-invoicing report](/platform-guides/analytics-reporting/report-downloads/e-invoicing-report).

## Integrations

**Address an integration by its reference**
The reference on an integration tells several integrations of the same type apart. You could already use it everywhere except on the integration itself, which only accepted its resource id. `GET`, `PATCH`, and `DELETE` on `/v1/integrations/{idOrReference}` now accept either, and so do the deprecate and activate actions under it. If your code already knows an integration by its reference, it no longer has to store the generated id alongside it. You can also now delete draft ERP integrations.

## Workflows

**Export an invoice to an ERP from a workflow**
`EXPORT_TO_ERP` joins `SEND_EMAIL_TO_CUSTOMER` as a [workflow action](/platform-guides/workflows/workflow-actions) type. The action names the integration responsible for the export through `export_to_erp.executor`. The one available executor is `THIRD_PARTY`. It performs the export through the connected ERP integration and records a workflow execution row for it. An export that used to happen outside the workflow engine is now visible in the [execution log](/platform-guides/workflows/monitoring-and-audit) beside the rest of the automation, with the same retry and status reporting. You can configure the action in Desk. The panel lists the ERP integrations connected to your platform.

**Tag the emails a workflow sends**
The send-email action now accepts `tags`: up to 3 ASCII tags of at most 128 characters each. Solvimon passes them to Mailgun as `o:tag`, so you can filter and report on messages in the Mailgun dashboard. The legacy Paragon integration exposed this, and migrated templates lost it. You set tags per workflow, so you can tell different workflows apart. They apply only to a Mailgun account you connected yourself. A Solvimon-managed email integration rejects them with `not allowed for Solvimon-managed email integrations`, and tags its own traffic instead.

**Find the workflow activity for one customer**
`POST /v1/workflow-action-executions/search` now accepts a `customer_id` filter. The workflow trigger endpoints accept `expand[]=customer_id`, which inlines the customer on the trigger. Before this, answering "what did the automation do for this customer" meant searching every execution and then looking up each row.

**An overdue reminder template of its own**
Selecting a trigger preselects the email template written for it, which keeps a workflow from going out under the wrong wording. The overdue invoice trigger had no template of its own and fell back to the standard final invoice template. That template reads as though the invoice had just been issued, not as though it were late. There is now a dedicated overdue reminder template. The "Invoice: status set to overdue" trigger preselects it, and a new sandbox is created with it already configured for the overdue notification.

## Customer portal

**Pricing defined directly on a schedule is shown**
A subscription schedule can carry pricing of its own, in addition to or instead of the pricing that comes from a pricing plan version. The portal used to render only the pricing plan version's pricing. A customer looking at their own subscription saw a plan that was missing the products negotiated onto it directly. The portal now lists those products with the rest.

## Desk

### Fixes

bug-fix

* Deleting a customer or a subscription now updates the list you deleted it from. A delete the API refuses now reports why, instead of appearing to do nothing. Deleting from a detail page no longer returns you to the previous page regardless of what happened.
* Text in the rich-text editor of a quote is now rendered in the PDF in the font it was written in, and the right-hand edge is no longer cut off.
* Invoice numbering settings no longer submit an invoice date determination type that you did not change. Editing a self-billing numbering format used to re-submit the platform's date determination type, default it to `WHEN_INVOICE_GOES_TO_FINAL`, and apply it at the wrong level. That could change when invoices take their date, across the whole platform.
* A custom field shared by two integrations is now edited per integration. With two fields carrying the same reference on different integrations, Desk used to match on the reference alone. Both inputs showed the same value, and editing one changed the other.