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 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 took one request for each status. A line reports IMPORTED, DONE, SKIPPED or ERROR. The same statuses[] parameter is now on GET /v1/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
- A cost prediction for a configuration that the engine does not price yet now reports
NOT_MATCHED. The separateNOT_SUPPORTEDstatus 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 with a quote_version_id reports what finalizing that quote version would do about approval. It creates and changes nothing:
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, where it would drift from the evaluation on the server. The endpoint needs QUOTE.VIEW and APPROVAL_POLICY.VIEW. See Approval policies.
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.
Invoices
A short-lived link to the PDF GET /v1/invoices/{id}/download-url and GET /v1/quote-versions/{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.
Fixes
- Paging GET /v1/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 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
- Deleting a draft subscription now releases the coupon 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_BILLINGsubscription 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 toDEFAULT.
Customers
Only a draft customer can be deleted
DELETE /v1/customers/{id} is now refused unless the 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. The archive keeps its history.
Customer portal
Terms and conditions on a checkout page
A checkout page 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 and PATCH /v1/portal-urls/{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 from the customer portal. They select the item, set a quantity on a one-off 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 or the 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 and the Claude integration.
Desk
Enum and boolean custom fields Creating a custom field in Desk now offers the types that POST /v1/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 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 gained this last release as applicable_for_clauses. The option appears only when the invoice has subscription clauses to apply.
Fixes
- The status filter on products and pricing plans no longer starts on “Deprecated”. The default hid the active resources that you were looking for.
- Publishing a checkout page 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): usestatuses[]instead. It takes one or more line statuses in a single request, wherestatustakes exactly one.
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 condition can now refer to a meter property variable. 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.
To create a variable, use POST /v1/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. The request contains an add set and a remove set. Solvimon applies the two sets as one change:
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 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 therefore stays short when a list is long. You can expand 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. Solvimon rejects DELETE /v1/meter-property-variables/{id} while a condition refers to the variable.
A quantity for a one-off item in the hosted checkout
A one-off 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. A customer who buys through the hosted checkout can therefore order three units. POST /v1/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
- When you override one pricing on a schedule, you no longer have to send the discounts, commitments and markups that you did not change. PATCH /v1/pricing-plan-schedules/{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} now expands
pricing_plan_version_idfor a subscription on a rate card 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 can now use the invoice-level 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 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 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 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}.
Fixes
- Solvimon now validates an invoice number prefix that refers to a custom field 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} returns the default for
self_billing_invoice_number_configurationandself_billing_credit_invoice_number_configurationwhen you did not set them. - The invoice PDF from GET /v1/invoices/{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. 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}.
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 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 accepts these content_type values:
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 now accepts a name, as a subscription already does. A sales team with more than one quote 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 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}:
The same object applies to PATCH /v1/products/{id}, PATCH /v1/product-items/{id} and PATCH /v1/quote-templates/{id}. Desk has an input for the guidance on each of these resources. See Agent context.
Event ingestion
Fixes
- POST /v1/ingest/meter-data/{id}/delete now returns
deleted: true. Before, Solvimon deleted the event but returned the event as it was before the update. The response therefore showedfalse, while the next GET /v1/ingest/meter-data/{id} showedtrue. See Event ingestion API.
Desk
Desk shows a wallet funded with an amount as a currency balance
Desk now shows a CREDIT wallet type that is funded with an amount as a currency balance. This applies to the balance, the top-up and the deduction on the invoice line. 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 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, POST /v1/approval-policies/{id}/deprecate and POST /v1/approval-policies/{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 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 with CUSTOM_FIELD in types. See 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 item priced FLAT. Set the quantity when you add the item.
Fixes
- 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.
- The event filter now selects a meter by its reference, not by its name. Event ingestion also uses the reference.
- The events screen now separates the table from the detail panel. The filter no longer shows two clear buttons.
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 now accepts PRICING_PLAN_SCHEDULE as its source resource type. When such a policy applies, POST /v1/pricing-plan-schedules/{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, POST /v1/approval-requests/{id}/decline, and POST /v1/approval-requests/{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, POST /v1/approval-requests/decline, and POST /v1/approval-requests/cancel.
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}. 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 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.
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 and POST /v1/pricing-plan-schedules/{id}/copy now accept coupon_discount_behaviour. This field sets what happens to the coupons that are still active when you replace the schedule:
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.
A discount can be backdated while its invoices are still draft
A discount 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.
An on-demand charge started from a payment carries its quantity
When you charge an on-demand item through POST /v1/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 item priced FLAT for the units ordered, and a flexible item for the agreed amount. FIXED items work as before.
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 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 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:
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 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:
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.
A grant 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 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 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}. Then change the status. See Seat-based credits.
Fixes
- You can now remove a billing email. PATCH /v1/customers/{id} with
{"email": null}used to returnINVALID_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. See Tax calculation and integrations.
Fixes
- Custom fields now persist after an invoice refresh. A
CUSTOM_FIELDrefresh with POST /v1/invoices/{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 nextGETreturned the values from before the refresh.
Workflows
Filter and expand the resources a workflow ran against
POST /v1/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 now expand 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 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 can now test invoice.billing_entity.id. A platform that bills from several 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.
Fixes
- An
EXPORT_TO_ERPaction 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 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 or resend its penalty box payload. Reactivate the webhook with PATCH /v1/webhooks/{id} first. Reactivation also clears the reason. See Webhooks.
Customer portal
German and Italian
The customer portal and 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 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 and paying through the 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 of type PAYMENT_GATEWAY on Adyen limits the methods it offers to a set you select. For example, one billing entity 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.
Desk
Manage pricing plan groups in Desk A pricing plan group 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. 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. You deprecate and reactivate groups from the same context menu as the other catalog resources.
Fixes
- Desk now creates a user with a role. 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 now clears it. An absent
end_atmeans “keep the end date of the source schedule”, and only an explicitnullmeans “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 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): usewallet_balancesinstead. 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 theused_balance,left_balance, andavailable_balanceof the matching entry inwallet_balancesinstead. These fields carry credits or an amount.
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 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 overrides the default per subscription. It is keyed on the pricing item config, the same way seats_values is:
On an on-demand charge, 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.
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 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 performs it:
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:
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.
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.
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.
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.
Fixes
- 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}/migrateused to fail withPricing ... 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. It keeps each seat’s credits to that seat: one grant per active seat assignment, 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 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.
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:
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.
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.
The new invoice PDF renderer is enabled per platform The rendering service 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.
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 failed in one request. Before this, you had to read the execution log and look each invoice up yourself.
Fixes
- You can now finalise a credit note against a one-off invoice without a manual refresh. The
PATCHtoFINALused 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, 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.
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 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 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
- 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.
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.
Fixes
- A seats value has to be a number.
default_seats_valueon a pricing item config was accepted as an amount, which is not a seat count anything could multiply by, and is now rejected withseats 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 and Credit types & wallets.
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.
A repeated on-demand item grants its credits per line Charging on demand 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.
Customers
Manage seat assignments from Desk
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 and Seat-based credits.
Invoices
Fixes
- Refunded payments are accounted for in the invoice amounts. An invoice that was paid and then partly refunded could report a negative
open_invoice_amountand apaid_invoice_amountabove its own total. A payment pending refund or reversal still counts as paid until the refund settles; a settled refund no longer does. payment_statusis 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 reportingNOT_PAIDorPARTIALLY_PAID. See 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 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.
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:
A consumer reading open_balance.quantity has to move to open_balance.credits.quantity. The send-email action 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.
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.
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
- 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 are off.
- Report generation requests that are
DONEare 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.
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 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 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.
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.
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:
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.
Subscriptions
Preview an invoice for a subscription that has only draft schedules A subscription whose schedules are all still drafts 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.
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.
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 into the provider without correlating timestamps.
More template variables on the send-email action
The native send-email action 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
- 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.
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 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
- Duplicate conditions on pricing item configs now fail validation. Before, a
PATCHcould 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.
Wallets and credits
Fixes
- Solvimon now compares the automatic top-up 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 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 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
- Desk now reads the invoice payment status from
payment_status, and no longer from the deprecatedpaidflag. Ifpayment_statusis not present, Desk usespaid. Desk now shows each status between unpaid and paid, for examplePARTIALLY_PAID. Before, Desk showed these statuses as “not paid”. - The status icon on an 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 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.
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.
Fixes
- The execution table for workflow actions now also shows the
PENDINGandNOT_EXECUTEDstatuses.NOT_EXECUTEDshows that the conditions of the action did not apply.PENDINGshows that the action has not run. See Execution statuses.
Meters and events
Edit a meter created during ingestion
Before, you could use a meter from 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 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
- 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.
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 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 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} 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
- An invoice that contains a deduction can reach
PAIDagain. 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 stayedPARTIALLY_PAIDforever. It now compares against the open amount, which is what “paid” means for such an invoice. - Wallet grants that came from an invoice are reversed when that invoice is voided, so voiding a top-up no longer leaves the customer with credits they did not pay for.
- Credit notes 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. 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, 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
- 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_FIELDerror but created the quote and its version anyway, leaving an invalid quote behind.
Meters and events
Reactivate a deprecated meter
A 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. 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 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.
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 on their own subscription from the 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 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 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 grant is booked against the invoice as it is created. See Wallet top-ups.
Automatic top-ups from the portal
Automatic 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 can now expand charge_on_demand_pricing_items, which lists the on-demand pricing items 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}, by passing a portal_url_id. This lets you point a finalized invoice at a specific published portal URL 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 for how those URLs are created.
Fixes
- 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_sourcein 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
- 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
- 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 now supports 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 screen gained a Status column and a per-row menu, backed by PATCH /v1/api-keys/{id}.
Fixes
PATCH /v1/api-keys/{id}no longer returns a 404 for keys that are visible throughGET /v1/api-keys. The handler resolved the key against the authenticated user’s entity instead of the platform selected throughX-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 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
- 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
- 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.
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: it links a customer_id to a meter_value_calculation_id 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 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: 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 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, 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 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, 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 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
- 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 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 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
- 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
- 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
- API requests with malformed URLs containing empty path segments now return a proper error response instead of a 500.