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

# Changelog

## October 7, 2026

## Summary

This release removes steps between Solvimon and the people and tools that use it. An AI client connects with your Solvimon login, so you no longer paste an API key into a configuration file. A sales rep can ask whether a quote version needs approval before they finalize it. A quote version and an invoice both give out a short-lived link to their PDF, so your portal or CRM does not proxy the file. A checkout page can show your terms and conditions, and a customer can buy an on-demand item from the portal and pay for it in one visit.

For integrations, the file lines endpoint filters on several statuses in one call, and a subscription created from a template accepts your own fields. Invoice paging is now stable, only a draft customer can be deleted, and Desk creates enum and boolean custom fields.

## Event ingestion

**Filter file lines on several statuses at once**
[GET /v1/files/\{id}/lines](https://docs.solvimon.com/api-docs/event-api/files/get-files-by-resource-id-lines) takes a repeatable `statuses[]` parameter. One call returns every line you still have to act on, for example `statuses[]=ERROR&statuses[]=SKIPPED`. Before this, reviewing a processed [file upload](/platform-guides/meter-and-event-design/usage-events/file-uploads) took one request for each status. A line reports `IMPORTED`, `DONE`, `SKIPPED` or `ERROR`. The same `statuses[]` parameter is now on [GET /v1/files](https://docs.solvimon.com/api-docs/event-api/files/get-files), so you can list the files that are still running or that ended in error. The single-valued `status` parameter on the lines endpoint still works and is deprecated.

### Fixes

bug-fix

* A [cost prediction](/platform-guides/meter-and-event-design/usage-events/event-cost-prediction) for a configuration that the engine does not price yet now reports `NOT_MATCHED`. The separate `NOT_SUPPORTED` status is removed. It described the same outcome, and two values for one result meant that every caller had to handle both.

## Quotes

**Ask whether a quote version needs approval before you finalize it**
[POST /v1/approval-policies/evaluate](https://docs.solvimon.com/api-docs/configuration-api/approval-policy/post-approval-policies-evaluate) with a `quote_version_id` reports what [finalizing](https://docs.solvimon.com/api-docs/configuration-api/quote-versions/post-quote-versions-by-resource-id-finalize) that quote version would do about approval. It creates and changes nothing:

**`Response`**

```json Response
{
  "object_type": "APPROVAL_POLICY_EVALUATION",
  "status": "APPROVAL_REQUIRED",
  "triggered_approval_policy_ids": ["appo_5RmXe2QbtKfYuA9DcNH3P"],
  "approval_strategy": "EVERYONE_HAS_TO_APPROVE",
  "approvers": [ ... ]
}
```

`status` is `NO_APPROVAL_REQUIRED`, `APPROVAL_REQUIRED`, or `CANNOT_BE_DETERMINED`. The last value means that a rule could not be answered, and finalize would refuse in that case. The response also names the policies that would be breached, the reason, and the approvers who would receive the request. A sales rep learns what a discount costs them in approval time while they still edit the quote. An integration no longer has to re-implement the rule engine against [GET /v1/approval-policies](https://docs.solvimon.com/api-docs/configuration-api/approval-policy/get-approval-policies), where it would drift from the evaluation on the server. The endpoint needs `QUOTE.VIEW` and `APPROVAL_POLICY.VIEW`. See [Approval policies](/platform-guides/customers-and-billing/quoting/approval-policy).

**Signature anchors on the quote PDF**
The quote PDF now renders a signing section with a labeled anchor for each signer. The e-signature integration places each signature box on a literal string in the PDF. A quote with several signers now routes each box to the correct person. See [Quote templates](/platform-guides/customers-and-billing/quoting/quote-templates).

## Invoices

**A short-lived link to the PDF**
[GET /v1/invoices/\{id}/download-url](https://docs.solvimon.com/api-docs/transaction-api/invoices/get-invoices-by-resource-id-download-url) and [GET /v1/quote-versions/\{id}/download-url](https://docs.solvimon.com/api-docs/configuration-api/quote-versions/get-quote-versions-by-resource-id-download-url) return a signed link to the rendered PDF. A portal, a CRM or an email template can give the document to the reader directly. Your own service does not have to proxy the bytes. See [Downloading the invoice PDF](/platform-guides/invoicing/invoices/invoice-operations#downloading-the-invoice-pdf).

### Fixes

bug-fix

* Paging [GET /v1/invoices](https://docs.solvimon.com/api-docs/transaction-api/invoices/get-invoices) now returns each invoice exactly one time. Without `order_by`, the list sorted on invoice date alone, and a billing run gives many invoices the same date. Rows with the same date could come back in a different order for each query. A page boundary then moved between calls, so the same invoice appeared two times while another was skipped.
* A self-billing invoice that was overpaid now shows "Total amount payable" instead of a negative "Amount due".

## Subscriptions

**Set your own fields when you create a subscription from a template**
[POST /v1/pricing-plan-subscriptions/init](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-subscriptions/post-pricing-plan-subscriptions-init) now accepts a set of fields on top of the copy from the template, instead of only `reference` and `status`. You can set `customer_id`, `variant`, `forward_customer_id`, `billing_entity_id`, `billing_currency`, `combine`, `custom_fields`, `purchase_order_number`, `name`, `description` and the invoice-line display flags. A subscription created from a template can now be forward billed. This was not possible while templates had to be `DEFAULT`. The template stays the source of truth for pricing. Solvimon refuses a field outside the set by name, so it never ignores a new field silently.

### Fixes

bug-fix

* Deleting a draft subscription now releases the [coupon](/platform-guides/products-and-pricing/product-catalog/coupons) redemptions that it held. Before this, the redemption stayed behind and pointed at a subscription that no longer existed. The detail screen of the coupon then failed to load.
* A `SELF_BILLING` subscription now shows the customer and the billing entity in the correct positions, as its invoices already did. Editing its details no longer resets the variant to `DEFAULT`.

## Customers

**Only a draft customer can be deleted**
[DELETE /v1/customers/\{id}](https://docs.solvimon.com/api-docs/configuration-api/customers/delete-customers-by-resource-id-or-reference) is now refused unless the [customer](/platform-guides/customers-and-billing/customer) has status `DRAFT`. A customer that has been active can have invoices, subscriptions and events that point at it. Deleting it left those references dangling. To take an active customer out of use, archive it with [POST /v1/customers/\{id}/archive](https://docs.solvimon.com/api-docs/configuration-api/customers/post-customers-by-resource-id-or-reference-archive). The archive keeps its history.

## Customer portal

**Terms and conditions on a checkout page**
A [checkout page](/platform-guides/customers-and-billing/hosted-checkout) portal URL now carries a `note`: up to 2,000 characters of Markdown that the customer sees on the page. Use it for your terms, a link to them, or anything the customer has to read before they subscribe. The field sits next to `success_url` on [POST /v1/portal-urls](https://docs.solvimon.com/api-docs/transaction-api/portal-urls/post-portal-urls) and [PATCH /v1/portal-urls/\{id}](https://docs.solvimon.com/api-docs/transaction-api/portal-urls/patch-portal-urls-by-resource-id). In Desk, you can edit it from the checkout page after you publish, not only in the publish dialog.

**Buy an on-demand item from the portal**
A customer can now buy an [on-demand item](/platform-guides/products-and-pricing/pricing-plans/on-demand-charges) from the [customer portal](/platform-guides/customers-and-billing/customer-portal). They select the item, set a quantity on a [one-off](/platform-guides/products-and-pricing/pricing-plans/one-off-pricing) item priced `FLAT`, see a preview of the cost, and pay. The preview is the same invoice preview that the API returns. The amount that the customer confirms is the amount that Solvimon charges.

**The SEPA direct debit mandate text**
Selecting SEPA direct debit in the [hosted checkout](/platform-guides/customers-and-billing/hosted-checkout) or the [customer portal](/platform-guides/customers-and-billing/customer-portal) now shows the mandate wording. The text names the billing entity that collects the payments. The customer sees what they authorize at the moment they authorize it.

## AI and MCP

**Connect an AI client with your Solvimon login**
An MCP client now signs in with OAuth 2.0 instead of a pasted API key. The Solvimon identity host publishes `/.well-known/oauth-authorization-server` and `/.well-known/jwks.json`. It registers a client through `POST /v1/oauth2/register`, and it runs the authorization code flow with PKCE over `GET /v1/oauth2/authorize` and `POST /v1/oauth2/token`. The browser step lands on a consent screen in Desk. The screen names the application, shows the access that it asks for, and lists the platforms that you are a member of. You select the platforms that the client may reach, then you approve or deny. A client asks for `solvimon:read`, `solvimon:write` or both. A request that names neither is narrowed to `solvimon:read` instead of refused.

To add Solvimon to an AI client, you enter the URL and log in. There is no key in a configuration file and no setup on our side. A token is bound to the platforms you selected and the scope that was granted, so a client you connected for one platform cannot read another. See [Connect with OAuth](/mcp-server/oauth) and the [Claude integration](/integrations/ai-assistants/claude).

## Desk

**Enum and boolean custom fields**
Creating a [custom field](/platform-guides/platform-configuration/custom-fields) in Desk now offers the types that [POST /v1/custom-fields](https://docs.solvimon.com/api-docs/configuration-api/custom-fields/post-custom-fields) already accepted: string, enum and boolean. An enum takes its allowed values in the same form. Before this, Desk created a string field whatever the field was meant to hold.

**Discounts on a one-off invoice line**
Adding a [one-off line](/platform-guides/invoicing/invoices/one-off-invoices) to an invoice now offers to include it in the invoice-level discounts of the subscriptions on that invoice. [POST /v1/invoices/\{id}/add-line](https://docs.solvimon.com/api-docs/transaction-api/invoices/post-invoices-by-resource-id-add-line) gained this last release as `applicable_for_clauses`. The option appears only when the invoice has subscription clauses to apply.

### Fixes

bug-fix

* The status filter on [products](/platform-guides/products-and-pricing/product-catalog) and pricing plans no longer starts on "Deprecated". The default hid the active resources that you were looking for.
* Publishing a [checkout page](/platform-guides/customers-and-billing/hosted-checkout) no longer creates the portal URL when activation failed. The two calls ran without the second waiting on the first.

## Deprecated API fields

The following parameter is now deprecated. It continues to work for backwards compatibility. Migrate to the replacement.

* **`status`** ([GET /v1/files/\{id}/lines](https://docs.solvimon.com/api-docs/event-api/files/get-files-by-resource-id-lines)): use `statuses[]` instead. It takes one or more line statuses in a single request, where `status` takes exactly one.

## September 30, 2026

## Summary

This release makes pricing conditions easier to maintain and makes invoice-level discounts more accurate. The largest change is the meter property variable: a named list of values that you define one time. A pricing condition matches the list with `IN` or `NOT_IN`, so a list of hundreds of countries or SKUs no longer has a copy in each condition.

On invoices, an invoice-level discount can now include one-off lines, and Solvimon applies a fixed-amount discount one time for each invoice, not one time for each billing period. Domestic UK self-billing invoices now contain the tax note that HMRC requires. A wallet grant that Solvimon reverts now shows the reason, so you can tell a removed charge from spent balance.

For sales teams, a quote template now accepts JSON, HTML, Markdown, PPTX and POTX attachments, and a quote can have a name. Products, product items, pricing plans and quote templates now have a guidance field for AI agents, which Solvimon's MCP server gives to an agent together with the catalog. Desk also gets a quantity input for one-off items, lifecycle actions for approval policies, and correct currency balances for wallets funded with an amount.

## Pricing

**A reusable list of values for a pricing condition**
A [meter property](/platform-guides/meter-and-event-design/meter/meter-properties) condition can now refer to a [meter property variable](/platform-guides/meter-and-event-design/meter/meter-property-variables). A variable is a named list of values that belongs to one meter property. The condition uses `meter_property_variable_id` and a comparator of `IN` or `NOT_IN`. It does not repeat the values inline. You therefore define a list of countries, SKUs or merchant ids one time. Each pricing item config that refers to the variable uses the same list.

**`A condition that matches a variable`**

```json A condition that matches a variable
{
  "id": "mprp_5RmXe2QbtKfYuA9DcNH3P",
  "comparator": "IN",
  "meter_property_variable_id": "mpva_2PnBx7VdtRgLmE4WcQJ8K"
}
```

To create a variable, use [POST /v1/meter-property-variables](https://docs.solvimon.com/api-docs/configuration-api/meter-property-variables/post-meter-property-variables). Send `meter_property_id` or `meter_property_reference`, and the initial `values`. A variable can contain a maximum of 10,000 values. To change the list, use [PATCH /v1/meter-property-variables/\{id}/values](https://docs.solvimon.com/api-docs/configuration-api/meter-property-variables/patch-meter-property-variables-by-resource-id-or-reference-values). The request contains an `add` set and a `remove` set. Solvimon applies the two sets as one change:

**`Add two values and remove one`**

```json Add two values and remove one
{
  "add": ["FR", "BE"],
  "remove": ["LU"]
}
```

The response gives the result for each value in `added`, `removed` and `skipped`. Solvimon skips a value in `add` that is already in the list, and a value in `remove` that is not in the list. You can therefore send the same request two times and get the same list. [GET /v1/meter-property-variables/\{id}/values](https://docs.solvimon.com/api-docs/configuration-api/meter-property-variables/get-meter-property-variables-by-resource-id-or-reference-values) returns the list in pages. Each value has an `added_at` timestamp. The variable shows `value_count` and does not include the values. [GET /v1/meter-property-variables](https://docs.solvimon.com/api-docs/configuration-api/meter-property-variables/get-meter-property-variables) therefore stays short when a list is long. You can [expand](/platform-guides/for-developers/expanding-responses) `meter_property_variable_id` on a condition into `meter_property_variable`.

A change to the list changes which future events match. Solvimon does not reprocess events that it priced before the change. See [Event reprocessing](/platform-guides/meter-and-event-design/event-reprocessing). Solvimon rejects [DELETE /v1/meter-property-variables/\{id}](https://docs.solvimon.com/api-docs/configuration-api/meter-property-variables/delete-meter-property-variables-by-resource-id-or-reference) while a condition refers to the variable.

**A quantity for a one-off item in the hosted checkout**
A [one-off](/platform-guides/products-and-pricing/pricing-plans/one-off-pricing#a-unit-price-and-a-quantity) item priced `FLAT` bills a unit price multiplied by a quantity. The schedule customization of the hosted pages now accepts `units`. This is the same quantity for each pricing item config that a pricing plan schedule already accepted. Send it in [POST /v1/pricing-plan-subscriptions/init](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-subscriptions/post-pricing-plan-subscriptions-init). A customer who buys through the [hosted checkout](/platform-guides/customers-and-billing/hosted-checkout) can therefore order three units. [POST /v1/invoices/preview](https://docs.solvimon.com/api-docs/transaction-api/invoices/post-invoices-preview) shows the price of the three units before the customer confirms. Each entry contains a `pricing_item_config_id` and a `number`. If you omit a config, Solvimon uses its `default_units`. If `default_units` is not set, Solvimon uses 1.

### Fixes

bug-fix

* When you override one pricing on a [schedule](/platform-guides/customers-and-billing/subscriptions/schedule-configurations), you no longer have to send the discounts, commitments and markups that you did not change. [PATCH /v1/pricing-plan-schedules/\{id}](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-schedules/patch-pricing-plan-schedules-by-resource-id) validated the clauses against the request body, not against the schedule. An override alone therefore failed with `discount applies to <pricing> but override for <pricing> is updated or removed`. Solvimon now validates against the schedule.
* [GET /v1/pricing-plan-subscriptions/\{id}](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-subscriptions/get-pricing-plan-subscriptions-by-resource-id) now [expands](/platform-guides/for-developers/expanding-responses) `pricing_plan_version_id` for a subscription on a [rate card](/platform-guides/products-and-pricing/rate-cards) pricing plan. Before, the expansion returned no data for this plan type.

## Invoices

**An invoice-level discount applies to one-off lines, one time for each invoice**
A one-off line that you add with [POST /v1/invoices/\{id}/lines](https://docs.solvimon.com/api-docs/transaction-api/invoices/post-invoices-by-resource-id-lines) can now use the invoice-level [discounts](/platform-guides/customers-and-billing/subscriptions/schedule-configurations#discounts) of the subscriptions on that invoice. Set `applicable_for_clauses` to `["discount.totals"]`. The line then counts towards the discount, and Solvimon applies the discount to the line. If you omit the field, the discounts do not include the line, as before. Use the field only for a one-off line on a regular invoice. A [one-off invoice](/platform-guides/invoicing/invoices/one-off-invoices) has no subscription clauses.

Solvimon now also applies a fixed-amount invoice-level discount one time for each invoice. Before, an invoice with a period billed in advance and a period billed in arrears got the full amount for each period. A 50 EUR discount therefore decreased the invoice by 100 EUR.

**Domestic UK self-billing invoices contain the HMRC tax note**
HMRC self-billing rules require a domestic UK self-billed VAT invoice to state whose output tax the VAT is. Solvimon now adds the [tax note](/platform-guides/invoicing/tax#tax-notes) `SELF_BILLING_UK_DOMESTIC` to a self-billing invoice when the two parties are in the United Kingdom. The text of the note is "The VAT shown is your output tax due to HMRC". Other self-billing invoices and other countries do not change.

**A future invoice can take its date from finalisation**
A subscription can start in the future and have its first invoice finalised in advance. Before, the invoice date was the start date of the subscription, also when the [invoice date setting](/platform-guides/invoicing/invoices/invoice-date-settings) was `WHEN_INVOICE_GOES_TO_FINAL`. The invoice date is now the date on which Solvimon finalised the invoice. Your invoice numbers and your reporting periods therefore agree with the issue date. To change the setting through the API, use [PATCH /v1/platforms/\{id}](https://docs.solvimon.com/api-docs/configuration-api/platforms/patch-platforms-by-resource-id).

### Fixes

bug-fix

* Solvimon now validates an [invoice number prefix](/platform-guides/platform-configuration/customising-invoices#setting-up-invoice-numbering-and-prefix) that refers to a [custom field](/platform-guides/platform-configuration/custom-fields) when you save the configuration. The prefix supports `{{customer.custom_fields[reference]}}` and `{{billing_entity.custom_fields[reference]}}`. Before, Solvimon resolved these references for the first time at finalisation. Solvimon now also prevents the rename or removal of a custom field that a prefix uses.
* The self-billing invoice number settings now have a default. [GET /v1/platforms/\{id}](https://docs.solvimon.com/api-docs/configuration-api/platforms/get-platforms-by-resource-id) returns the default for `self_billing_invoice_number_configuration` and `self_billing_credit_invoice_number_configuration` when you did not set them.
* The [invoice PDF](/platform-guides/invoicing/invoices/invoice-operations#downloading-the-invoice-pdf) from [GET /v1/invoices/\{id}/pdf](https://docs.solvimon.com/api-docs/transaction-api/invoices/get-invoices-by-resource-id-pdf) now shows the invoice number in its title, not the resource id. The tax details are now below the price details. The body text and the headings are larger. Bold text no longer breaks in the middle of a word.

## Wallets and credits

**A reverted grant shows the reason for the revert**
A charge can issue a [wallet grant](/platform-guides/wallets-credits/credits-in-pricing-plans#grant-fields-in-the-api). Solvimon reverts the grant when the charge no longer exists, for example when you shorten a schedule and Solvimon no longer bills its period. The grant now contains `reverted_at` and `revert_reason`, and the customer can no longer spend it. Read the two fields with [GET /v1/wallet-grants/\{id}](https://docs.solvimon.com/api-docs/transaction-api/wallet-grants/get-wallet-grants-by-resource-id).

`revert_reason` has one of these values: `INVOICE_VOIDED`, `SCHEDULE_DATES_CHANGED`, `SCHEDULE_DELETED`, `SUBSCRIPTION_CANCELLED`, `SUBSCRIPTION_UPGRADED`, `SUBSCRIPTION_VOIDED`, `SUBSCRIPTION_CANCEL_UNDONE` or `WALLET_GRANT_REVERTED`. A balance can decrease because the customer spent it, or because Solvimon removed the charge. The two fields identify the second cause. Before this release, a grant without a charge stayed funded and the customer could spend it. Grants that Solvimon reverted before this release have neither field.

## Quotes

**More file types on a quote template**
A [quote template](/platform-guides/customers-and-billing/quoting/quote-templates#attachments) attachment now accepts JSON, HTML, Markdown, PPTX and POTX files in addition to PDF. A quote that inherits the attachment accepts the same types. Your sales team can therefore send a presentation or a structured appendix through the quote portal. [POST /v1/documents](https://docs.solvimon.com/api-docs/configuration-api/documents/post-documents) accepts these `content_type` values:

| File type | `content_type`                                                              | Maximum size |
| --------- | --------------------------------------------------------------------------- | ------------ |
| JSON      | `application/json`                                                          | 10 MB        |
| HTML      | `text/html`                                                                 | 10 MB        |
| Markdown  | `text/markdown`                                                             | 10 MB        |
| PPTX      | `application/vnd.openxmlformats-officedocument.presentationml.presentation` | 25 MB        |
| POTX      | `application/vnd.openxmlformats-officedocument.presentationml.template`     | 25 MB        |

Solvimon accepts `text/plain` only when the document name ends in `.md` or `.markdown`, because editors frequently send that type for Markdown. Solvimon validates the content against the declared type, and rejects a macro-enabled PowerPoint file.

**A name on a quote**
[POST /v1/quotes](https://docs.solvimon.com/api-docs/configuration-api/quotes/post-quotes) now accepts a `name`, as a subscription already does. A sales team with more than one [quote](/platform-guides/customers-and-billing/quoting) for one customer can give each quote a name. The team does not have to identify the quotes by resource id.

## AI and MCP

**Guidance for AI agents on a catalog resource**
Products, product items, pricing plans and quote templates now have an `agent_context` object with a `guidance` field of a maximum of 2,500 characters. The field contains free text for an AI agent, not for a customer: when to select this plan, what the item includes, and which deals it fits. Solvimon's [MCP server](/integrations/ai-assistants/claude) gives this text to agents together with the catalog metadata. An agent that selects a template or a plan therefore reads your intent and does not guess from names. Set the field with a `PATCH` on the resource, for example [PATCH /v1/pricing-plans/\{id}](https://docs.solvimon.com/api-docs/configuration-api/pricing-plans/patch-pricing-plans-by-resource-id-or-reference):

**`Guidance on a pricing plan`**

```json Guidance on a pricing plan
{
  "agent_context": {
    "guidance": "Mid-market plan, 50 to 500 seats. Pick this over Starter once a prospect asks for SSO or a custom contract."
  }
}
```

The same object applies to [PATCH /v1/products/\{id}](https://docs.solvimon.com/api-docs/configuration-api/products/patch-products-by-resource-id-or-reference), [PATCH /v1/product-items/\{id}](https://docs.solvimon.com/api-docs/configuration-api/product-items/patch-product-items-by-resource-id-or-reference) and [PATCH /v1/quote-templates/\{id}](https://docs.solvimon.com/api-docs/configuration-api/quote-templates/patch-quote-templates-by-resource-id-or-reference). Desk has an input for the guidance on each of these resources. See [Agent context](/mcp-server/agent-context).

## Event ingestion

### Fixes

bug-fix

* [POST /v1/ingest/meter-data/\{id}/delete](https://docs.solvimon.com/api-docs/event-api/ingest/post-ingest-meter-data-by-resource-id-or-reference-delete) now returns `deleted: true`. Before, Solvimon deleted the event but returned the event as it was before the update. The response therefore showed `false`, while the next [GET /v1/ingest/meter-data/\{id}](https://docs.solvimon.com/api-docs/event-api/ingest/get-ingest-meter-data-by-resource-id-or-reference) showed `true`. See [Event ingestion API](/platform-guides/meter-and-event-design/usage-events/event-ingestion-api).

## Desk

**Desk shows a wallet funded with an amount as a currency balance**
Desk now shows a `CREDIT` [wallet type](/platform-guides/wallets-credits/credit-types-wallets#wallet-types) that is funded with an amount as a currency balance. This applies to the balance, the [top-up](/platform-guides/wallets-credits/wallet-top-ups) and the [deduction on the invoice line](/platform-guides/wallets-credits/credit-types-wallets#an-amount-denominated-wallet-pays-for-the-invoice). More than one wallet can pay an invoice line, so each line lists the contribution of each wallet. Before, Desk showed each wallet as a quantity of credits.

**Manage the lifecycle of an approval policy**
You can now create an [approval policy](/platform-guides/customers-and-billing/quoting/approval-policy) as a draft and activate it when it is ready. You can deprecate or archive a policy that you no longer need. The actions are in the same context menu as for the other catalog resources. They use [POST /v1/approval-policies/\{id}/activate](https://docs.solvimon.com/api-docs/configuration-api/approval-policy/post-approval-policies-by-resource-id-activate), [POST /v1/approval-policies/\{id}/deprecate](https://docs.solvimon.com/api-docs/configuration-api/approval-policy/post-approval-policies-by-resource-id-deprecate) and [POST /v1/approval-policies/\{id}/archive](https://docs.solvimon.com/api-docs/configuration-api/approval-policy/post-approval-policies-by-resource-id-archive). You therefore do not have to edit a policy in place while it approves live traffic.

**Refresh the custom fields on a final invoice**
The refresh menu on a `FINAL` invoice now has the option "Refresh custom fields", next to customer information, PO number and payment acceptors. Use it when a [custom field](/platform-guides/platform-configuration/custom-fields#show-on-invoice) with `copy_value_to_invoice` changed after you issued the invoice. Desk then copies the new value to the invoice. The API equivalent is [POST /v1/invoices/\{id}/refresh](https://docs.solvimon.com/api-docs/transaction-api/invoices/post-invoices-by-resource-id-refresh) with `CUSTOM_FIELD` in `types`. See [Refreshing invoice data](/platform-guides/invoicing/invoice-corrections#refreshing-invoice-data).

**A quantity for a one-off flat item**
The subscription and quote screens now have a quantity input for a [one-off](/platform-guides/products-and-pricing/pricing-plans/one-off-pricing#a-unit-price-and-a-quantity) item priced `FLAT`. Set the quantity when you add the item.

### Fixes

bug-fix

* The audit log now shows the name of the user who made a change, not the user id. The same records are available from [GET /v1/audit-records](https://docs.solvimon.com/api-docs/identity-api/audit-records/get-audit-records).
* The event filter now selects a meter by its reference, not by its name. [Event ingestion](/platform-guides/meter-and-event-design/usage-events/event-ingestion-api) also uses the reference.
* The [events](/platform-guides/meter-and-event-design/usage-events) screen now separates the table from the detail panel. The filter no longer shows two clear buttons.

## September 23, 2026

## Summary

A wallet denominated in a currency now pays for the invoice. Solvimon deducts the money from the taxable base of the lines the wallet covers, so no amount is taxed twice. You can now require approval before a pricing plan schedule becomes active. A coupon can now continue on the next schedule of a subscription. You can now backdate a discount to billing periods whose invoices are still draft. Solvimon now bills AvaTax partial exemptions as partial. You can now manage pricing plan groups in Desk. The customer portal is now available in German and Italian. Solvimon now disables a webhook after 20 consecutive failed deliveries. Desk now shows workflow executions on the customer and the invoice they ran for.

## Pricing

**Activating a pricing plan schedule can require approval**
An [approval policy](/platform-guides/customers-and-billing/quoting/approval-policy) now accepts `PRICING_PLAN_SCHEDULE` as its source resource type. When such a policy applies, [POST /v1/pricing-plan-schedules/\{id}/activate](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-schedules/post-pricing-plan-schedules-by-resource-id-activate) does not activate the schedule. It creates an approval request, sets the schedule's `approval_status` to `PENDING_REVIEW`, and returns the schedule in `DRAFT`. The schedule becomes active when an approver approves the request. A user who prepares a change can therefore ask for its release without the permission to release it.

Approval uses the same endpoints as quotes and invoices: [POST /v1/approval-requests/\{id}/approve](https://docs.solvimon.com/api-docs/configuration-api/approval-request/post-approval-requests-by-resource-id-approve), [POST /v1/approval-requests/\{id}/decline](https://docs.solvimon.com/api-docs/configuration-api/approval-request/post-approval-requests-by-resource-id-decline), and [POST /v1/approval-requests/\{id}/cancel](https://docs.solvimon.com/api-docs/configuration-api/approval-request/post-approval-requests-by-resource-id-cancel). The same approver models apply: by group or ranked. A renegotiation usually changes more than one schedule. The three actions are therefore also available in bulk: [POST /v1/approval-requests/approve](https://docs.solvimon.com/api-docs/configuration-api/approval-request/post-approval-requests-approve), [POST /v1/approval-requests/decline](https://docs.solvimon.com/api-docs/configuration-api/approval-request/post-approval-requests-decline), and [POST /v1/approval-requests/cancel](https://docs.solvimon.com/api-docs/configuration-api/approval-request/post-approval-requests-cancel).

**`Approve every schedule of a change in one request`**

```bash Approve every schedule of a change in one request
curl -X POST https://test.api.solvimon.com/v1/approval-requests/approve \
  -H "X-API-KEY: <apiKey>" \
  -H "Content-Type: application/json" \
  -d '{
    "resource_ids": ["appr_jwDeeN0tYSY3F7BkeN1v", "appr_QwDeeN0vcYJaBLAc0C29"]
  }'
```

Solvimon acts on each request separately. The response contains one result for each resource id. Each result has a `status` of `SUCCEEDED` or `FAILED`. A failed result also contains the error that stopped it. One failed request does not roll back the others.

To find the schedules that wait for review, read the subscription with [GET /v1/pricing-plan-subscriptions/\{id}](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-subscriptions/get-pricing-plan-subscriptions-by-resource-id). `pending_review_pricing_plan_schedule_ids` lists the schedules of the subscription that are pending review, in `start_at` order. `pricing_plan_schedule_infos` is unchanged: it lists only the active schedules. [GET /v1/approval-requests](https://docs.solvimon.com/api-docs/configuration-api/approval-request/get-approval-requests) now accepts `source_ids[]` for several sources in one request, in addition to `source_id`. Solvimon rejects a request that sends both.

A platform can have one active approval policy for each source type. These policies contain a single always-approve rule, and a second policy would repeat it. Solvimon also approves schedules in timeline order. You cannot activate a schedule while the schedule before it is still draft or pending review. See [Requiring approval to activate](/platform-guides/customers-and-billing/subscriptions/draft-schedules#requiring-approval-to-activate).

**A coupon can carry over to the next schedule**
You redeem a coupon against a pricing plan schedule, so its discounts belong to that schedule. [POST /v1/pricing-plan-schedules/\{id}/migrate](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-schedules/post-pricing-plan-schedules-by-resource-id-migrate) and [POST /v1/pricing-plan-schedules/\{id}/copy](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-schedules/post-pricing-plan-schedules-by-resource-id-copy) now accept `coupon_discount_behaviour`. This field sets what happens to the coupons that are still active when you replace the schedule:

**`Continue the active coupons on the new schedule`**

```json Continue the active coupons on the new schedule
{
  "pricing_plan_version_id": "ppve_QwDeeN0vcYJaBLAc0C29",
  "start_at": "2026-10-01T00:00:00+02:00",
  "coupon_discount_behaviour": "CARRY_OVER"
}
```

`NONE` is the default. The discounts end with their schedule, as before. `CARRY_OVER` continues them on the new schedule for the remaining time of the coupon. Solvimon calculates the end date from the original start of the coupon. A three-month coupon that has run for two months therefore continues for one month on the new schedule. Solvimon does not redeem the coupon a second time. See [Coupons across schedule changes](/platform-guides/products-and-pricing/product-catalog/coupons#coupons-across-schedule-changes).

**A discount can be backdated while its invoices are still draft**
A [discount](/platform-guides/customers-and-billing/subscriptions/schedule-configurations#discounts) can now start in any billing period whose invoice is not issued. The lock on billed periods now follows the last `FINAL` invoice. An invoice that is still `DRAFT`, for example on hold and not sent, therefore leaves its period open. You can add an ad hoc discount that you agreed on before the invoice goes out. A period with a `FINAL` invoice stays closed. The same rule applies when you remove a discount clause that has already started. See [Invoice lifecycle](/platform-guides/invoicing/invoices/invoice-lifecycle).

**An on-demand charge started from a payment carries its quantity**
When you [charge an on-demand item](/platform-guides/products-and-pricing/pricing-plans/on-demand-charges) through [POST /v1/payments/authorize](https://docs.solvimon.com/api-docs/transaction-api/payments/post-payments-authorize), you now state the quantity and the amount in the payment context. Each item accepts the same two optional fields as the direct request: `units` and `flexible_amount`. The flow can therefore charge a [one-off](/platform-guides/products-and-pricing/pricing-plans/one-off-pricing) item priced `FLAT` for the units ordered, and a [flexible](/platform-guides/products-and-pricing/pricing-plans/flexible-pricing) item for the agreed amount. `FIXED` items work as before.

**`Pricing items on the payment context`**

```json Pricing items on the payment context
{
  "pricing_items": [
    { "pricing_item_id": "prii_5RmXe2QbtKfYuA9DcNH3P", "units": { "number": "2" } },
    { "pricing_item_id": "prii_2PnBx7VdtRgLmE4WcQJ8K", "flexible_amount": { "quantity": "50.00", "currency": "EUR" } }
  ]
}
```

Solvimon validates both fields when you create the payment context. An incorrect quantity therefore returns an error to the caller before the customer pays. Solvimon still reads payment contexts stored in the earlier shape.

## Wallets and credits

**A wallet denominated in a currency pays for the invoice**
A `CREDIT` [wallet type](/platform-guides/wallets-credits/credit-types-wallets#wallet-types) funded with an amount holds money, not credits. Such a wallet now pays for the invoice lines it is allowed to cover. Solvimon invoices the customer for the remainder.

The wallet holds a net amount, because Solvimon already charged tax on the [top-up](/platform-guides/wallets-credits/wallet-top-ups) that funded it. Solvimon therefore deducts the money from the net amount of the line, and the covered part has no tax. Solvimon charges and taxes the remainder of the line as usual. For example, a 30.00 EUR line at 21% VAT against a 10.00 EUR balance invoices 20.00 EUR plus 4.20 EUR VAT:

|                         | Net    | VAT  | Total  |
| ----------------------- | ------ | ---- | ------ |
| Line as priced          | 30.00  | 6.30 | 36.30  |
| Covered from the wallet | -10.00 | 0.00 | -10.00 |
| Charged                 | 20.00  | 4.20 | 24.20  |

Solvimon always splits the coverage and never skips it. If the balance is less than the line, the wallet covers part of the line and Solvimon charges the remainder. Only lines whose product [tax category](/platform-guides/invoicing/tax#tax-categories) matches the tax category of the wallet type are in scope. Other lines do not change. Solvimon applies the deduction last: after included volume, credits, discounts, and minimum commitment, and before tax. The deduction applies to the net amount that remains. A customer with both types of wallet spends credits first, because credits reduce volume and the money wallet reduces the amount.

Each charge reports what each wallet paid in a new `wallet_balances` list. Each entry names the wallet and gives its figures:

**`What one wallet paid for a charge`**

```json What one wallet paid for a charge
"wallet_balances": [
  {
    "wallet_id": "wall_5RmXe2QbtKfYuA9DcNH3P",
    "used_balance": { "amount": { "quantity": "10.00", "currency": "EUR" } },
    "left_balance": { "amount": { "quantity": "0.00", "currency": "EUR" } },
    "available_balance": { "amount": { "quantity": "10.00", "currency": "EUR" } }
  }
]
```

The list has one entry for each wallet that paid. A charge that a credits wallet and a money wallet both paid therefore reports the figures of each wallet separately. `wallet_balances` replaces `wallet_balance_details`, which holds one wallet at a time and only credits. See "Deprecated API fields" below, and [An amount-denominated wallet pays for the invoice](/platform-guides/wallets-credits/credit-types-wallets#an-amount-denominated-wallet-pays-for-the-invoice).

A [grant](/platform-guides/wallets-credits/credits-in-pricing-plans) into such a wallet must be in the currency of the wallet. Solvimon rejects a fixed grant in a different currency with `wallet type <reference> is denominated in EUR, but the grant is in USD`. Solvimon also rejects a grant that takes a percentage of pricing in a different currency, because there is no conversion. The error tells you to use a fixed grant or a different wallet type. A wallet type created before the default currency became mandatory has no default currency. You cannot grant into it until you set one.

## Customers

**A live seat assignment protects the resources it references**
[Seat assignments](/platform-guides/customers-and-billing/customer/seat-assignments) record which child customer occupies which seat. Solvimon attributes per-seat credits against them. Solvimon now protects three resources while a live assignment references them: the child customer in the seat, the parent customer that holds the seat, and the per-seat product item that defines the seat type. For example, Solvimon rejects [POST /v1/customers/\{id}/archive](https://docs.solvimon.com/api-docs/configuration-api/customers/post-customers-by-resource-id-or-reference-archive) for a child customer who still holds a live seat. A seat therefore cannot stay occupied by a customer that is no longer operational. The error names the assignment that blocks the change, and its status. First end the assignment: set `end_at` with [PATCH /v1/seat-assignments/\{id}](https://docs.solvimon.com/api-docs/configuration-api/seat-assignments/patch-seat-assignments-by-resource-id). Then change the status. See [Seat-based credits](/platform-guides/wallets-credits/seat-based-credits).

### Fixes

bug-fix

* You can now remove a billing email. [PATCH /v1/customers/\{id}](https://docs.solvimon.com/api-docs/configuration-api/customers/patch-customers-by-resource-id-or-reference) with `{"email": null}` used to return `INVALID_FIELD`, although an empty string cleared the email. `null`, `""`, and whitespace now all mean no email.

## Invoices

**AvaTax partial exemptions are billed as partial**
Some jurisdictions tax part of a line and exempt the remainder. Software in Texas is a common example: 8.25% applies to 80% of the base, and the other 20% is exempt. Solvimon now stores the taxable fraction that AvaTax returns on the rate, and applies it. Solvimon calculates the rates on the taxable part of the base. The exempt remainder becomes a single 0% summary. The invoice and the tax filing therefore both show the split. The Texas example produces four tax lines: three on the 80% base, and one exempt line for the 20%. The invoice shows the exempt part once, not once per jurisdiction. A fully taxable or fully exempt line does not change. An invoice that is already issued keeps its tax. To apply the split to it, refresh it with [POST /v1/invoices/\{id}/refresh](https://docs.solvimon.com/api-docs/transaction-api/invoices/post-invoices-by-resource-id-refresh). See [Tax calculation and integrations](/platform-guides/invoicing/tax/tax-calculation-integrations).

### Fixes

bug-fix

* [Custom fields](/platform-guides/platform-configuration/custom-fields) now persist after an invoice refresh. A `CUSTOM_FIELD` refresh with [POST /v1/invoices/\{id}/refresh](https://docs.solvimon.com/api-docs/transaction-api/invoices/post-invoices-by-resource-id-refresh) used to update the "show on invoice" fields in the response and in the regenerated PDF. It did not write them to the invoice. The next `GET` returned the values from before the refresh.

## Workflows

**Filter and expand the resources a workflow ran against**
[POST /v1/workflow-action-executions/search](https://docs.solvimon.com/api-docs/transaction-api/workflow-action-executions/post-workflow-action-executions-search) now filters on `pricing_plan_subscription_id` and `wallet_id`. The existing invoice, customer, payment, action, workflow, trigger, and status filters still apply. [Workflow triggers](https://docs.solvimon.com/api-docs/transaction-api/workflow-triggers/get-workflow-triggers-by-resource-id) now [expand](/platform-guides/for-developers/expanding-responses) the resources they reference: `workflow_id` on a workflow trigger, `pricing_plan_subscription_id` and `pricing_plan_schedule_id` on a subscription trigger, `wallet_id` on a wallet trigger, and `payment_id` on a payment trigger. An execution row expands its `workflow_action_id`. You can therefore read the [execution log](/platform-guides/workflows/monitoring-and-audit) in one call, with the resources of each row inline.

**A condition on the billing entity of the invoice**
On invoice workflows, a [workflow condition](/platform-guides/workflows/workflow-triggers) can now test `invoice.billing_entity.id`. A platform that bills from several [billing entities](/platform-guides/platform-configuration/billing-entities) can give each entity its own automation. Before this, you had to run one workflow and filter afterwards.

**Workflow executions are shown where the work happened**
Desk now shows the execution table on the customer screen, on the invoice screen, and on the workflow. Each table lists the executions for that resource. To see what the automation did for an invoice, open the invoice. Select a row to open a panel with the details of that execution. The full log stays on its own page, "All workflow executions". See [Monitoring and audit](/platform-guides/workflows/monitoring-and-audit).

### Fixes

bug-fix

* An `EXPORT_TO_ERP` [action](/platform-guides/workflows/workflow-actions) now requires an integration. You could leave the field empty, and Desk saved an action that had no integration to export through.

## Integrations

**Solvimon disables a webhook that never succeeds**
Solvimon now disables a webhook automatically after 20 consecutive failed deliveries. `disabled_reason` on the [webhook](https://docs.solvimon.com/api-docs/configuration-api/webhooks/get-webhooks-by-resource-id) gives the reason, so you can tell a webhook that Solvimon disabled from one that you disabled. An endpoint that is permanently unavailable therefore leaves the delivery queue. Before this, Solvimon retried it without end, on a backoff of up to several hours. While a webhook is disabled this way, you cannot [send it a test event](https://docs.solvimon.com/api-docs/configuration-api/webhooks/post-webhooks-by-resource-id-send-test) or [resend its penalty box payload](https://docs.solvimon.com/api-docs/configuration-api/webhooks/post-webhooks-by-resource-id-send-penalty-box-payload). Reactivate the webhook with [PATCH /v1/webhooks/\{id}](https://docs.solvimon.com/api-docs/configuration-api/webhooks/patch-webhooks-by-resource-id) first. Reactivation also clears the reason. See [Webhooks](/platform-guides/for-developers/webhooks).

## Customer portal

**German and Italian**
The [customer portal](/platform-guides/customers-and-billing/customer-portal) and [hosted checkout](/platform-guides/customers-and-billing/hosted-checkout) are now available in German and Italian, in addition to English and Dutch. The language switcher is now a standard part of both. You do not have to enable it. The starting language is the selection the visitor made. Without a selection, it is the [locale](/platform-guides/for-developers/locale) on the customer record. Geolocation is the last fallback, because the location of the browser does not identify the customer. A regional variant falls back to its language: `de-AT` shows German and `nl-BE` shows Dutch.

**The pay-invoice page uses the checkout components**
Paying an invoice from the [customer portal](/platform-guides/customers-and-billing/customer-portal) and paying through the [hosted checkout](/platform-guides/customers-and-billing/hosted-checkout) now use the same components. A payment therefore behaves the same from both starting points. Solvimon now passes the invoice amount to the payment method options. The page therefore offers only the methods that can settle the currency of the invoice.

## Payments

**Restrict the payment methods an Adyen acceptor offers**
`allowed_payment_methods` on a [payment acceptor](https://docs.solvimon.com/api-docs/configuration-api/payment-acceptors/patch-payment-acceptors-by-resource-id) of type `PAYMENT_GATEWAY` on [Adyen](/integrations/paymentserviceproviders/adyen) limits the methods it offers to a set you select. For example, one [billing entity](/platform-guides/platform-configuration/billing-entities) can accept cards, and a second can accept cards and direct debit. Desk lists the methods enabled on the selected Adyen integration, for integrations that Solvimon owns. If you leave the field empty, the acceptor offers every method that the integration has enabled. See [Payment options](/platform-guides/platform-configuration/payment-options).

## Desk

**Manage pricing plan groups in Desk**
A [pricing plan group](/platform-guides/customers-and-billing/subscriptions/plan-changes) lets a subscription move between related plans. Plan groups are now a tab next to pricing plans. You can create and maintain a group in Desk, without the [pricing plan groups API](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-groups/post-pricing-plan-groups). You create a group with its name, its plans, and its portal settings. Drag the plans to order them, lowest tier first. That order is the upgrade path. Upgrades, downgrades, and cancellations each have their own timing: immediately, immediately with pro-rated fees, from the next billing period, or not allowed. A toggle sets whether customers can move between these plans themselves in the [self-serve portal](/platform-guides/customers-and-billing/self-serve-plan-changes). You deprecate and reactivate groups from the same context menu as the other catalog resources.

### Fixes

bug-fix

* Desk now creates a user with a [role](/platform-guides/platform-configuration/users-permissions/roles-permissions). The form used to allow a user without a role, and that user could not do anything. The form now preselects Read only.
* Clearing the end date of a [schedule](/platform-guides/customers-and-billing/subscriptions/subscription-schedules) now clears it. An absent `end_at` means "keep the end date of the source schedule", and only an explicit `null` means "no end date". Desk used to drop the key when you emptied the field. The schedule therefore kept the old end date, which was usually in the past.
* Extending an active schedule past a [draft schedule](/platform-guides/customers-and-billing/subscriptions/draft-schedules) that follows it no longer fails with `No adjacent schedule found but flag to adjust the NEXT schedule was passed`. The save used to ask to shift the next schedule without a check that this schedule was a draft.
* The customer screen now hides archived payment methods. The list shows only the methods you can charge.
* Revenue items on a pricing plan version now have even spacing. A gap used to appear between two of them.

## Deprecated API fields

The following fields are now deprecated. They continue to work for backwards compatibility. Migrate to the replacements.

* **`wallet_balance_details`** (pricing item summary price details): use `wallet_balances` instead. The block holds one wallet at a time. A charge paid by two wallets therefore reports only the figures of the wallet that paid last. The block can also express only credits.
* **`used_wallet_credits`**, **`left_wallet_credits`**, **`available_wallet_credits`** (pricing item summary wallet balance details): use the `used_balance`, `left_balance`, and `available_balance` of the matching entry in `wallet_balances` instead. These fields carry credits or an amount.

## September 16, 2026

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

## September 9, 2026

## Summary

A wallet denominated in a currency now drives the same automation as one denominated in credits. Auto top-up fires on an amount threshold. Wallet balance workflows accept an amount threshold too. The trigger reports both the balance and the threshold in the wallet's own unit. Wallet grants are now issued per pricing period, not per invoice. A repeated on-demand charge grants its credits per line. A forward-billed grant lands in the wallet of the customer the subscription is for. Seat assignments are manageable from Desk. Calendar billing anchors a multi-month period on the schedule start.

## Pricing

**A clearer error when pass-through pricing meets the wrong meter value**
Pass-through pricing bills the amount carried on the event, so it can only be configured against a meter value of type `AMOUNT`. Any other meter value was rejected with "not compatible with non AMOUNT usage", which named neither the pricing type that refused nor what it needed. The message now reads `PASS_THROUGH pricing requires an AMOUNT meter value`.

**Adding a pricing group no longer fails on the pricing category order**
Adding a pricing group to a pricing plan version could introduce a product category the version did not have yet. If a custom order had been set through "Change pricing order", this failed with `display_order is configured on 3 of the 4 pricing categories, it must be configured on all of them or on none of them`. Desk now assigns a display order to the new category itself, so an explicit order stays complete. See [Pricing groups](/platform-guides/products-and-pricing/pricing-plans/pricing-groups).

### Fixes

bug-fix

* A seats value has to be a number. `default_seats_value` on a pricing item config was accepted as an amount, which is not a seat count anything could multiply by, and is now rejected with `seats value must be a number`. A seat count configured as zero bills no seat line at all, as a zero count already did.

## Wallets and credits

**Auto top-up and balance workflows for wallets denominated in a currency**
A `CREDIT` wallet type funded with an amount keeps its ledger in a currency rather than in a credit type. Invoice usage that takes the open balance below the configured threshold now schedules the auto top-up, and `wallet.wallet_balance.threshold.amount` is accepted on a workflow definition for such a wallet type.

There is no conversion between a credit type and a currency, so a threshold is only valid in the wallet's own unit. An `amount` threshold on a wallet type denominated in credits is rejected with `amount threshold not allowed for CREDITS wallets`. A `credits` threshold on a wallet type denominated in an amount is rejected with `credits threshold not allowed for AMOUNT credit wallets`. An `amount` threshold's currency has to match the wallet type's `credit.amount_grant.default_currency`. `LESS_THAN` remains the only comparator. See [Wallet top-ups](/platform-guides/wallets-credits/wallet-top-ups) and [Credit types & wallets](/platform-guides/wallets-credits/credit-types-wallets).

**`Threshold on a wallet type funded with an amount`**

```json Threshold on a wallet type funded with an amount
{
  "wallet_balance": {
    "wallet_type_id": "wtyp_...",
    "comparator": "LESS_THAN",
    "threshold": { "amount": { "quantity": "500.00", "currency": "EUR" } }
  }
}
```

In Desk, the threshold field now follows the unit of the selected wallet type. It resets when you switch to a wallet type in the other unit, so a figure entered in credits is never resubmitted as an amount. The workflow summary now states the amount with its currency instead of a credit unit name.

**A wallet grant is issued per pricing period, not per invoice**
A `wallet_grants` rule on a pricing item config was issued once per invoice. Now each pricing period produces its own grant, with its own validity window. This matters when the config's `pricing_period` is shorter than its `billing_period`: on a plan billed twice a year, a monthly `expiry_policy` now behaves as configured, instead of covering the whole six-month invoice. See [Credits in pricing plans](/platform-guides/wallets-credits/credits-in-pricing-plans).

**A repeated on-demand item grants its credits per line**
[Charging on demand](/platform-guides/products-and-pricing/pricing-plans/on-demand-charges) accepts the same pricing item more than once in one request. Both lines carry the invoice date and land in the same invoice group. So an item at 100 USD granting a fixed 500 credits, charged twice, takes 200 USD and deposited 1000 credits.

**A forward-billed grant lands in the wallet of the customer the subscription is for**
Under forward billing the invoice is addressed to the parent, but the subscription belongs to the child. A wallet grant from such a subscription now resolves to the subscription's own customer, not the parent. The same holds for forwarded usage: it draws from the subscription's own customer balance. See [Parent-child relationships](/platform-guides/customers-and-billing/customer/parent-child-relationships).

## Customers

**Manage seat assignments from Desk**
[Seat assignments](/platform-guides/customers-and-billing/customer/seat-assignments) record which processing-only child customer occupies which seat, and they are what per-seat credits are attributed against. Until now they existed only on the API. From the context menu of a per-seat pricing item on a subscription, beside "Show seats history", the new panel lists the seats assigned under that parent for that seat type with occupant, window and status, and assigns a child to a seat, ends an assignment, reopens it or deletes it. Only processing-only children of that parent are offered, and each action respects its `SEAT_ASSIGNMENT` permission. See [Seat-based pricing](/platform-guides/products-and-pricing/pricing-plans/per-seat-pricing) and [Seat-based credits](/platform-guides/wallets-credits/seat-based-credits).

## Invoices

### Fixes

bug-fix

* Refunded payments are accounted for in the invoice amounts. An invoice that was paid and then partly refunded could report a negative `open_invoice_amount` and a `paid_invoice_amount` above its own total. A payment pending refund or reversal still counts as paid until the refund settles; a settled refund no longer does.
* `payment_status` is persisted when an invoice is marked as paid by hand. The write that settled the invoice updated the paid flag and the open amount but left the payment status at its previous value, so the invoice read back as paid while still reporting `NOT_PAID` or `PARTIALLY_PAID`. See [Invoice payment status](/platform-guides/invoicing/invoices/invoice-payment-status).
* Voiding a subscription no longer fails when an ingested event fed both a pricing summary and a feature summary. The reprocessing that follows the void removed every link for the event, then looked for the feature summary's link again and raised an error on it. The feature summary is now removed with the rest, so [reprocessing](/platform-guides/meter-and-event-design/event-reprocessing) leaves nothing dangling to duplicate later.

## Payments

**A rate-limited request is retried instead of failing the payment**
A burst of invoice payments can take the payment provider over its rate limit. A `429` response was not in the retry set. It surfaced as an error, even though nothing had actually gone wrong: the payment action was marked `ERROR` and each item raised an alert. `429` is now retried. The fixed half-second wait between retries was also replaced with exponential backoff and jitter, as the provider documents. See [Dunning and payment retries](/platform-guides/invoicing/dunning-and-payment-retries).

## Workflows

**Breaking: a wallet balance trigger reports the balance in the wallet's own unit**
`open_balance` and `threshold` on the `wallet_balance` trigger were a bare credits object, which cannot express the balance of a wallet denominated in a currency. Both are now a container carrying `credits` and `amount`, of which exactly one is populated, decided by the wallet type:

**`Before and after`**

```json Before and after
// was
"wallet_balance": { "open_balance": { "quantity": "40", "credit_type_id": "ctyp_..." } }

// is
"wallet_balance": { "open_balance": { "credits": { "quantity": "40", "credit_type_id": "ctyp_..." } } }
"wallet_balance": { "open_balance": { "amount": { "quantity": "40.50", "currency": "EUR" } } }
```

A consumer reading `open_balance.quantity` has to move to `open_balance.credits.quantity`. The [send-email action](/platform-guides/workflows/workflow-actions) is unaffected: `wallet_balance` and `wallet_balance_threshold` are rendered figures either way, and `credit_unit_name` carries the currency code when the wallet is denominated in an amount. See [Workflow triggers](/platform-guides/workflows/workflow-triggers).

## Desk

**Pick the billing currency when adding pricing to a subscription**
Adding a pricing directly on a subscription pre-selected the platform's default currency and gave no way to change it, so a subscription billed in another currency had to be built elsewhere. The currency is still pre-selected, from the subscription, and is now editable. See [Custom pricing](/platform-guides/customers-and-billing/subscriptions/custom-pricing).

**Event reprocessing moved next to the usage it explains**
The reprocessing log is now a tab under Usage metering rather than a page of its own, so the events and the reprocessing they went through are read in one place.

### Fixes

bug-fix

* Events load on the Events page. Opening the page showed an empty table until a full refresh, and changing the meter in the dropdown did not fetch anything.
* Only active meters are offered in the Events overview selector, so a deprecated or archived meter is no longer something you can select and get nothing back for.
* The "Metric filters" option appears for a usage-based billing item that points at a meter directly, and not only for a saved meter value calculation, when [dynamic meters](/platform-guides/meter-and-event-design/meter/dynamic-meter-creation) are off.
* Report generation requests that are `DONE` are hidden on an invoice, since the report they produced is listed already and showed as a duplicate beside it.
* Inviting a user whose email address differs only in capitalisation from an existing one reports that the user already exists instead of failing on an error.
* Collapsing the sidebar works again, and closing a modal by clicking the overlay no longer discards what you were verifying.
* Status chips follow one set of meanings across Desk and the portal: neutral for metadata such as countries, currencies and versions, and a shared mapping from lifecycle state to neutral, informational, positive, warning or negative.

## September 2, 2026

## Summary

Credit grants on a pricing item can now scale with the number of seats purchased. Seat occupancy is tracked as its own resource through the new seat assignments API, and a wallet can show which customer's usage reserved which part of its balance. Also: a credit-amount wallet's tax category is now always set, workflow emails include the template variables the legacy integration used to inject, and draft workflows can be deleted.

## Pricing

**You can now leave product items unpriced**
One product can back several plans that each charge for a different subset of its items: leave the rest unpriced, and events matching those items produce no charge on that plan. The API always allowed a pricing to skip product items of its product; Desk was what enforced pricing every item. Now you can remove a product item from a pricing on a pricing plan version, a rate card version, or a schedule pricing, which makes [filtered product items](/platform-guides/products-and-pricing/pricing-plans#leaving-a-product-item-unpriced) usable in practice. An unpriced item still renders as an "Unpriced" card with an "Add pricing" action, so leaving one out stays visible rather than silent.

## Wallets and credits

**Credit grants that scale with seats**
A wallet-grant rule on a pricing item config declares what its quantity is derived from through a new `source` field, so a plan sold per seat can hand out credits per seat without a separate rule per customer. `FIXED` is the default and grants the quantity exactly as configured, which is what every existing rule does. `SEAT_POOL` multiplies the configured quantity by the applicable purchased seat count and grants the result into the shared customer wallet as a single grant per rule per billing period, never one grant per seat. The seat count comes from `seats_values` on the pricing plan schedule for that pricing item, falling back to `default_seats_value` on the item config when the schedule sets none.

`SEAT_POOL` is only valid on a `PER_SEAT` pricing item, since seat counts are scoped to a pricing item rather than to the schedule as a whole; the API rejects it elsewhere with a validation error on `wallet_grants`. Issued grants carry the same `source` read-only, so a grant that was seat-scaled is identifiable after the fact. The enum also declares `PER_SEAT`, for credits owned by an individual seat rather than pooled; it is not implemented yet and is rejected with `PER_SEAT is not supported yet`. See [Seat-based credits](/platform-guides/wallets-credits/seat-based-credits) for the full configuration, including how the seat count is resolved and how seats added mid-period are topped up.

In Desk, the grant source is configured on the wallet grant in a pricing, and a seat-scaled grant shows the seat count it resolved against.

**See whose usage reserved a wallet balance**
A shared credit pool spent by several child customers reported one reserved figure with no way to see where it came from. The wallet balance status can now return `customer_details`, a breakdown of the reserved balance per customer, requested with `include_customer_details` on the balance call. Each entry carries `customer_id` and that customer's share of the reserved balance. The split is recorded when an invoice is built or refreshed, so it reflects the usage that has actually been drafted against the wallet.

### Request

POST [https://test.api.solvimon.com/v\{version}/wallets/\{resourceIdOrReference}/balance](https://test.api.solvimon.com/v\{version}/wallets/\{resourceIdOrReference}/balance)

**`Wallets_postWalletsByResourceIdOrReferenceBalance_example`**

```curl Wallets_postWalletsByResourceIdOrReferenceBalance_example
curl -X POST https://test.api.solvimon.com/v1/wallets//balance \
     -H "X-API-KEY: <apiKey>" \
     -H "Content-Type: application/json" \
     -d '{
  "balance_at": "2026-10-09T12:00:00Z"
}'
```

**`Wallets_postWalletsByResourceIdOrReferenceBalance_example`**

```python Wallets_postWalletsByResourceIdOrReferenceBalance_example
import requests

url = "https://test.api.solvimon.com/v1/wallets/:resourceIdOrReference/balance"

payload = { "balance_at": "2026-10-09T12:00:00Z" }
headers = {
    "X-API-KEY": "<apiKey>",
    "Content-Type": "application/json"
}

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

print(response.json())
```

**`Wallets_postWalletsByResourceIdOrReferenceBalance_example`**

```javascript Wallets_postWalletsByResourceIdOrReferenceBalance_example
const url = 'https://test.api.solvimon.com/v1/wallets/:resourceIdOrReference/balance';
const options = {
  method: 'POST',
  headers: {'X-API-KEY': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"balance_at":"2026-10-09T12:00:00Z"}'
};

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

**`Wallets_postWalletsByResourceIdOrReferenceBalance_example`**

```go Wallets_postWalletsByResourceIdOrReferenceBalance_example
package main

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

func main() {

	url := "https://test.api.solvimon.com/v1/wallets/:resourceIdOrReference/balance"

	payload := strings.NewReader("{\n  \"balance_at\": \"2026-10-09T12:00:00Z\"\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))

}
```

**`Wallets_postWalletsByResourceIdOrReferenceBalance_example`**

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

url = URI("https://test.api.solvimon.com/v1/wallets/:resourceIdOrReference/balance")

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

request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"balance_at\": \"2026-10-09T12:00:00Z\"\n}"

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

**`Wallets_postWalletsByResourceIdOrReferenceBalance_example`**

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

HttpResponse<String> response = Unirest.post("https://test.api.solvimon.com/v1/wallets/:resourceIdOrReference/balance")
  .header("X-API-KEY", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"balance_at\": \"2026-10-09T12:00:00Z\"\n}")
  .asString();
```

**`Wallets_postWalletsByResourceIdOrReferenceBalance_example`**

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://test.api.solvimon.com/v1/wallets/:resourceIdOrReference/balance', [
  'body' => '{
  "balance_at": "2026-10-09T12:00:00Z"
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-API-KEY' => '<apiKey>',
  ],
]);

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

**`Wallets_postWalletsByResourceIdOrReferenceBalance_example`**

```csharp Wallets_postWalletsByResourceIdOrReferenceBalance_example
using RestSharp;

var client = new RestClient("https://test.api.solvimon.com/v1/wallets/:resourceIdOrReference/balance");
var request = new RestRequest(Method.POST);
request.AddHeader("X-API-KEY", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"balance_at\": \"2026-10-09T12:00:00Z\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Wallets_postWalletsByResourceIdOrReferenceBalance_example`**

```swift Wallets_postWalletsByResourceIdOrReferenceBalance_example
import Foundation

let headers = [
  "X-API-KEY": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = ["balance_at": "2026-10-09T12:00:00Z"] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://test.api.solvimon.com/v1/wallets/:resourceIdOrReference/balance")! 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()
```

### Response (200)

```json
{
  "balance": {
    "credits": {
      "quantity": "string",
      "credit_type_id": "string",
      "credit_type": {
        "object_type": "string",
        "id": "string",
        "reference": "string",
        "status": "DRAFT",
        "name": "string",
        "description": "string",
        "unit_name": {
          "singular": "string",
          "plural": "string"
        },
        "created_at": "string",
        "updated_at": "string"
      }
    },
    "amount": {
      "quantity": "string",
      "currency": "AED"
    }
  },
  "reserved_balance": {
    "credits": {
      "quantity": "string",
      "credit_type_id": "string",
      "credit_type": {
        "object_type": "string",
        "id": "string",
        "reference": "string",
        "status": "DRAFT",
        "name": "string",
        "description": "string",
        "unit_name": {
          "singular": "string",
          "plural": "string"
        },
        "created_at": "string",
        "updated_at": "string"
      }
    },
    "amount": {
      "quantity": "string",
      "currency": "AED"
    }
  },
  "open_balance": {
    "credits": {
      "quantity": "string",
      "credit_type_id": "string",
      "credit_type": {
        "object_type": "string",
        "id": "string",
        "reference": "string",
        "status": "DRAFT",
        "name": "string",
        "description": "string",
        "unit_name": {
          "singular": "string",
          "plural": "string"
        },
        "created_at": "string",
        "updated_at": "string"
      }
    },
    "amount": {
      "quantity": "string",
      "currency": "AED"
    }
  },
  "balance_at": "string",
  "over_granted": {
    "credits": {
      "quantity": "string",
      "credit_type_id": "string",
      "credit_type": {
        "object_type": "string",
        "id": "string",
        "reference": "string",
        "status": "DRAFT",
        "name": "string",
        "description": "string",
        "unit_name": {
          "singular": "string",
          "plural": "string"
        },
        "created_at": "string",
        "updated_at": "string"
      }
    },
    "amount": {
      "quantity": "string",
      "currency": "AED"
    }
  },
  "customer_details": [
    {
      "customer_id": "string",
      "reserved_balance": {
        "credits": {
          "quantity": "string",
          "credit_type_id": "string",
          "credit_type": {
            "object_type": "string",
            "id": "string",
            "reference": "string",
            "status": "DRAFT",
            "name": "string",
            "description": "string",
            "unit_name": {
              "singular": "string",
              "plural": "string"
            },
            "created_at": "string",
            "updated_at": "string"
          }
        },
        "amount": {
          "quantity": "string",
          "currency": "AED"
        }
      },
      "balance": {
        "credits": {
          "quantity": "string",
          "credit_type_id": "string",
          "credit_type": {
            "object_type": "string",
            "id": "string",
            "reference": "string",
            "status": "DRAFT",
            "name": "string",
            "description": "string",
            "unit_name": {
              "singular": "string",
              "plural": "string"
            },
            "created_at": "string",
            "updated_at": "string"
          }
        },
        "amount": {
          "quantity": "string",
          "currency": "AED"
        }
      },
      "over_granted": {
        "credits": {
          "quantity": "string",
          "credit_type_id": "string",
          "credit_type": {
            "object_type": "string",
            "id": "string",
            "reference": "string",
            "status": "DRAFT",
            "name": "string",
            "description": "string",
            "unit_name": {
              "singular": "string",
              "plural": "string"
            },
            "created_at": "string",
            "updated_at": "string"
          }
        },
        "amount": {
          "quantity": "string",
          "currency": "AED"
        }
      }
    }
  ]
}
```

**A credit-amount wallet type's tax category is enforced**
A `CREDIT` wallet type funded with an amount carries a tax category and a default currency. The tax category was required at create time but only stored and returned: nothing read it, so it constrained nothing. It is now the declared spending scope of the wallet. Money funded under one tax category must not become spendable under another, so the tax category and the default currency can no longer be changed once wallets exist for that wallet type, and neither can be removed once set. Changing either on a wallet type that has no wallets yet is still allowed, and an attempt to change one under existing wallets is rejected with a validation error on `credit.amount_grant.tax_category` or `credit.amount_grant.default_currency`. See [Credit types and wallets](/platform-guides/wallets-credits/credit-types-wallets).

## Customers

**Seat assignments: record which customer occupies a seat**
`seats_values` on a pricing plan schedule records how many seats are paid for, but nothing recorded *which* child customer occupies one. A processing-only child was linked to its parent only through `parent_resource_ids`, so there was no way to exclude a child from being counted as a seat, to distinguish seat types, or to compare occupied seats against paid seats. Seat assignment is now an explicit resource at `/v1/seat-assignments`, with create, list, get, update and delete:

### Request

POST [https://test.api.solvimon.com/v\{version}/seat-assignments](https://test.api.solvimon.com/v\{version}/seat-assignments)

**`SeatAssignments_postSeatAssignments_example`**

```curl SeatAssignments_postSeatAssignments_example
curl -X POST https://test.api.solvimon.com/v1/seat-assignments \
     -H "X-API-KEY: <apiKey>" \
     -H "Content-Type: application/json" \
     -d '{
  "parent_customer_id": "cust_UwDeeN0vxxHxMTCJeF1K",
  "customer_id": "cust_FwDeeN0vxxHxN0COeY1H",
  "product_item_id": "proi_pwDeeN0vxxHxUUCxh81e",
  "start_at": "2026-10-01T00:00:00-05:00"
}'
```

**`SeatAssignments_postSeatAssignments_example`**

```python SeatAssignments_postSeatAssignments_example
import requests

url = "https://test.api.solvimon.com/v1/seat-assignments"

payload = {
    "parent_customer_id": "cust_UwDeeN0vxxHxMTCJeF1K",
    "customer_id": "cust_FwDeeN0vxxHxN0COeY1H",
    "product_item_id": "proi_pwDeeN0vxxHxUUCxh81e",
    "start_at": "2026-10-01T00:00:00-05:00"
}
headers = {
    "X-API-KEY": "<apiKey>",
    "Content-Type": "application/json"
}

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

print(response.json())
```

**`SeatAssignments_postSeatAssignments_example`**

```javascript SeatAssignments_postSeatAssignments_example
const url = 'https://test.api.solvimon.com/v1/seat-assignments';
const options = {
  method: 'POST',
  headers: {'X-API-KEY': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"parent_customer_id":"cust_UwDeeN0vxxHxMTCJeF1K","customer_id":"cust_FwDeeN0vxxHxN0COeY1H","product_item_id":"proi_pwDeeN0vxxHxUUCxh81e","start_at":"2026-10-01T00:00:00-05:00"}'
};

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

**`SeatAssignments_postSeatAssignments_example`**

```go SeatAssignments_postSeatAssignments_example
package main

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

func main() {

	url := "https://test.api.solvimon.com/v1/seat-assignments"

	payload := strings.NewReader("{\n  \"parent_customer_id\": \"cust_UwDeeN0vxxHxMTCJeF1K\",\n  \"customer_id\": \"cust_FwDeeN0vxxHxN0COeY1H\",\n  \"product_item_id\": \"proi_pwDeeN0vxxHxUUCxh81e\",\n  \"start_at\": \"2026-10-01T00:00:00-05:00\"\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))

}
```

**`SeatAssignments_postSeatAssignments_example`**

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

url = URI("https://test.api.solvimon.com/v1/seat-assignments")

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

request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"parent_customer_id\": \"cust_UwDeeN0vxxHxMTCJeF1K\",\n  \"customer_id\": \"cust_FwDeeN0vxxHxN0COeY1H\",\n  \"product_item_id\": \"proi_pwDeeN0vxxHxUUCxh81e\",\n  \"start_at\": \"2026-10-01T00:00:00-05:00\"\n}"

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

**`SeatAssignments_postSeatAssignments_example`**

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

HttpResponse<String> response = Unirest.post("https://test.api.solvimon.com/v1/seat-assignments")
  .header("X-API-KEY", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"parent_customer_id\": \"cust_UwDeeN0vxxHxMTCJeF1K\",\n  \"customer_id\": \"cust_FwDeeN0vxxHxN0COeY1H\",\n  \"product_item_id\": \"proi_pwDeeN0vxxHxUUCxh81e\",\n  \"start_at\": \"2026-10-01T00:00:00-05:00\"\n}")
  .asString();
```

**`SeatAssignments_postSeatAssignments_example`**

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://test.api.solvimon.com/v1/seat-assignments', [
  'body' => '{
  "parent_customer_id": "cust_UwDeeN0vxxHxMTCJeF1K",
  "customer_id": "cust_FwDeeN0vxxHxN0COeY1H",
  "product_item_id": "proi_pwDeeN0vxxHxUUCxh81e",
  "start_at": "2026-10-01T00:00:00-05:00"
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-API-KEY' => '<apiKey>',
  ],
]);

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

**`SeatAssignments_postSeatAssignments_example`**

```csharp SeatAssignments_postSeatAssignments_example
using RestSharp;

var client = new RestClient("https://test.api.solvimon.com/v1/seat-assignments");
var request = new RestRequest(Method.POST);
request.AddHeader("X-API-KEY", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"parent_customer_id\": \"cust_UwDeeN0vxxHxMTCJeF1K\",\n  \"customer_id\": \"cust_FwDeeN0vxxHxN0COeY1H\",\n  \"product_item_id\": \"proi_pwDeeN0vxxHxUUCxh81e\",\n  \"start_at\": \"2026-10-01T00:00:00-05:00\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`SeatAssignments_postSeatAssignments_example`**

```swift SeatAssignments_postSeatAssignments_example
import Foundation

let headers = [
  "X-API-KEY": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "parent_customer_id": "cust_UwDeeN0vxxHxMTCJeF1K",
  "customer_id": "cust_FwDeeN0vxxHxN0COeY1H",
  "product_item_id": "proi_pwDeeN0vxxHxUUCxh81e",
  "start_at": "2026-10-01T00:00:00-05:00"
] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://test.api.solvimon.com/v1/seat-assignments")! 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()
```

### Response (201)

```json
{
  "object_type": "string",
  "id": "string",
  "status": "SCHEDULED",
  "parent_customer_id": "string",
  "customer_id": "string",
  "product_item_id": "string",
  "start_at": "2024-01-15T09:30:00Z",
  "end_at": "2024-01-15T09:30:00Z",
  "created_at": "string",
  "updated_at": "string"
}
```

Occupancy is keyed on the `PER_SEAT` product item, which is the seat type, rather than on the schedule, because capacity itself is scoped per pricing item. `status` is derived at read time from the dates: `SCHEDULED` before `start_at`, `ACTIVE` while the seat is occupied, `ENDED` after `end_at`. Ending an assignment is a `PATCH` that sets `end_at`, which must be strictly after `start_at`; clearing `end_at` re-opens the seat. On its own this changes no billing behaviour: it is the record that makes seat-owned credits and occupied-versus-paid reporting possible. Four permissions govern it, `SEAT_ASSIGNMENT.VIEW`, `SEAT_ASSIGNMENT.CREATE`, `SEAT_ASSIGNMENT.UPDATE` and `SEAT_ASSIGNMENT.DELETE`, with the viewer role rolled into Read Only. See [Seat assignments](/platform-guides/customers-and-billing/customer/seat-assignments).

## Subscriptions

**Preview an invoice for a subscription that has only draft schedules**
A subscription whose schedules are all still [drafts](/platform-guides/customers-and-billing/subscriptions/draft-schedules) could not be previewed: with no active schedule there was nothing to price, so the preview refused rather than showing what the drafts would produce. The preview now includes draft schedules, which is what makes a draft subscription reviewable before anyone activates it.

**Draft schedules behave as one unit on a draft subscription**
While a subscription is itself a draft, every schedule on it is a draft too, so the per-schedule draft chip, the per-schedule activate action and the "Create as draft" toggles added noise around a decision that is not yours to make yet. On a draft subscription these are gone: schedules are created as drafts and all of them are activated together when you activate the subscription, from a single button. On an active subscription new schedules are still created as drafts, and the existing per-schedule controls are unchanged.

## Quotes

**Sales representatives can add schedules to a draft quote version**
Creating a pricing plan schedule required `PRICING_PLAN_SCHEDULE.CREATE`, which is the permission that governs changing live subscriptions. A sales representative building a quote therefore needed a permission far broader than the work in front of them. Schedule creation now checks which kind of subscription it targets: a quote subscription requires `QUOTE_PRICING_PLAN_SCHEDULE.CREATE` and is accepted only while the quote version is `DRAFT`, and anything else still requires `PRICING_PLAN_SCHEDULE.CREATE`. Adding a schedule to a quote version that has left draft is rejected with a validation error on `quote_version_id`, since a change to agreed terms belongs in the upgrade quote flow rather than in an edit.

**Custom fields carry over to a new quote version**
Creating a new version of a quote sends the custom fields of the version it came from. The link a CRM establishes through a custom field, an opportunity id in particular, survived into the subscription when a quote ran straight through, but was lost as soon as the quote was rejected or returned to draft and continued from a new version. The subscription created from that version now carries the link. See the [Salesforce quotes integration](/integrations/crm/salesforce/quotes).

## Invoices

**A new invoice PDF renderer, in preview**
Invoice PDFs can be produced by the same rendering service that already produces quote PDFs, which brings invoices onto one templating and localisation stack: the layout is built from the same component library as the rest of the product, and the invoice template ships translations for the languages the customer portal supports. It is opt-in while it is being rolled out. Contact your account manager to have it enabled for your platform.

**An auto rescue stops when the invoice is paid another way**
An invoice payment refused for insufficient funds schedules an automatic rescue with the payment provider. If the platform then settled the invoice through `POST /v1/invoices/{id}/pay`, the rescue still fired later and charged the customer a second time, leaving a refund to be issued. A successful invoice payment now cancels any pending auto rescue for that invoice, and a second auto rescue is not started for an invoice that already has one active. See [dunning and payment retries](/platform-guides/invoicing/dunning-and-payment-retries).

## Workflows

**Delete a draft workflow**
A workflow in `DRAFT` can be deleted, in line with the other draft resources: subscriptions, customers and pricing plans. A workflow that was created by mistake, or that was superseded before it ever ran, no longer has to be left in place. Deletion is governed by the new `WORKFLOW.DELETE` permission, and only draft workflows are deletable; an active one still has to be deactivated rather than removed, so no execution history disappears.

**Expand the trigger on an action execution**
`GET /v1/workflow-action-executions/{id}` and `POST /v1/workflow-action-executions/search` accept `expand[]=workflow_trigger_id`, which inlines the full `workflow_trigger` that produced the row. Building a list of executions with the resource each one acted on was a second call per row; it is now part of the search response. Successful email actions also record a more useful reason, naming the invoice and the message id the provider accepted, so a delivery can be traced from the [execution log](/platform-guides/workflows/monitoring-and-audit) into the provider without correlating timestamps.

**More template variables on the send-email action**
The native [send-email action](/platform-guides/workflows/workflow-actions) now passes `invoice_type`, `open_amount`, `customer_name`, `customer_type`, `credit_note` and `original_invoice_number` to the template alongside the variables it already sent. Templates written against the legacy Paragon-based Mailgun integration relied on these, and rendered them as empty values after the migration to native workflows. Migrated templates that use them render correctly without being rewritten.

**Action execution details in Desk**
A workflow action execution opens into a details panel showing what the action did, the trigger it came from and the outcome it recorded, rather than only the row in the executions table.

## Customer portal

### Fixes

bug-fix

* The Pay button is visible on the invoice list in the portal. It worked, but was not rendered, so paying from the list was only discoverable by accident; the overview page was unaffected.
* Embedded components receive your platform's primary colour as their brand colour, so an embedded balance or subscription view matches the surrounding page instead of falling back to a default palette.

## Desk

**Consistent metric selection on products**
The billing metric selector on a product item follows the same pattern as the rest of the side panel, and the meter filter, tiering metric and unit names moved into a context menu next to it. Choosing what a usage-based item measures looked different from every other field around it, and each of the related settings used its own control.

**The integration reference explained where it is used**
The reference on an integration is what makes a multi-account setup possible: several integrations of the same type on one platform, told apart by their reference. The dialog now explains what the reference is for and what to put in it, rather than presenting an unexplained field.

## August 26, 2026

## Summary

📬 This release completes localized emails. `use_template_subject` sends no subject override, so the provider uses the subject of the same template as the body. Each email also has a threading header. Gmail therefore no longer puts the invoices and reminders of one customer in a single conversation. Solvimon now compares a top-up threshold in the unit of the wallet balance. Draft pricing plan schedules, which the API got last release, now have a full editor in Desk. Price indexation has its own page. Two silent errors also stop: Solvimon validates filter values against the meter property type, and Desk reads the invoice payment status from `payment_status`. Editable meters from ingestion and a pass on the Desk buttons and sidebar complete the release.

## Pricing

**Filter values are checked against the meter property type**
A usage-based product item can filter on a meter property. Before, Solvimon did not check the filter value against the type of the property. For example, Solvimon accepted `EQUALS 1hello1` on a `NUMBER` property. The filter then matched no events, and the item stayed at zero usage. There was no indication of the cause. Solvimon now validates each value on a `NUMBER` [meter property](/platform-guides/meter-and-event-design/meter/meter-properties) when you configure the item. An invalid value gives a `values.{index}` error that names the value and the property. `STRING` and `ENUM` properties do not change.

### Fixes

bug-fix

* Duplicate conditions on pricing item configs now fail validation. Before, a `PATCH` could add two configs with the same meter property conditions. Solvimon also compared a nested condition group only with its sibling groups, and not with its parent group. A child group could therefore repeat a condition of its parent group. Two configs can no longer claim the same usage. See [Filtering on a meter property](/platform-guides/meter-and-event-design/meter/meter-properties).

## Wallets and credits

### Fixes

bug-fix

* Solvimon now compares the [automatic top-up](/platform-guides/wallets-credits/wallet-top-ups#threshold-units) threshold in the unit of the wallet balance. Before, a credit wallet with a threshold in a currency compared two different units. The balance never crossed the threshold, and the wallet never topped up. The configuration gave no indication of the cause. Solvimon now converts the threshold before it makes the comparison.

## Subscriptions

**Draft schedules in Desk**
You can now build and release [draft pricing plan schedules](/platform-guides/customers-and-billing/subscriptions/draft-schedules) from the subscription screen. The API got them last release. You add a schedule as a draft, and you can edit it while it is a draft. You then activate it from the row actions or the context menu. The schedule list shows which schedules are drafts. You can therefore see a pending renegotiation, and you do not mistake it for the current terms. Activation obeys the permissions of the API. A user who cannot activate a draft sees a disabled action, and does not get an error on save.

**Pricing plans on a schedule are collapsible**
The pricing plan and its connected pricings on a subscription or quote schedule are now a card. You can collapse the card. A subscription with several schedules stays easy to read. You do not scroll past pricing detail that you do not need.

**Select or deselect a whole pricing group**
Multi-select pricing groups on subscription and quote schedules now have a control that selects and deselects all pricings. Before, you enabled each pricing in the group with a separate click. This is the usual case if you sell a plan in full.

**Index a subset of revenue items**
If you apply [price indexation](/platform-guides/customers-and-billing/subscriptions/price-indexation) to revenue items, a percentage is no longer necessary on each item. If you leave an item empty, Solvimon does not index that item. You can therefore raise one revenue item, and not enter a value for the other items. The API accepted a partial set before this change, but the form did not.

## Invoices

### Fixes

bug-fix

* Desk now reads the invoice [payment status](/platform-guides/invoicing/invoices/invoice-payment-status) from `payment_status`, and no longer from the deprecated `paid` flag. If `payment_status` is not present, Desk uses `paid`. Desk now shows each status between unpaid and paid, for example `PARTIALLY_PAID`. Before, Desk showed these statuses as "not paid".
* The status icon on an invoice [usage report](/platform-guides/invoicing/invoices/invoice-usage-report) request no longer spins after the request is `DONE`. Before, the label showed "Done", but the icon continued to spin.

## Workflows

**Use the subject defined in the email template**
Before, a send-email [workflow action](/platform-guides/workflows/workflow-actions) had an optional `subject`. If you did not set it, Solvimon used a hard-coded English subject. You could not complete a localized email. The body came from the localized template, but the subject did not. `use_template_subject` sends no subject override, so the provider uses the subject of the template that it renders. The default is `true` if the action has no `subject`. An action that has a `subject` continues to send it. You cannot use the two fields together. If you set both fields, Solvimon rejects the request with a validation error on `use_template_subject`. To use the subject of the template, remove the `subject`. See [Subject lines](/platform-guides/workflows/workflow-actions#subject-lines).

**Each email starts its own conversation**
Each workflow email now has an `X-Entity-Ref-ID` header. The value is unique to that email, and comes from the action and the trigger. Some mail clients group messages by this header. Gmail put the invoices and reminders of one customer in a single conversation. It then hid the most recent message behind the older messages. Each email now starts its own conversation. See [Threading](/platform-guides/workflows/workflow-actions#threading).

### Fixes

bug-fix

* The execution table for workflow actions now also shows the `PENDING` and `NOT_EXECUTED` statuses. `NOT_EXECUTED` shows that the conditions of the action did not apply. `PENDING` shows that the action has not run. See [Execution statuses](/platform-guides/workflows/monitoring-and-audit#execution-statuses).

## Meters and events

**Edit a meter created during ingestion**
Before, you could use a meter from [dynamic meter creation](/platform-guides/meter-and-event-design/meter/dynamic-meter-creation), but you could not examine it. It was only a name in a dropdown. The **Show meter** action on the meter dropdown now opens a side panel. In the panel you can see the meter, and make its values and properties required. You can also add new values and properties, and add more values to an existing `ENUM` property. You can therefore make a meter from your event stream into a deliberate definition. You do not create the meter again.

## Reporting

**Reporting metrics show their aggregation**
A reporting metric on a revenue item becomes read-only after you set it. Before, the read-only state showed only the name of the meter. Two revenue items with the same meter and different aggregations looked the same. You could not see a mistake. The state now shows the aggregation with the meter, for example `SUM of Amount`.

## Integrations

**A link that creates a new quote version**
The **create new quote version** action is now also available as a link. An external system can send a user directly into a new version of a quote. The [Salesforce quotes integration](/integrations/crm/salesforce/quotes#creating-a-new-quote-version) uses this link. The user starts the new version from the opportunity, and does not first find the quote in Desk.

## Desk

**Consistent action buttons**
Action groups now use one pattern in all screens. One action is a single primary button. Two actions are a primary button and a secondary button. Three or more actions are a primary button and a context menu. The context menu always has the horizontal ellipsis icon. An action no longer moves between a row of buttons and a hidden menu on different screens.

**A sidebar section with one page is a link**
A sidebar parent with only one visible child is now a link, and no longer an expander. The link has the name of that child page. Product catalog is now Products. Payments keeps its own name, and does not show the child name "Overview". Usage metering also changed: Files is now a tab and not a submenu, and Reprocessing is under Insights.

**Clearer labels on the wallet balance box**
The wallet balance box on a customer now shows correct labels. The first figure is "Available balance". This is the open balance, which is the balance minus the reserved amount. The second line gives the name of the wallet type, and no longer repeats the word "Balance". You can also create a credit and use it in one step. "Save and create wallet" on the credit panel saves the credit type. It then opens the wallet panel with that credit type selected.

### Fixes

bug-fix

* You can now edit a wallet workflow after you create it. Before, you could only create and delete it.
* The parent customer list on the customer form no longer includes archived customers. You cannot connect a hierarchy to a customer that you retired.
* Desk now shows products in the display order of the catalog.
* Desk no longer shows a separator next to a quantity if the unit name is empty.
* The date range selector no longer shows an arrow between the two fields. The fields now have the labels "Start date" and "End date".
* Smaller interface corrections: the payments side panel is now wide enough to read. A long shopper reference truncates, and does not break the layout. The buttons in each group have the same size. Desk hides the "Manage entitlements" action for a product that has no features.

## August 19, 2026

## Summary

✍️ We know that a renegotiated contract is rarely one edit, and until now every edit was live the moment you saved it. This release is about getting a change reviewed before it bills: pricing plan schedules gain an explicit draft status with an approval gate, pricing categories can finally be reordered on a plan version, and workflow trigger conditions get a proper editor in Desk with nested AND/OR groups. On the invoicing side, tax summaries are split per rate, wallet grants are reversed when an invoice is voided, and an invoice containing a deduction can reach `PAID` again. A reactivate action for deprecated meters and clearer custom field ownership round out the release.

## Pricing

**Draft status on pricing plan schedules**
A [pricing plan schedule](/platform-guides/customers-and-billing/subscriptions/subscription-schedules) now carries an explicit `status`, either `DRAFT` or `ACTIVE`, defaulting to `ACTIVE` so existing integrations are unaffected. A draft schedule can be prepared and reviewed before it takes effect, which is what an approval flow needs; initializing a subscription with status `ACTIVE` is rejected when its earliest schedule is still a draft, so a subscription cannot go live against a schedule nobody signed off on. The read-only `approval_status` of the associated approval request is exposed on the schedule alongside it. [Draft schedules](/platform-guides/customers-and-billing/subscriptions/draft-schedules) covers the full flow, including the permissions below.

A read-only `is_current` was added at the same time, combining the schedule's date window with its status so you can tell what a customer is on today without evaluating both yourself. Drafts are promoted through `POST /v1/pricing-plan-schedules/{id}/activate` for one schedule, or `POST /v1/pricing-plan-schedules/activate?pricing_plan_subscription_id=...` to activate every draft schedule of a subscription in one all-or-nothing batch, so a multi-schedule change goes live together or not at all. Two permissions govern this: `PRICING_PLAN_SCHEDULE.STATUS.UPDATE` to activate a draft, and `PRICING_PLAN_SCHEDULE.WRITE_ACTIVE` to create, copy or migrate a schedule that is not a draft. A user who only holds the first can prepare changes but not put them live. Quote schedules keep their own quote permissions and are unaffected.

**Reorder pricing categories on a plan version**
`display_order` on a pricing category is now honoured. Previously the order of categories on a pricing plan version always followed the order of the product categories in the catalog, and patching `display_order` through [PATCH /v1/pricing-plan-versions/\{id}](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-versions/patch-pricing-plan-versions-by-resource-id) had no effect, so the only way to change how a plan reads was to reorder the catalog itself. Ordering is validated as a complete permutation: set it on all categories or on none, each value unique, and the values covering 1 through the number of categories without gaps. Desk exposes the reordering on the pricing plan version screen.

## Invoices

**Tax summaries are split per rate**
The tax summary key now combines the tax category with the rate instead of the category alone, so an invoice that applies more than one rate within the same category (a mixed-rate AvaTax result, for example) produces one summary line per rate rather than collapsing them into a single line with a blended base. Invoice groups are grouped the same way as the periods and the invoice itself, so the totals reconcile at every level. The Canadian tax engine keys on the combined rate too; it resolves one rate set per customer, so nothing splits there today, but the behaviour is now consistent across engines. Cost prediction follows the same grouping.

### Fixes

bug-fix

* An invoice that contains a deduction can reach [`PAID`](/platform-guides/invoicing/invoices/invoice-payment-status) again. The payment status compared the paid amount against the invoice total before deductions, but the deducted part is settled through the deduction and is never invoiced, so no payment could ever cover it and the status stayed `PARTIALLY_PAID` forever. It now compares against the open amount, which is what "paid" means for such an invoice.
* [Wallet](/platform-guides/wallets-credits/credit-types-wallets) grants that came from an invoice are reversed when that invoice is voided, so voiding a [top-up](/platform-guides/wallets-credits/wallet-top-ups) no longer leaves the customer with credits they did not pay for.
* [Credit notes](/platform-guides/invoicing/invoice-corrections/credit-invoices) and one-off invoices are excluded from reprocessing. Reprocessing only regenerates standard invoices, which is the only kind whose lines are derived from usage.
* The per-seat unit price is shown in the price column of an invoice line for [per-seat pricing](/platform-guides/products-and-pricing/pricing-plans/per-seat-pricing). A line showing a quantity of 30 seats now also states the price per seat, matching how the subscription presents the same pricing.

## Workflows

**Trigger conditions in Desk**
The `conditions` tree on a [workflow trigger](/platform-guides/workflows/workflow-triggers), added to the API in a previous release, now has an editor in Desk. You build comparisons on the fields the trigger exposes, with the comparators allowed for each field type, and combine them into nested `AND`/`OR` groups. The recursive tree editor is shared with the meter condition editor, so workflow conditions are no longer limited to a flat list of ANDs while the API accepted nested groups: a rule such as "only for credit notes above \$500, or for any invoice of a customer in this segment" can be expressed in the UI.

## Quotes

### Fixes

bug-fix

* An upgrade quote with a start date after the end of the original subscription's schedule is now rejected outright. The request returned an `INVALID_FIELD` error but created the quote and its version anyway, leaving an invalid quote behind.

## Meters and events

**Reactivate a deprecated meter**
A [meter](/platform-guides/meter-and-event-design/meter) in `DEPRECATED` status can be activated again from Desk. The transition was already allowed by the API, but the Activate action was missing from the status menu, so a deprecated meter could only be archived, and archiving is the one step in the resource lifecycle you cannot walk back. Deprecating a meter you still need is a common mistake when tidying up a catalog: the meter keeps matching events but is hidden from new configuration, and the fix is now a single action rather than rebuilding the meter and re-pointing every product item at it.

## Reporting

**Request a usage report from the invoice**
The "Request usage report" action stays available on an invoice that already has a [usage report](/platform-guides/invoicing/invoices/invoice-usage-report). The button previously disappeared as soon as one report existed, so requesting a fresh one meant going to the Reports tab and pasting the invoice id. That is the wrong way round for the common case: a usage report is most often re-requested precisely because an earlier one is now stale, after usage was reprocessed or a dispute made the numbers worth re-checking. Support agents answering "can you send me the detail behind this line" can now do it from the invoice they are already looking at.

## Integrations

**Custom fields state which integration they belong to**
A [custom field](/platform-guides/platform-configuration/custom-fields) created for a system integration now exposes a read-only `integration_type` next to its `integration_id` and `integration_reference`, and the reference itself became read-only, so the link between a field and the integration that owns it cannot drift. Sandboxes seed the Adyen and email integrations with named custom fields ("Adyen Shopper Reference", "Adyen Balance Account" and so on) under clearly named integrations, so it is obvious which fields are managed for you and should not be edited by hand.

## August 12, 2026

## Summary

🛒 This release turns the customer portal from a place to look at invoices into a place to change your plan: customers can create a new pricing plan schedule, upgrade mid-cycle, top up a wallet and manage automatic top-ups without touching your product. Around it, invoices gain two display settings (tax per line item and the original customer's line), the pay link on an invoice becomes patchable, wallet balance workflows can send emails, API keys can be deactivated from Desk, and the events and customer screens received a round of usability work. Wallet insights and a credit burndown graph in Desk round out the release.

## Customer portal

**Self-serve plan changes**
A customer can now create a new [pricing plan schedule](/platform-guides/customers-and-billing/subscriptions/subscription-schedules) on their own subscription from the [customer portal](/platform-guides/customers-and-billing/customer-portal), which is what a free-to-premium upgrade needs. `POST /v1/portal/pricing-plan-schedules` takes the `pricing_plan_subscription_id` and the `enabled_pricings` the customer selected; the schedule reuses the subscription's current pricing plan version, and the previous schedule is closed at the new start. `PATCH /v1/portal/pricing-plan-schedules/{id}` updates an existing one. [Self-serve plan changes](/platform-guides/customers-and-billing/self-serve-plan-changes) walks through both calls end to end.

Two start dates are possible. Left out, `start_at` puts the change at the start of the next billing period. Passed as now (it is only accepted within five minutes of the current time), the change takes effect immediately and the billing period realigns to it, which requires a subscription with billing time `EXACT`; on a `CALENDAR` subscription the request is rejected with a message telling you to omit `start_at`. The subscription's next invoice is refreshed afterwards so the change is visible right away.

The upgrade and the payment can also be tied together. Call `POST /v1/portal/payments/authorize` with context type `CREATE_PRICING_PLAN_SCHEDULE` and a `create_pricing_plan_schedule` block naming the `pricing_plan_subscription_id`, `pricing_plan_version_id`, `pricing_id` and optional `start_at`; the schedule is created once the payment is authorized, so a customer never ends up on a new plan with an unpaid balance or the other way round. The step is idempotent: an existing schedule with the same start date is reused rather than duplicated when a retried payment lands twice.

Portal-driven changes are gated on the customer being able to pay for them. The subscription must be `ACTIVE` and belong to an `ACTIVE` customer, and it must carry an `ACTIVE` payment method of that same customer whose type is one of `CARD`, `DIGITAL_WALLET`, `DIRECT_DEBIT`, `ONLINE_BANKING` or `BUY_NOW_PAY_LATER`. Prepaid and unrecognized payment methods are rejected.

**Wallet top-ups from the portal**
`POST /v1/portal/invoices/charge-on-demand-pricing-items` now charges the customer instead of only returning a preview. The request carries the `pricing_plan_schedule_id`, the `pricing_items` to charge (each with an optional `flexible_amount` for [flexible pricing](/platform-guides/products-and-pricing/pricing-plans/flexible-pricing) items), a `payment_method_id` and `finalize_immediately`; pass `preview: true` to get the previous preview-only behaviour. This completes the self-service top-up flow for credit-based pricing: the customer picks a bundle or an amount, pays with a stored payment method, and the [wallet](/platform-guides/wallets-credits/credit-types-wallets) grant is booked against the invoice as it is created. See [Wallet top-ups](/platform-guides/wallets-credits/wallet-top-ups).

**Automatic top-ups from the portal**
[Automatic wallet top-ups](/platform-guides/wallets-credits/wallet-top-ups), introduced through the platform API in a previous release, can now be managed by the customer: `/v1/portal/auto-top-up-configs` supports creating a config, listing the configs for a `wallet_id`, fetching one, and the `activate` and `deactivate` actions. The config gained a `topup_amount` field, which is required when the linked on-demand item uses flexible pricing and rejected when it uses a fixed price, so a customer choosing "top up \$50 whenever I drop below 100 credits" is expressible without a dedicated fixed-price item. The amount is validated against the item's flexible pricing band, and the resulting wallet grant is booked synchronously so the new balance is visible the moment the top-up completes.

**Available top-ups on the wallet balance**
[POST /v1/customers/\{id}/wallets/balance](https://docs.solvimon.com/api-docs/configuration-api/customers/post-customers-by-resource-id-or-reference-wallets-balance) can now expand `charge_on_demand_pricing_items`, which lists the on-demand [pricing items](/platform-guides/products-and-pricing/pricing-plans/on-demand-charges) that can top up each wallet, each with its `pricing_item_id`, `pricing_plan_schedule_id` and the expandable `pricing_item` itself. A portal or in-product balance widget can therefore render the balance and the top-up options the customer is entitled to from a single call, instead of resolving the customer's schedules separately.

## Invoices

**Hide the tax breakdown per line item**
A new `show_tax_per_line_item` setting on the subscription controls whether the VAT breakdown is rendered per invoice line or only as a summary at the total level. It defaults to on, which is the existing behaviour, and the value in effect when the invoice is created is copied onto the invoice (and onto its credit notes), so historical documents keep rendering the way they were issued. Turning it off removes the tax rate column and the per-subtotal "incl. tax" labels from the invoice PDF and the specification page. This is aimed at self-billing setups, where a per-line tax breakdown adds noise rather than information. The toggle is available in Desk under "Edit subscription details".

**Show the original customer's invoice line**
`show_original_customer_invoice_line` is a new boolean on the platform's `default_pricing_plan_subscription_settings`, overridable per subscription, following the same pattern as `display_invoice_sub_lines_per_processing_only_customer`. The subscription value wins, the platform default applies when the subscription leaves it unset, and the resolved value is stored on the invoice so it survives regeneration and crediting. Desk exposes it in the Edit subscription modal.

**Set the pay link on an invoice**
The `pay_invoice_link` on an invoice is now writable through [PATCH /v1/invoices/\{id}](https://docs.solvimon.com/api-docs/transaction-api/invoices/patch-invoices-by-resource-id), by passing a `portal_url_id`. This lets you point a finalized invoice at a specific published [portal URL](/platform-guides/customers-and-billing/customer-portal) instead of the one Solvimon generates, which matters when you send invoices through your own communication flow. The link must reference a `PUBLISHED` portal URL that belongs to the invoice being patched, and only a `FINAL` invoice can be patched. A manually set link takes precedence and is preserved when the invoice is refreshed. See [Hosted checkout](/platform-guides/customers-and-billing/hosted-checkout) for how those URLs are created.

### Fixes

bug-fix

* An on-demand product is no longer charged again on the first invoice of a subscription.
* The delivery date entered on a partial credit is now sent with the credit request, so credit notes for entities that require a delivery date (for example in Hungary) carry the right one.
* On self-billing invoices, Desk now swaps the customer and supplier blocks the same way the invoice PDF does, so the screen matches the document.
* Crediting an invoice sends `credit_details_source` in every case (full or partial, standard or one-off), and the toggle now reads as "use the original customer details", matching what the service does by default.

## Quotes

### Fixes

bug-fix

* An upgrade quote can no longer be created with a start date at or before the start of the subscription's current schedule. This previously passed validation and produced an action queue item that failed later.

## Pricing

### Fixes

bug-fix

* Pricing item configs that specify a pricing period or a currency are ignored when the schedule has no period or currency set, instead of being applied to a schedule they were not meant for.
* "Edit plan details" no longer shows a single-select currency field for multi-currency plans, where it could not be pre-filled and did not reflect the plan's actual currency list. Currency management for those plans stays in "Edit version details".
* Filling in the prices of a single-currency plan no longer triggers a request to complete prices for other currencies.

## Workflows

**Wallet balance actions and emails**
The wallet balance [trigger](/platform-guides/workflows/workflow-triggers) now supports [actions](/platform-guides/workflows/workflow-actions) in general and send-email in particular, so a customer can be emailed when their credits run low rather than only receiving a webhook. Desk supports building these wallet workflows, and the wallet, credit and wallet balance workflow set is seeded in sandboxes so you can try the flow without configuring it from scratch.

**Date format selector in Desk**
The `date_format` field on send-email actions, added to the API in a previous release, now has an editor in Desk: you compose the pattern from the supported tokens (`yyyy`, `yy`, `MMMM`, `MM`, `dd` and so on) and see a rendered example while typing, so an email that should read `24.08.2026` no longer needs an API call to configure.

**Email actions on test platforms**
On test platforms, send-email actions now override the `to` and `cc` addresses with the platform's `support_email`. Testing an invoice email flow against realistic customer data no longer risks delivering it to that customer.

## Developers

**API key status management**
An API key can now be activated and deactivated from Desk, so rotating a key no longer means creating a replacement and leaving the old one live. The [API keys](/platform-guides/platform-configuration/api-key-creation) screen gained a Status column and a per-row menu, backed by [PATCH /v1/api-keys/\{id}](https://docs.solvimon.com/api-docs/identity-api/api-keys/patch-api-keys-by-resource-id).

### Fixes

bug-fix

* `PATCH /v1/api-keys/{id}` no longer returns a 404 for keys that are visible through `GET /v1/api-keys`. The handler resolved the key against the authenticated user's entity instead of the platform selected through `X-Platform-Id`; it now resolves against the request's platform.

## Integrations

**Set up your own email provider from Desk**
Connecting your own Mailgun account to native [workflow emails](/platform-guides/workflows/workflow-actions) previously required Solvimon to create the system integration for you through the API. Desk now exposes that setup directly, so you can connect and update your mail integration yourself and get the email configuration for a new platform working without a support round trip.

## Meters and events

**Events table improvements**
The events table now folds the deleted flag into the processing details column as a red "Deleted" badge (the "Not matched" badge gained an info icon in the same pass), and the reference column is capped at half the container width with truncation on screens below 1200px, so the columns that carry meaning stay readable. In Desk, events can also be ingested for a customer that does not exist yet: the ingest panel can create the customer inline.

### Fixes

bug-fix

* The events table shows both properties when two meter properties share a display name but differ by id, instead of rendering one of them twice. Properties are now matched by id rather than by name.

## Customers

**Create a customer where you need one**
The "New invoice" and "Ingest events" panels can now create a customer inline, so a one-off invoice or a first test event for a customer that is not in Solvimon yet no longer means abandoning the form. Creating a customer from the customers screen now navigates straight to the new customer's detail page.

## Desk

**Wallet insights and credit burndown**
Insights gained wallet sub-tabs under Usage and Customers, and the customer screen now shows a credit burndown graph under the wallet balance box, so credit consumption over time is visible next to the current balance instead of only in a separate dashboard.

**Side column and panel cleanup**
Content blocks in the side column place the label and the value next to each other in the same size and weight, with the edit control next to the title where the block is editable, which makes the column shorter and easier to scan. Edit and delete were also split into separate actions, so the edit modal no longer doubles as a delete confirmation.

### Fixes

bug-fix

* Copy and terminology on the pricing pages were corrected, including the wording that distinguishes a regular plan from an add-on and the fields in the "add a plan" modal.
* The invoice summary now shows the customer prefix, and child customer names, so an invoice that bills for a hierarchy can be read without opening each line.

## August 5, 2026

## Summary

☀️🏖️ Summer is in full swing and we're not euro-maxxing so this release packs a good bunch of goodies: usage limits with threshold webhooks across the customer hierarchy, a send-webhook workflow action, localized email templates, flexible pricing and tiering conditions in Desk, a guided billing metric setup, and a credit-and-duplicate flow for invoices. Payment error details in Desk and a tidied-up invoice creation flow round out the release.

## Meters and events

**Usage limits with threshold alerts on the customer hierarchy**
You can now store usage limits per customer and get alerted when consumption approaches them, which is the building block for pay-as-you-go platforms that give each end user (modelled as a processing-only child customer) a soft cap. A new `UsageLimit` resource is managed through [POST /v1/usage-limits](https://docs.solvimon.com/api-docs/configuration-api/usage-limits/post-usage-limits): it links a `customer_id` to a [`meter_value_calculation_id`](/platform-guides/meter-and-event-design/meter/meter-values) with a `limit` (a number, amount or credits) and a `scope`. `SELF` limits the customer's own usage; `CHILDREN` sets a default limit for each child, resolved by walking up the [hierarchy](/platform-guides/customers-and-billing/customer/parent-child-relationships) to the nearest parent, and a child's own `SELF` limit for the same metric overrides its parent's default.

Alerting is configured through a new `USAGE` [workflow trigger type](/platform-guides/workflows/workflow-triggers): the workflow holds the thresholds (percentages of the resolved limit, or absolute values) while the limit itself stays per customer, so one platform-level workflow covers every customer and seat. Each threshold crossing fires the workflow's actions once per billing period.

Two supporting pieces ship alongside it. [GET /v1/usage-limits/status](https://docs.solvimon.com/api-docs/event-api/usage-limits/get-usage-limits-status) returns the current-period consumption against the resolved limit per customer (with `include_children=true` to cover a whole hierarchy), giving you the numbers for an in-product usage dashboard. And `POST /v1/workflows/init` idempotently ensures a workflow and its actions exist by `reference`, creating them when missing and leaving them untouched otherwise, so provisioning can be scripted safely.

## Workflows

**Send a webhook as a workflow action**
A new `SEND_WEBHOOK` action type delivers a workflow's trigger payload to one of your registered [webhooks](/platform-guides/for-developers/webhooks), referenced by `webhook_id` in the action's `send_webhook` block. Combined with usage-limit workflows this produces `USAGE_LIMIT.THRESHOLD_REACHED` notifications carrying the customer, the metric, the threshold that was crossed, the resolved limit and the consumption, so your platform can react to usage events in real time.

**Localized email templates**
Send-email [workflow actions](/platform-guides/workflows/workflow-actions) gained a `localize_template_names` toggle. When enabled, the action appends the end customer's locale to the template name at send time (for example `credit.invoice_en-us`) and looks that template up with your email provider, falling back to the base template when no localized variant exists. This matches the behaviour of the legacy Paragon email integration, so platforms sending invoice emails in their customers' languages can migrate to native workflows without losing localization. Desk exposes the toggle, and the support email field from the previous release, in the send-email action settings.

## Pricing

**Flexible pricing in Desk**
[Flexible pricing](/platform-guides/products-and-pricing/pricing-plans/flexible-pricing), where the customer chooses how much to pay within a band and receives wallet credits in proportion, could previously only be configured through the API. The Desk pricing editor now supports the `FLEXIBLE` pricing type directly, including the minimum and maximum amounts and the credit conversion, so self-service top-up pricing can be set up without code.

**Tiering conditions in Desk**
The pricing item config editor now includes an editor for `tiering_conditions`, the filter that decides which events count toward a config's [tier](/platform-guides/products-and-pricing/pricing-plans/usage-based-pricing-methods/tiered-pricing) for product items with a tiering metric. You build the same AND/OR/XOR expression tree used for product item conditions, using the properties of the tiering meter; a config without tiering conditions keeps counting every tiering event. The editor only appears when the product item actually has a tiering meter value calculation, matching the API's validation.

**Clearer tiering tables and custom unit names**
Tiering tables in Desk now state explicitly which range each tier covers, and custom unit names you define on a product item are now used consistently across the pricing screens instead of a generic "units". Pricing screens now read the way you'd explain the pricing to a customer, which makes it easier to spot a misconfigured tier before it reaches an invoice.

### Fixes

bug-fix

* Per-seat product items marked as not billed no longer trigger a "Please fill in all fields in all currencies" error when using "Copy from default" on a multi-currency plan.
* The root condition of a pricing condition tree is collapsible again.

## Products

**Guided billing metric setup**
Picking the billing metric while setting up a revenue item was redesigned into a guided flow, so you can create a new product and its pricing in one go without first learning how meters, meter values and calculation methods fit together. You select the meter, then the meter values and properties attached to it, then the usage metric (calculation method), and a summary text describes what the resulting metric measures. Anything missing can be created inline; choosing "add" opens the meter panel pre-filled for the meter you were configuring, so first-time setups no longer bounce between the products and meters areas. This flow is new and we're actively shaping it, so if it helps (or gets in your way), tell us: we'd love to hear how it works for your setup.

**Edit metric filters on active product items**
Metric filters on a product item can now be edited while the item is active, instead of requiring a new draft version for a filter change. Small corrections, like excluding a test event type you forgot to filter out, no longer force you through a full version cycle.

**Navigation cleanup**
Features moved out of the product catalog submenu, and the products list now loads 50 items per page so most catalogs fit on a single page.

## Invoices

**Credit and duplicate an invoice in Desk**
The credit flow in Desk can now credit an invoice and create a duplicate in one action, which is the common way to correct a finalized invoice: the [credit note](/platform-guides/invoicing/invoice-corrections/credit-invoices) cancels the original and the duplicate gives you an editable draft to fix and re-send. Previously this combination was only available through the API.

**Pricing plan schedules on the invoice**
Invoices now expose a read-only `pricing_plan_schedule_ids` field listing the [pricing plan schedules](/platform-guides/customers-and-billing/subscriptions/subscription-schedules) the invoice bills for, next to the existing `pricing_plan_subscription_ids`. Because an invoice can span multiple subscriptions and can include usage and pay-in-advance fees from different schedules around a cut-over, this is a list; it lets you trace every line back to the schedule that produced it without expanding each line.

**Invoice creation flow cleanup**
Creating an invoice in Desk was reworded and tidied up, so occasional users, like a support agent issuing a one-off correction, can find the right action without guessing what a button does: the one-off invoice action and button were renamed to say what they do, the report request form was rebuilt, the two "Add" buttons on the invoice were joined into one, the list filters now sit on a single row, and the paid status filter uses a clearer select component.

### Fixes

bug-fix

* Invoices for plans with multiple billing cycles now only use the pricing item configs that belong to the cycle being invoiced.

## Payments

**Payment error details in Desk**
When a payment ends in an error, the payment side panel now shows the result details including the `external_reason` returned by the payment service provider (for example a missing acquirer account for a card type and currency), so you can diagnose a failed payment without pulling the raw API response.

## Customers

### Fixes

bug-fix

* Updating a customer no longer sends the PATCH request twice, which could cause unexpected side effects.
* Individual customers can be created using the keyboard only; selecting from a dropdown no longer drops the focus out of the form.
* The "Add subscription" button is hidden on deprecated customers, where creating a subscription is not allowed.
* The reprocess option on the customer screen received usability improvements, and a duplicate country code chip was removed from the customer form.
* Where an invoice email address is expected, Desk now falls back to an individual customer's email when no separate invoice email is set.

## Subscriptions

**Subscription form and overview cleanup**
The new-subscription form and the subscription overview page were decluttered: redundant fields and sections were removed and the remaining information was regrouped, making the common path through creating and inspecting a subscription shorter.

## Developers

### Fixes

bug-fix

* API requests with malformed URLs containing empty path segments now return a proper error response instead of a 500.

## July 29, 2026

## Summary

This release introduces automatic wallet top-ups, workflow trigger conditions with configurable email variables, rate card discounts below the invoice level, richer usage reports, and Paragon multi-account support, alongside a broad set of Desk improvements across invoices, products, subscriptions and event ingestion.

## Pricing

**Automatic wallet top-ups**
[Wallets](/platform-guides/wallets-credits/credit-types-wallets) can now be topped up automatically when their balance runs low, so a customer on credit-based pricing never stalls because their credits ran out. You create a config through [POST /v1/auto-top-up-configs](https://docs.solvimon.com/api-docs/configuration-api/wallets/post-auto-top-up-configs) with the `wallet_id`, a `threshold` (an amount or a number of credits, matching the wallet's denomination), the `pricing_plan_schedule_id` and `pricing_item_id` of a one-off [on-demand](/platform-guides/products-and-pricing/pricing-plans/on-demand-charges) item that grants credits to the wallet, and the `payment_method_id` to charge. Whenever the balance drops below the threshold, the charge is executed through the existing on-demand flow against the stored payment method. Configs can be switched on and off through the `activate` and `deactivate` actions, and this also works with [flexible pricing](/platform-guides/products-and-pricing/pricing-plans/flexible-pricing) items.

**Wallet grants are no longer prorated**
When a subscription change starts mid-period, pricing item configs that carry wallet grants are now charged at the full price and grant the full number of credits, instead of being prorated. This makes mid-period upgrades work as customers expect for credit bundles: someone who runs out of credits halfway through the month and upgrades receives the complete credit allowance of the new tier immediately. Items without wallet grants are still prorated as before.

**Discounts on rate cards at every level**
Rate cards previously only accepted discounts on the invoice total; a discount on a product, product item or condition was rejected. Discounts within subscription schedules can now target all levels, including a new `product_item_meter_property_conditions` clause type for discounting a specific meter-property condition of a product item. This removes a common blocker for platforms that offer their merchants item-specific discounts but want the bulk-update convenience of rate cards.

**Fixed amounts for tiered pricing on amount-based metrics in Desk**
When a usage metric is amount-based, Desk previously only offered percentage-based tier prices even though the API already accepted fixed amounts. The pricing editor now lets you choose between a percentage and a fixed amount per tier, so this setup no longer requires the API.

**Copy pricing plans with indexing across currencies and billing periods**
Copying a pricing plan that combines multiple currencies and/or multiple billing periods now carries the pricing indexing configuration over to the copy, instead of dropping it.

### Fixes

bug-fix

* Weekly proration is calculated correctly again for pricing periods expressed in weeks.
* Previewing a subscription with a rate card now works when the rate card's schedule does not start on the day of the preview.
* The `Equals` comparator is no longer offered for discount conditions, in the API and in Desk. A discount condition compares against a usage total, where an exact-equality match is ambiguous; the remaining comparators (greater/less than) express the intent unambiguously.
* Patching a subscription schedule now validates that a config's pricing period does not exceed its billing period, returning a clear error instead of accepting a configuration that cannot be invoiced.

## Products

**Guided product setup in Desk**
The product catalog area of Desk received a set of usability improvements: the new-product modal was streamlined, selecting a revenue type now explains what each type means, the empty state points you to the next step, and naming was aligned across the UI (including renaming the pricing "amount type" to "Tax behavior", which is what it controls). Activating a product item now shows a confirmation overlay summarizing the effect before you commit.

## Meters and events

**Ingest page improvements in Desk**
The manual ingest page (now called "Ingest event") is easier to work with: the meter and reference fields are searchable selectors, and typing a name that doesn't exist yet offers to create the meter on the spot, building on dynamic meter creation. Existing meters are summarized in a card instead of raw input fields, the code example was restyled, and the Events menu was reordered to Events, Meters, Files, Reprocessing log.

## Workflows

**Trigger conditions**
[Workflow triggers](/platform-guides/workflows/workflow-triggers) now support an optional `conditions` tree that gates whether the workflow's actions run. A condition node is either a `COMPARISON` (a single trigger field evaluated against a value) or a `GROUP` combining nested conditions with an `AND`/`OR` operator, so you can express rules such as "only run this action for credit invoices". Conditions are evaluated when the trigger fires, and each execution records the evaluated tree in `conditions_result_tree`, so you can see in the [execution audit](/platform-guides/workflows/monitoring-and-audit) exactly why a workflow did or did not run.

**Date format and support email in email actions**
Send-email [workflow actions](/platform-guides/workflows/workflow-actions) gained two configuration fields. `date_format` controls how date variables such as `invoice_date` and `invoice_due_date` are rendered in the email, so dates can match your locale or house style (for example `DD.MM.YYYY` instead of `YYYY-MM-DD`). `support_email` is exposed to email templates as the `support_email` variable and falls back to the action's reply-to address when not set.

**Fetch a single action execution**
A workflow action execution can now be fetched directly through `GET /v1/workflow-action-executions/{id}`, instead of misusing the search endpoint to look up one record. Desk uses this to show the details of a single execution.

## Invoices

**Invoice screen improvements in Desk**
The invoice detail screen now combines the summary and invoice details into one view, shows a "Credited" label next to the status of an invoice that has been credited, and drops the due date column from invoice tables where it added noise. The context menu actions were reordered by frequency of use, and adding items to a one-off invoice received copy and interaction improvements.

**Original or current customer details on one-off invoice credits**
The choice between keeping the original customer details or using the customer's current details, introduced for standard [credit notes](/platform-guides/invoicing/invoice-corrections/credit-invoices) in the previous release, is now also available when crediting a one-off invoice.

### Fixes

bug-fix

* Idempotent retries of API requests whose original response was stored gzip-compressed now return the response body correctly. Previously the replay could return the compressed bytes instead of the JSON payload.

## Subscriptions

**Clearer billing timing and currency selection in Desk**
The subscription form now explains the difference between Exact and Calendar billing when you pick a billing timing, and shows a billing currency selector when the selected plan supports multiple currencies, so multi-currency plans can be subscribed in the right currency without leaving Desk.

### Fixes

bug-fix

* Changing a subscription's end date now only skips invoices that fall after the new end date. Previously invoices could be skipped too eagerly when an end date was moved.
* The invoice preview no longer flashes while toggling an add-on on the subscription form.
* Inactive add-ons are no longer hidden by default in schedules, and multi-select pricings are shown for selection again, so existing configurations remain visible and editable.
* "Show seats history" is hidden when a subscription has no seats history, and the non-optional billing period field no longer shows a clear ("X") control.

## Integrations

**Paragon multi-account setup**
Platforms that run multiple instances of the same ERP integration (for example one Xero account per billing entity) can now model this through Paragon's multi-account authorization. A new `ERP` integration type with the `PARAGON_MULTI_ACCOUNT` variant represents one Paragon multi-account as a single Solvimon integration, and a `paragon_external_ids` custom field on the synced resources controls routing: webhook deliveries are fanned out per account, each carrying the `X-Paragon-Credential` header for the target account, so every object lands in the right ERP instance.

### Fixes

bug-fix

* AvaTax: processing-only customers are now excluded from the AvaTax sync, so tax documents are only created for customers you actually invoice.

## Reporting

**Usage report improvements**
The usage report now breaks the currency conversion chain into its two actual steps and shows the tax-inclusive cost. New columns: `Cost Amount Quantity Including Tax` (the event's cost in the invoice currency including tax), `Billable Metric Quantity` and `Billable Metric Currency` (the metric in its original currency), `Pricing Currency`, `Metric to Pricing Exchange Rate`, and `Pricing to Invoice Exchange Rate`. Previously only a single exchange rate between the metric currency and the invoice currency was reported, which hid the intermediate pricing-currency conversion. The precision of the cost amount quantity movement columns was increased as well.

## Desk

**AI chat on Insights**
The Insights area now embeds an AI chat assistant, so you can ask questions about the data behind your dashboards in natural language instead of composing filters by hand.

**Redesigned side panels and schedule actions**
Side panels across Desk were restyled with a consistent footer and close button, the subscription and quote view screens use explicit action buttons on schedules, and "Expand all" on pricing plan schedules now renders expanded sections only when they scroll into view, keeping large plans responsive.

### Fixes

bug-fix

* Nested pricing conditions now display correctly for multi-currency plans.
* Discounts show up again on checkout pages when a rate card is selected, along with other rate card display fixes on checkout pages.
* The quote template selector was improved, making it easier to find the right template when building a quote.
* Fixed an overlap issue when clicking "Show plan" on quotes and subscriptions, and a sizing issue in the pricing plan dropdown.

## Customer portal

### Fixes

bug-fix

* Fixed the CORS configuration for the portal's `X-CLIENT-VERSION` header, which could block portal API requests from the browser.

## July 22, 2026

## Summary

This release introduces pricing plan groups, an invoice cadence that is independent of the pricing period, reverse-a-payment support, dynamic variables in quote templates, and a clearer payment status on invoices, alongside subscription, customer and integration improvements.

## Pricing

**Pricing plan groups**
A new `PricingPlanGroup` resource lets you group related [pricing plans](/platform-guides/products-and-pricing/pricing-plans) together with the rules for moving between them (upgrade, downgrade and cancellation timing). A pricing plan belongs to at most one group. Create and manage groups through `POST /pricing-plan-groups` and the accompanying CRUD endpoints; a read-only link field on the pricing plan shows the group it belongs to. This release adds the resource and its management API; executing an upgrade or downgrade on a subscription through a group follows in a later release.

**Invoice cadence independent of the pricing period**
Pricing item configs now support a `billing_period` that is separate from the `pricing_period`, so you can invoice on a different cadence than the one used to price usage (for example price per month, invoice per quarter). In Desk, single-cycle [recurring](/platform-guides/products-and-pricing/pricing-plans/recurring-pricing) and [per-seat](/platform-guides/products-and-pricing/pricing-plans/per-seat-pricing) product items gain a "Billing period" field to set this cadence without editing the pricing period.

## Subscriptions

**Cancellation reason**
When [cancelling a subscription](/platform-guides/customers-and-billing/subscriptions/subscription-lifecycle) you can now pass an optional free-text cancellation reason on the [POST /v1/pricing-plan-subscriptions/\{id}/cancel](https://docs.solvimon.com/api-docs/configuration-api/pricing-plan-subscriptions/post-pricing-plan-subscriptions-by-resource-id-cancel) request. It is persisted on the subscription and is available through both the API and the customer portal, so you can record why a subscription ended.

**Keep indexing overrides when copying a schedule**
[Copying a subscription schedule](/platform-guides/customers-and-billing/subscriptions/subscription-schedules) in Desk now carries over its indexing overrides, so a copied schedule keeps the index adjustments you already configured instead of resetting them.

## Invoices

**Payment status on invoices**
Invoices now expose a [`payment_status`](/platform-guides/invoicing/invoices/invoice-payment-status) field with the values `UNPAID`, `PARTIALLY_PAID`, `PAID` and `OVERPAID`, and Desk shows this status on the invoice screen. It gives a more precise view of collection state than the previous paid/unpaid boolean, including partial and over-payments.

**Finalize invoices in order**
An invoice can now only be made final once the previous period's invoice is already final. This prevents finalizing out of order, where changing an earlier period (for example through included volume) could retroactively affect a later invoice that was already [final](/platform-guides/invoicing/invoices/invoice-lifecycle).

**Keep original customer details on a credit note**
When [crediting a standard invoice](/platform-guides/invoicing/invoice-corrections/credit-invoices) you can now choose whether to keep the original customer details from the invoice being credited, or use the customer's current details in the platform. The choice is offered as a radio option in the credit note modal.

### Fixes

bug-fix

* The product item dropdown for adding a one-off line now shows a label for the product item type (Revenue or Deduction), so items with the same name in both categories can be told apart before adding the line.
* "Recent invoices" lists in Desk are now sorted by date instead of returning in an unsorted order.

## Payments

**Reverse a payment**
A new reverse action, [POST /v1/payments/\{id}/reverse](https://docs.solvimon.com/api-docs/transaction-api/payments/post-payments-by-resource-id-reverse), undoes a payment without you having to know its state at the payment service provider. If the payment has not been captured yet it is cancelled; if it has already been captured it is refunded, the same way as a [referenced refund](/platform-guides/invoicing/invoice-corrections/referenced-refunds-on-credited-invoices). Previously you had to determine capture state yourself and choose between cancel and refund, and a refund on an uncaptured payment would fail.

### Fixes

bug-fix

* The refund button on a payment now only appears when the payment is authorised. It was previously shown on payments in an error state, where a refund is not possible.
* Adyen recurring SEPA direct debit: two metadata field keys that exceeded Adyen's 20-character limit have been shortened, with backwards compatibility, so these payments no longer fail metadata validation.

## Quotes

**Dynamic variables in quote templates**
[Quote templates](/platform-guides/customers-and-billing/quoting/quote-templates) and drafts now support dynamic variables such as `{{customer.name}}`, which are hydrated with the quote's own data when the quote is rendered. To insert a variable, type `{{` in the content editor and select it from the list. This builds on quote templates from the previous release, letting a single template adapt to each customer and quote instead of carrying hard-coded values.

## Customers

**Filter customers by role**
The customer list in Desk can now be filtered by role (Self billing, Non processing, Processing only), using the `role` filter already supported by [POST /v1/customers/search](https://docs.solvimon.com/api-docs/configuration-api/customers/post-customers-search). This makes it quicker to narrow a large customer list to the group you care about.

**Notification preferences on by default for contacts**
Contacts now have notification preferences enabled by default, matching the behaviour already in place for customers, so newly added contacts receive the relevant notifications without extra configuration.

## Workflows

**Action executions on the workflow detail page**
A workflow's [detail page in Desk](/platform-guides/workflows/monitoring-and-audit) now includes a table of its action executions, so you can see what a workflow has run and monitor its activity from one place.

## Meters and events

**Type to search meter property enum values**
Selecting a value from a [list-type (enum) meter property](/platform-guides/meter-and-event-design/meter/meter-properties) now supports typing to filter the options, instead of only scrolling. This is noticeably faster for long lists such as country codes.

## Integrations

### Fixes

bug-fix

* AvaTax: an invoice is now synced to AvaTax again when it transitions to final, so the tax provider always holds the finalized version of the document.

## Deprecated API fields

The following field is now deprecated. It continues to work for backwards compatibility, but we recommend migrating to the replacement.

* **`paid`** (Invoice): deprecated in favour of `payment_status`, which distinguishes `UNPAID`, `PARTIALLY_PAID`, `PAID` and `OVERPAID` rather than a single boolean.

## July 15, 2026

## Summary

This release brings flexible pricing for self-service wallet top-ups, webhook delivery events with resend, editable payment gateway integrations, Stripe payment-method and 3-D Secure fixes, split-by-meter-property pricing in Desk, and a range of invoicing, quote and meter improvements.

## Meters and events

**Delete events by reference**
Events (meter data) can now be deleted using their `reference` as a path parameter on the delete endpoint, in addition to the resource id. Previously deleting by reference was only possible via a `POST`, which made it hard to trace; you can now remove a specific event directly by the reference you sent when ingesting it.

**Use non-required meter values and properties in calculations and pricing rules**
The `required` flag on a meter value or property now only governs event validation on ingest; it no longer gates what can be referenced elsewhere. Non-required values and properties can now be used in meter value calculations and in pricing rule conditions, and Desk offers them in the usage-metric and pricing-rule editors. This lets you build calculations and conditional pricing on optional usage data without forcing it onto every event.

### Fixes

bug-fix

* A meter value or property can no longer be removed while it is still used by a feature, and a feature can no longer be created that references a value or property on a draft meter, preventing broken feature configurations.

## Pricing

**Flexible pricing for on-demand top-ups**
A new [`FLEXIBLE` pricing type](/platform-guides/products-and-pricing/pricing-plans/flexible-pricing) lets a customer choose how much to pay within a range and receive wallet credits in proportion to the amount. You set a minimum and maximum amount and a credit conversion rate on a one-off, on-demand product item; the customer pays anything within the range (for example between \$20 and \$100) and is granted the matching number of [wallet credits](/platform-guides/wallets-credits/credit-types-wallets). It is charged through the [on-demand](/platform-guides/products-and-pricing/pricing-plans/on-demand-charges) flow, so it fits self-service wallet top-ups where the customer decides the amount.

**Create pricing groups directly on a schedule**
You can now create pricing groups directly on a pricing plan schedule, without linking them to a pricing plan version, via the pricing group API. This complements pricing defined directly on a schedule, so a schedule's stand-alone pricing can also include grouped, selectable options rather than only flat pricings.

**Split pricing by meter property in Desk**
Split pricing by meter property is now available in Desk. When a usage-based product item's meter has meter properties, you can define split pricing on the pricing item config directly in the UI, instead of only through the API. This unblocks setting up contracts that rely on splitting usage across a property (for example per region or per SKU) without hand-crafting the configuration over the API.

## Quotes

**Leave the expiry date empty on an upgrade quote**
The quote upgrade flow now allows leaving the expiry date empty (`expired_at` can be null), matching the other quote flows, so an upgrade quote no longer has to carry an expiry date.

**Quote flow improvements**
A set of UX refinements to the quote creation flow in Desk, streamlining the steps and interactions when building a quote.

## Invoices

**Clearer "price per X units" on invoices**
Usage prices expressed per a number of units (for example a price per 1,000 API calls) are now displayed more clearly on invoices, so the unit basis of a charge is easier to read on the document.

### Fixes

bug-fix

* One-off lines added from a Deduction-type product can now be deleted from a draft invoice. Previously the delete icon only appeared for one-off lines from Revenue-type products, so deduction lines could not be removed once added.
* Fixed the wallet balance shown on the invoice, where expired wallet credits were not correctly deducted, so the wallet balance details on the invoice now reflect credit expiry correctly.

## Customers

### Fixes

bug-fix

* Fixed adding parent customers to a customer that already has parents. The form previously sent the existing parents as null values, which blocked adding more; you can now extend an existing parent list.

## Payments

**Stripe payment method options by currency and amount, and 3-D Secure handling**
Stripe payment method options are now filtered by the payment's currency and amount limits, so only methods that actually support that currency and amount are returned; previously a payment in one currency could be offered methods that don't support it. Alongside this, the Stripe authorisation flow and 3-D Secure handling were fixed so payments complete correctly after 3DS validation, and a Stripe customer is created when needed so the payment method can be stored rather than the payment being taken as a guest.

## Developers

**Webhook delivery events**
You can now inspect and replay individual [webhook](/platform-guides/for-developers/webhooks) deliveries. New read endpoints return the [delivery events](/platform-guides/for-developers/webhook-events) for a webhook, each including the action that triggered the delivery, the HTTP status code your endpoint returned, the request payload, and any failure details. A resend action lets you re-trigger a delivery after fixing an outage on your side, and Desk adds a matching view in the Developers area to browse a webhook's delivery events and resend one.

## Integrations

**Edit payment gateway integrations**
Payment gateway integrations can now be edited after creation. `PATCH /integrations/{id}` accepts updates to the integration's name, description and reference, so a typo or a rename no longer requires deleting and recreating the integration. In Desk, Adyen payment gateway integrations now have an Edit option (rotate the API key, update name, description and reference, and configure AFP blocks), and the AFP fields for `PLATFORM`-type Adyen integrations can now be filled in.

## Customer portal

### Fixes

bug-fix

* Fixed the quote preview in the customer portal not reflecting the saved quote. After saving a quote, the portal preview now shows the same up-to-date data as the Desk preview.

## July 8, 2026

## Summary

This release brings invoice export to your ERP from a workflow, Stripe in the hosted checkout, price indexing on schedules that carry their own pricing, and a broad set of pricing, quote and subscription improvements in Desk.

## Events

### See the subscription behind an event

Events can be ingested against a specific subscription, by passing a pricing plan subscription id or reference on [`POST /v1/events/ingest`](/platform-guides/meter-and-event-design/usage-events/event-ingestion-api), which is useful when you want usage attributed to one subscription directly. The Event panel now shows that pricing plan subscription, and the subscription id is included in the event metadata, so it's clear which subscription an event was booked against.

### Fixes

bug-fix

* Meter values and properties can no longer be archived while they're still used in meter value calculations that feed active products, which previously could leave a product referencing archived building blocks.
* The meter creation form now shows a usage-metric disclaimer when a value or property is added without a usage metric, and correctly hides the notice once a usage metric exists.

## Pricing

### Index prices on schedules that carry their own pricing

Price indexing when [copying a schedule](/platform-guides/customers-and-billing/subscriptions/schedule-configurations) now also works for schedules that define their pricing directly, rather than by referencing a pricing plan version. This closes the gap between two recent additions, [pricing defined directly on a schedule](/platform-guides/customers-and-billing/subscriptions/custom-pricing) and indexing all prices by a percentage when copying, so direct-priced schedules can be indexed the same way as plan-based ones.

### Pricing improvements in Desk

* You can add a new version to an active pricing plan directly, via a "Create new version" button, with a tooltip explaining that a new version is needed to add or edit pricings.
* The only remaining version of a pricing plan can no longer be deleted; instead you can clear it, removing all pricings, so a plan always keeps at least one version.
* When adding a discount, the relevant level is now preselected (invoice-discount level on a schedule, the product when adding to a product, and so on), saving a manual step.
* Conditional pricing is easier to set up, with more explicit actions in the creation panel.
* In the single-currency case, the currency for a new pricing item now defaults to the pricing plan version's currency.

### Fixes

bug-fix

* Archiving a pricing plan version is now blocked while it's still used by a cancelled schedule, which previously could break invoice refreshes for rate-card schedules with a "no applicable version" error.

## Quotes

### Sales representatives can complete upgrade quotes

Accepting an upgrade quote shows a modal to update the customer's details, but a sales representative only had view access to customers and was blocked at that step. Sales representatives now have a customer-contributor [capability](/platform-guides/platform-configuration/users-permissions/roles-permissions), so they can complete the acceptance flow without being handed broader customer-management rights.

### Fixes

bug-fix

* Quote links and the post-activation redirect now point to the correct subscription. Where a billing subscription existed, the quote overview and the redirect after activating a subscription from a quote could point at the billing subscription instead of the quote's own subscription.
* Submitting a quote that still has form errors is now blocked, so the finalize call isn't sent while the form is invalid.
* The company registration number is no longer required when verifying the customer profile during quote acceptance; it is now optional.

## Subscriptions

### Cancelling an approval request returns the subscription to draft

When an approval request for a subscription is cancelled, the subscription is now set back to `DRAFT`, so it clearly reflects that it still needs to go through [approval](/platform-guides/customers-and-billing/quoting/approval-policy) rather than being left in an in-between state.

### Subscription details in Desk

* Schedule start and end dates are now shown in the collapsed schedule view, and the end date of an initial term is shown on the input when you set one.
* When a customer has no country set, an "Add customer country" shortcut appears in the customer selector while creating a subscription, opening the edit-customer panel inline.
* The Additional details section now shows what's selected, including when default values apply, instead of hiding the chosen values.
* The recent-invoices list now has a loading state and refreshes automatically once the first invoices have been created after activating a subscription, since they are generated asynchronously.
* The edit-subscription-terms modal now uses radio groups instead of toggles for options such as combining subscriptions and split usage, for a clearer, more consistent form.

## Invoices

### Mixed tax-inclusive and tax-exclusive prices in combined invoices

When multiple subscriptions are [combined onto a single invoice](/platform-guides/customers-and-billing/subscriptions/subscription-groups), prices configured including tax and prices configured excluding tax are now handled correctly together on that combined invoice, so the totals are right regardless of how each subscription's prices were set up.

### Fixes

bug-fix

* Fixed invoice preview for rate cards where the rate card doesn't start on the day of the preview, for example previewing before a rate card's start date, which previously failed.

## Customers

### Filter for processing-only customers

You can now filter customers by whether they are processing-only, in both the API and Desk, making it easier to find and work with this group of customers.

## Payments

### Stripe in the hosted checkout

Stripe payments are now supported in the hosted payment web experience, so customers paying through the hosted checkout can pay via Stripe.

### External payments without a payment acceptor

For externally-created payments that Solvimon only receives, rather than initiates, the payment acceptor is no longer required: `paymentAcceptorResourceId` may be null, since the payment method and acceptor don't exist in the platform.

## Integrations

### Export invoices to your ERP from a workflow

A new [workflow action](/platform-guides/workflows/workflow-actions) lets you export an invoice to your [ERP](/integrations/enterprise-resource-planning-erp) as part of a workflow, so finalised invoices can flow into your accounting system automatically instead of being exported by hand.

## Deprecated API fields

The following field is now deprecated. It continues to work for backwards compatibility, but we recommend not relying on it.

* **`total_number_of_pages`** (paginated list responses): no longer supported; kept only as a legacy page-count value for compatibility.

## July 1, 2026

## Summary

This release brings mid-term subscription upgrades, pricing directly on a schedule, custom fields on price plans and product items, conditional coupon discounts, new payment capabilities (direct authorisation and reverse), a redesigned quote creation flow, attachments on subscriptions, and a range of invoicing, reporting and Desk improvements.

## Events

**Event processing details**
The event processing details panel has been reworked so you can see exactly how an event was handled. The event timeline now uses a compact card design that lets you scan the full processing history at a glance, and the panel includes the raw ingest data received for the event. Understanding why an event was priced, persisted or skipped no longer requires leaving Desk or digging through logs.

## Pricing

### Conditional discounts on coupons

Conditional discounts (discounts that apply only when certain conditions are met) can now be attached to [coupons](/platform-guides/products-and-pricing/product-catalog/coupons). Previously you had to configure a conditional discount on each subscription individually; now you can define it once as a coupon and apply the same standardised discount across many subscriptions, with less manual work and less room for inconsistency.

### Conditions on tiered pricing

You can now define conditions directly on the pricing item config of a `TIERING` pricing metric (`tiering_conditions`). This lets you vary how [tiered event pricing](/platform-guides/products-and-pricing/pricing-plans/usage-based-pricing-methods/tiered-pricing) is applied based on the conditions you set, giving you finer control over usage-based charging without splitting it across multiple product items. This is available via the API for now; support in Desk will follow soon.

### Custom fields on price plans

Price plans can now carry [custom fields](/platform-guides/platform-configuration/custom-fields), so you can enrich a plan with your own structured metadata, for example to categorise plans as Trial vs Standard. Those custom field values are also available as a criterion in the price plan search, letting you find and group plans by your own categorisation rather than only by name or ID.

### Custom fields on product items

Product items now support custom fields as well, letting you attach additional structured data to an item. This is especially useful for integrations: for example, you can store the identifiers needed to map a product item onto a product or revenue line in an external ERP, so downstream systems can reconcile against your own references.

### Index prices by a percentage when copying a schedule

When you [copy a schedule](/platform-guides/customers-and-billing/subscriptions/schedule-configurations) you can now index all price overrides at once by a percentage, instead of recalculating and re-entering each price by hand. A tedious, error-prone task, such as applying an annual price increase across a customer's negotiated prices, becomes a single action.

### Define pricing directly on a schedule

A pricing plan schedule can now carry its own pricing directly, through an optional `pricing_categories` field with the same structure as a pricing plan version (pricings, items, configs and bands, including pricing groups). Until now a schedule could only get its pricing by referencing a pricing plan version or by resolving it from a rate card. Defining bespoke pricing on the schedule itself is ideal for one-off, customer-specific arrangements that don't warrant a separate pricing plan version. See [Custom pricing](/platform-guides/customers-and-billing/subscriptions/custom-pricing) for the other ways to tailor pricing per subscription.

## Quotes

### New quote creation flow

Creating a quote now uses a redesigned init flow that simplifies the steps from "new quote" to a ready-to-send document, making the starting point clearer and faster to work through.

### Auto-fill VAT and registration details on quotes

A new CPQ content block automatically generates the VAT and company registration details on a quote, filling them in directly from the quote's data. Instead of copying these legal details in by hand for every quote, the block keeps them accurate and consistent with the underlying quote, reducing manual effort and mistakes on outgoing documents.

### Mid-term subscription upgrades

Upgrade quotes now support mid-term upgrades: upgrading a subscription part-way through a billing period. When such an upgrade is accepted and activated, the fee for the original subscription is [credited pro rata](/platform-guides/customers-and-billing/subscriptions/billing-cycles-proration) for the unused part of the period, and the new subscription's fee is invoiced in full, so the customer is billed correctly for the switch. The upgrade quoting flow lets you choose the crediting options that apply, and the quote view now shows direct links to the subscriptions the upgrade moves from and to, so the relationship between the old and new subscription is always traceable.

### Clearer share actions on published quotes

Once a quote is published, a share message now appears at the top of the page, making it immediately obvious that the quote is ready and how to send it to the customer.

### Fixes

bug-fix

* Finalising a quote that still has an invalid configuration, for example a pricing group that requires exactly one selection but has none, is now blocked, preventing broken quotes from being finalised.
* Switching the plan during an upgrade no longer carries over stale data (enabled pricings, overrides) from the previously selected pricing plan.
* Fixed the quotes search returning fewer results than expected when no `order_by` parameter was provided, so the Quotes page now consistently shows all matching quotes.

## Subscriptions

### Attachments on subscriptions

You can now add attachments to subscriptions, the same way you already can on other resources. Keep supporting documents, such as signed agreements and purchase orders, together with the subscription they belong to.

### Realign the invoice date while the period is still open

The [billing cycle](/platform-guides/customers-and-billing/subscriptions/billing-cycles-proration) and invoice date can now be realigned while the current period is still open, rather than only after it has closed, giving you more flexibility to correct or adjust billing timing when it matters. In addition, when creating a schedule, Desk now offers an option to realign the invoice date to the schedule's start date directly at creation time.

### Hide inactive add-ons

Inactive add-ons are now hidden by default on active, voided and cancelled subscriptions, keeping the subscription view focused on what's actually in effect. When you do need to see them, a "Show inactive addons" option in the context menu brings them back into view.

### Auto-refresh after saving a subscription

The invoice list and invoice preview now refresh automatically after a subscription is saved or activated, so you immediately see the resulting invoices and preview reflect your changes without a manual page refresh.

## Customers

### More parents per customer

A customer can now have up to 10 [parents](/platform-guides/customers-and-billing/customer/parent-child-relationships), up from the previous limit of 5, provided the additional parents are non-processing customers. This gives more headroom for modelling complex account hierarchies, such as reporting or grouping structures, without being constrained by the old cap.

## Invoices

### Credit and duplicate an invoice via the API

You can now [credit an invoice](/platform-guides/invoicing/invoice-corrections/credit-invoices) and create a corrected copy of it in a single API call. This is designed for the common correction scenario where a finalised invoice had the wrong details, for example an incorrect address or tax setting, and you want to credit the original and reissue a fixed version in one step rather than as two separate actions.

### Search invoices by billing period date

Invoices can now be filtered by billing period date. This is especially valuable when the invoice date is only set at finalisation, as it is for customers who define the invoice date when the invoice goes to `FINAL`, because in those cases the invoice date alone no longer tells you which period an invoice covers. Filtering on the billing period date surfaces the invoices for a given period, even for backdated subscriptions or invoices.

## Payments

### Direct authorisation

Payments can now be authorised directly through the `/payments/authorise` action using a new `DIRECT` payment context type. This supports flows where you want to authorise a payment on its own, independent of the usual invoice-driven context.

### Reverse a payment

A new reverse action lets you reverse a payment that has not yet been captured at the payment provider. Previously a refund would fail in this situation, because an uncaptured payment has to be cancelled rather than refunded. With the reverse action, Solvimon determines whether the payment needs to be cancelled or refunded and performs the correct operation, so you no longer have to track the capture state yourself or handle the two cases differently in your integration.

## Reporting

### Invoice tax and header report improvements

The invoice tax report now includes the tax note code (`tax_categories.notes`) and the "Invoice to Final at" timestamp (`updated_to_final_at`), and the [invoice header report](/platform-guides/analytics-reporting/report-downloads) now also includes "Invoice to Final at". These additions help you reconcile tax reporting and see exactly when each invoice was finalised.

### Enabled pricings on the subscription report

The Price Plan Subscription report now includes an "Enabled Pricings IDs" column, listing the enabled pricings from the subscription's schedule. You can see, per subscription, exactly which pricings are active directly in the report, which is useful for auditing and analysis without opening each subscription individually.

## Integrations

### Email: invoice template name for your own email integration

Platforms that send email through their own (non-SYSTEM) email integration can now set the invoice template name used for their outgoing emails. This gives you control over the template applied to your invoice emails when you bring your own email account, rather than being limited to the system default.

## Desk

### Activate pricing plans from the bottom bar

Draft pricing plans now have an "Activate pricing plan" button in a bottom bar, matching the pattern already used on products, so activating a plan is available right where you're working.

### Reuse references of archived resources

When a resource is archived, its reference is now suffixed, so you can immediately reuse the original reference for a new resource instead of running into a uniqueness conflict.

### Clearer metric filters

Metric filter availability logic has been fixed and the filter labels renamed, so the available filters are correct and clearer to understand.

### Expanded product card on the subscription timeline

The first product card is now expanded automatically when a new subscription's pricing plan is rendered on the subscription timeline, so the most relevant details are visible straight away.

### Fixes

bug-fix

* Fixed the "from" and "reply-to" email addresses not rendering correctly in the action workflow view.

## June 24, 2026

## Summary

This release brings dynamic meter creation during ingest (send events now, decide what to bill for later), quote templates, on-demand charging end-to-end (API and portal), a new Metronome integration, more control over credited invoices, and a range of subscription, invoicing and Desk improvements.

## Event ingestion

**Create meters on the fly during ingest**
You can now feed Solvimon events without configuring [meters](/platform-guides/meter-and-event-design/meter) first, and figure out what to bill for later. [`POST /v1/events/ingest`](/platform-guides/meter-and-event-design/usage-events/event-ingestion-api) can create meters, properties and values automatically for data that doesn't exist yet: send the event, and Solvimon sets up the underlying meter on the fly.

This changes how you can approach metering. Instead of deciding up front which usage is billable, you can stream everything that might matter and treat that decision as a downstream one: some events stay as signals you observe and analyse, others become the basis of a charge once you attach pricing to them. It also streamlines onboarding, since getting a working event feed no longer depends on a fully configured meter catalog, and it gives your metering setup room to evolve with your product, driven by the events you actually see rather than the ones you predicted.

A new `meter_validation` query parameter controls the behaviour: `STATIC` (the default, unchanged) requires the meter to already exist and rejects the event otherwise, while `DYNAMIC` creates whatever is missing and then processes the event against it. Concurrent creation of the same meter is handled safely, so two events racing to create the same meter won't fail.

## Pricing

**Charge on-demand (one-off) pricing items**
Some charges don't fit a recurring schedule: a one-time setup fee, or an [add-on](/platform-guides/products-and-pricing/pricing-plans/add-ons) a customer buys on the spot. You can now flag selected [one-off](/platform-guides/products-and-pricing/pricing-plans/one-off-pricing) product items as "on-demand" and charge them on top of an existing subscription whenever you need to, on their own invoice and with an optional preview first. See [On-demand charges](/platform-guides/products-and-pricing/pricing-plans/on-demand-charges).

* Retrieve all on-demand pricing items available for a schedule, so you can present them to your customers, for example as buy buttons in your own UI or portal.
* Charge one or more on-demand items, optionally previewing the resulting invoice before it is created, and optionally finalising it in a single step.

**Nested pricing improvements in Desk**
Working with deeply nested pricings is now more manageable. You can collapse and expand individual nesting levels to focus on the part you're editing, manage and edit the pricings per level, and expand or collapse all levels at once from the context menu, making complex pricing structures clearer to navigate and reason about.

## Quotes

**Quote templates**
You can now configure reusable quote templates in Desk that define the defaults for a quote, such as the pricing plan, subscription settings, and content blocks. When creating a new quote you can select a template to pre-fill it, so quotes start from a consistent, approved baseline instead of being built from scratch each time. This speeds up quote creation, reduces manual entry, and keeps quotes consistent across your sales and billing teams. Templates also support attachments, which are carried onto the quotes created from them.

**New quote renderer enabled by default**
The new quote renderer is now the default for everyone, and the previous developer-tool toggle has been removed, so all quotes are produced by the new renderer without any opt-in. This release also adds the remaining translations and refinements needed to fully support it.

**Set subscription custom fields when creating a quote**
When creating a quote (on quote init or on a quote upgrade), you can now set the [custom fields](/platform-guides/platform-configuration/custom-fields) for the subscription that the quote will create. The values are stored on the quote and carry over automatically once it is finalised into a subscription, so you don't have to go back and fill them in afterwards.

**Submit custom fields via URL parameter**
Quote and subscription forms can now receive `custom_fields` through URL parameters (for `v1/quotes/init`, `v1/quotes/upgrade` and `v1/pricing-plan-subscriptions/init`). This makes it possible to pre-fill custom fields when the form is embedded in an iframe, for example in Salesforce, where the embedding page can't control the request body, so values from the host system flow straight into the form.

**Quotes only for active pricing plan versions**
Quotes can now only be created for *active* pricing plan versions. This closes a gap where a quote could be built on a draft pricing plan version and then finalised, resulting in an active subscription running on a draft plan: a state you generally never want in production.

### Fixes

bug-fix

* Subscription details (terms) can now be edited correctly while building a quote, where previously saving appeared to be required but had no effect.
* Signers created from an existing contact are no longer shown as "Unknown Contact" when a quote is accepted; the contact is now resolved correctly.

## Customer portal

**Upgrade subscriptions with on-demand items**
The upgrade-subscription flow in the customer portal is now wired to the on-demand pricing endpoints, replacing the earlier placeholder behaviour. Your customers can upgrade their subscription and add on-demand items to it directly from the portal, in a self-service flow.

**Quote attachments in the portal**
Attachments on a quote are now shown in the Quote Portal and can be downloaded by your customers, so any supporting documents you include on a quote are available to them directly. (Attachments can only be viewed and downloaded from the portal, not added.)

## Invoices

**Choose the source of truth for credit note details**
When you [credit a `FINAL` invoice](/platform-guides/invoicing/invoice-corrections/credit-invoices), the credit note is created in `DRAFT`, and by default its customer and billing details are refreshed to the customer's *current* values, which means a credit note can end up showing details that differ from the invoice it is crediting (for example if the address changed in the meantime). You can now control this explicitly with a new `credit_details_source` option:

* `CURRENT` (default, unchanged behaviour): the credit note is refreshed with the customer's *current* details.
* `ORIGINAL`: the customer and billing entity are copied over *exactly* from the original invoice, without a refresh. Use this when the credit note must mirror the invoice it credits, even if the customer's address or details have since changed.

See [Credit note customer details](/platform-guides/invoicing/invoice-corrections/credit-invoices#customer-details-on-the-credit-note).

### Fixes

bug-fix

* Fixed creating a one-off invoice for a billing entity when the platform's default payment acceptor belonged to a *different* billing entity. One-off invoice creation now works correctly across billing entities, regardless of which one holds the platform default.
* Fixed a bug that prevented generating an invoice preview for a customer that has no address on file, so previews now work for these customers too.
* Fixed a case where, after copying a schedule and then changing its end date, the next billing period's invoice could be skipped. Invoices are now generated reliably in this scenario.

## Customers

**New `PROCESSING_ONLY` customer role**
Processing-only customers can now be expressed through the roles model with a dedicated `PROCESSING_ONLY` role, alongside the existing boolean flag; the two are kept in sync so existing integrations keep working unchanged. As with the previous flag, a customer with the `PROCESSING_ONLY` role cannot hold any other roles, keeping processing-only customers clearly distinct.

## Subscriptions

**Upgrade before the initial term has expired**
You can now upgrade a subscription even when its initial (committed) term has not yet ended. Previously this was blocked, forcing you to wait until the initial term expired before an upgrade could go through; now you can process the upgrade whenever the customer wants to move up.

**Filter subscriptions by multiple statuses**
The `POST /v1/pricing-plan-subscriptions/search` endpoint now lets you filter by multiple statuses in a single request, so you can retrieve, for example, all active *and* cancelled subscriptions at once instead of querying each status separately.

### Fixes

bug-fix

* Fixed an issue when editing subscription terms where removing a custom field value stored an empty string instead of actually clearing the field. Empty custom fields are now correctly treated as "not set", so clearing a value has the effect you'd expect.

## Integrations

**Metronome integration**
Added a [Metronome integration](/integrations/data-imports/metronome) to Desk, available under Settings → Integrations, for syncing your usage and billing data with Metronome.

## Currencies

**Added SLE and deprecated SLL**
Added support for the new Sierra Leonean Leone (`SLE`) and deprecated the old `SLL`, following the currency's redenomination, so conversions and pricing use the correct, current currency.

## Desk

**Clearer revenue item labels**
The revenue item panel now uses clearer labels ("Add revenue item", "Revenue type") to better describe what you're configuring.

### Fixes

bug-fix

* Fixed adding metric rules from the UI, which had stopped working.
* Fixed the context menu on the "Customize price rule header text" block not showing when the header text is long.

## Deprecated API fields

The following fields and values are now deprecated. They continue to work for backwards compatibility, but we recommend migrating to the replacements.

* **`processing_only`** (Customer): deprecated in favour of the new `PROCESSING_ONLY` role. Use the `roles` field instead.
* **`forceInitOnHold`** (invoice credit request): deprecated in favour of the snake_case `force_init_on_hold` field.
* **`SLL`** currency: deprecated; use `SLE` instead.

## June 17, 2026

## Summary

This release adds conditional discounts, duplicating pricings within a pricing group, filtering invoices that have credit invoices, payment improvements, and a set of invoicing and Desk fixes.

## Pricing

**Conditional discounts**
You can now attach conditions to a discount, so it only applies when the condition is met: for example, apply a percentage discount only when the invoice total (before taxes) is above or below a threshold you set. This lets you model common commercial arrangements such as volume-based or minimum-spend discounts directly in the pricing configuration instead of handling them manually. Conditional discounts are configured on the pricing plan schedule (`POST`/`PATCH /pricing-plan-schedules`) and returned when you read it.

**Duplicate pricings within a pricing group**
Building pricing groups is now faster: you can duplicate an existing pricing and add it as an additional option in the group via a new "Duplicate pricing" action in the context menu, which opens the "Add pricing" panel pre-filled from the original, so you no longer have to re-enter a near-identical price from scratch.

## Invoices

**Filter invoices that have credit invoices**
Invoice search now offers an option to filter for invoices that have credit invoices linked to them, making it easier to find and review invoices that have been (partially) credited.

### Fixes

bug-fix

* Restored the default invoice ordering to newest-first. Recent invoices in Desk (and calls that don't specify an `order_by`) had stopped returning invoices with the newest first; they now do again.

## Payments

**Payment improvements**
Payments can now be filtered by their external reference, so you can look a payment up directly by the reference from your own system. In addition, an invoice's paid quantity and payment status are now kept up to date as payments change, so the invoice always reflects the true payment state.

### Fixes

bug-fix

* An integration can no longer be archived while it still has an active payment acceptor under a billing entity. Previously this was allowed and could leave a billing entity pointing at an archived integration; you now have to archive the payment acceptors first.

## Desk

**Guidance in the meter creation panel**
The meter creation panel now includes inline help, giving extra context about meters, values and properties while you set them up.

### Fixes

bug-fix

* Fixed adding a commitment on a schedule other than the first one, which previously did nothing when the "Add commitment" button was clicked.

## June 10, 2026

## Summary

This release brings coupons for the first period, quote fields for CRM sync, manual invoice sending, Adyen onboarding (KYC), richer cost predictions, self-service sandbox provisioning, and a broad set of invoicing and Desk fixes.

## Pricing

**Coupons for the first period**
Coupons can now be configured to apply to the first period only, so you can give a customer an introductory discount on their first invoice without it carrying over to subsequent periods.

## Quotes

**First invoice amount stored on the quote**
The result of a quote's invoice preview, in particular the first invoice amount, is now stored in a field on the quote. This makes the expected first-invoice value directly available on the quote, so it can be surfaced or synced to an external system such as a CRM (for example Salesforce) without recomputing it.

**Custom field on the quote object**
Quotes can now carry a [custom field](/platform-guides/platform-configuration/custom-fields) of their own, letting you store an external identifier (such as a CRM organisation ID) directly on the quote for reconciliation with your other systems.

## Invoices

**Send invoices manually**
You can now configure, at platform level, that invoices are only sent manually rather than automatically, giving you a final review step and full control over when an invoice actually goes out to the customer.

**Resubmit an e-invoice even when it succeeded**
It's now possible to resubmit an e-invoice from the UI even when its status is already `SUCCESS` or `IGNORE`. Previously resubmission was only allowed for e-invoices that hadn't reached those states; you can now re-send when needed regardless of the current status.

### Fixes

bug-fix

* Fixed weekly (WEEK CALENDAR) proration: weekly-priced items were being billed at the full-week rate for partial weeks, and are now prorated correctly.
* Fixed a case where adding one-off invoice lines could corrupt the invoice due to tax category handling.
* One-off products that aren't tied to a subscription now correctly have their sales tax loaded.
* Fixed an invoice update failing with a "group amount does not match tax category summaries" error when combining percentage-based pricing with a fixed-amount total discount.
* Added validation when adding a one-off invoice line with flat pricing but no volume.
* The Desk invoice view now shows the missing information for "Already deducted" one-off deduction lines.

## Payments

**Adyen onboarding (KYC)**
This release adds support for Adyen KYC onboarding. A new `POST /v1/adyen/kyc/sessions` endpoint creates a hosted-onboarding session for a legal entity, and Desk now surfaces the onboarding flow: a warning banner appears on the Payments overview when an active Adyen integration has an unverified legal entity, with an "Update details" action that opens Adyen's hosted onboarding, and the onboarding component now lives on the payout page. In addition, payment-method tokenization initiated from Desk now uses the correct `Moto` shopper interaction instead of `Ecommerce`.

## Cost prediction

**More detail in the cost prediction response**
The cost prediction response now includes the wallet balance and the `invoice_id`, so a prediction carries more of the context you need to act on it without additional lookups.

## Reporting

**Invoice usage report file-to-file transfer**
Invoice usage reports can now be delivered via file-to-file transfer. This is far more efficient for very large usage report files, which can be streamed and synced directly instead of being processed line by line.

## Sandboxes

**Self-service sandbox provisioning**
You can now provision a sandbox self-service, without manual setup on our side. A sandbox token is requested with an email address and a captcha (for bot protection), and a new sandbox platform is created automatically, including a default payments (Adyen) system integration, so you can start testing payments straight away.

## Desk

**Choose a role when creating an API key**
When creating an API key you can now choose its role, `ADMINISTRATOR` or `READ_ONLY`, so you can hand out read-only keys where full access isn't needed.

### Fixes

bug-fix

* Fixed several seat-count editing bugs, including saves failing on draft subscriptions and on schedules with an end date, caused by the frontend re-sending an `end_at` that the backend assigns itself.
* Fixed slow loading of subscriptions that have complex pricing plans.