Summary
A one-off item priced FLAT can now bill a quantity instead of a single lump sum. Four products at 100 EUR is a unit price and a count, not an amount somebody multiplied by hand. A subscription can move between the plans of a pricing plan group in one call, which is the part the group resource has been waiting for since it was introduced. Credits can now be owned by an individual seat rather than pooled across the customer. An invoice that spent an amount-denominated wallet books that money out when it goes final. E-invoicing gets a downloadable report of its own. Also: an invoice’s payment status is patchable. Workflows can export to an ERP and tag the emails they send. An integration can be addressed by its reference.
Pricing
A one-off item can bill a unit price and a quantity
A one-off charge could only carry a single lump sum, so selling four products at 100 EUR meant working out 400 EUR and entering that. A one-off pricing item config priced FLAT now carries a unit count, the 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 and remains a lump sum.
The count is set in three places. default_units on the pricing item config is the plan-level default, and is read as 1 when absent. units on a pricing plan schedule overrides that per subscription, keyed on the pricing item config the way seats_values is:
On an on-demand charge the caller states the quantity itself. units is required for a FLAT item and must be positive. It deliberately does not fall back to default_units: that value is a prefill for an ordering flow, not a quantity anyone confirmed.
A unit count is a number, so default_units.amount and default_units.count are rejected in favour of number. Both fields are only accepted on a one-off item priced FLAT. A count of zero is valid on a schedule and bills no line. Ordering zero units on demand is rejected, because that is not an order. A fixed wallet grant on such an item scales by the unit count: a top-up product granting 500 credits per unit grants 1500 for three. A grant that converts from the paid amount, or takes a percentage of it, cannot scale that 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, without a way to execute the move. POST /v1/pricing-plan-subscriptions/{id}/change-plan performs it:
The target has to be a member of the same group as the subscription’s current plan, and that group has to be ACTIVE. Direction is inferred from the members’ order. Lower to higher is an upgrade and follows the group’s upgrade_type; higher to lower is a downgrade and follows its downgrade_type. That policy then decides the timing. IMMEDIATE and IMMEDIATE_PRO_RATA start now, or at a start_at you supply. NEXT_BILLING_PERIOD starts at the next invoice date and refuses a caller-supplied start_at. NOT_ALLOWED rejects the transition outright. A plan sold in several billing periods can also be moved between them: pass billing_period with the same pricing_plan_id, and the direction 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. The optional pricings, commitments, discounts and seats configured on the schedule it replaces are not carried over. It works whether the current schedule follows a pricing plan version or a rate card, and the currency is validated 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. That is what a self-service plan picker needs to know which plans it may offer.
A credits-priced config takes its periods from the credits item
A pricing item config that prices its usage in credits declared a billing_period and a pricing_period of its own. These could disagree with the CREDITS item that settles the credit type it consumes. Where they did, each pricing period’s overage landed on an invoice of its own instead of on the one invoice of the billing period: August’s usage was invoiced on 1 September rather than 1 October. Both periods are now inherited from the credits item. That is what keeps the overage and the credits it draws on the same invoice. Three configurations that used to be accepted are now rejected: own periods on a credits-priced config, credits items that disagree on a cadence, and a pricing period wider than its billing period. See Credits in pricing plans.
A draft schedule can be corrected and a draft subscription removed Two validations treated a draft as though it were already billing. A draft schedule’s start date could not be moved into the past. That is the ordinary case: the 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. Both are allowed now. See Draft schedules.
Fixes
- A discount can be added to a product item a coupon already discounts. The request failed with
start_at before previous, needs to start afterwhatever dates you sent. The index it named 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 succeeds.
POST /v1/pricing-plan-schedules/{id}/migratefailed withPricing ... not in the pricing plan version ...whenever the schedule had any override, even though the request never touches overrides. The override was being resolved 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 passed 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, where any seat can spend any credit. PER_SEAT is now implemented as the third wallet grant source. It keeps each seat’s credits to that seat: one grant per active seat assignment, restricted to the customer occupying it. A seat spends its own grant plus any unrestricted grant in the same wallet, so a personal allowance and a shared pool can sit side by side.
The quantity is prorated by how much of the period the assignment covers. A seat handed over mid-month grants the incoming occupant the remainder, not a full allowance. Assignments are not capped against the seats that were 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. That excess is reported rather than withheld, 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 has to be attributable to one seat. PER_SEAT therefore requires a fixed credits_grant.credits on a pricing item priced PER_SEAT. An amount grant is rejected with PER_SEAT requires a fixed credits_grant.credits; a seat-owned amount grant is not supported. A wallet type granted from PER_SEAT also carries no second grant rule on the same wallet type: two rules produce the same grant key, and the second would be dropped 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 was held as a reservation: the available balance already reflected it, so a second invoice could not spend the same money, but nothing had left the wallet. When the invoice goes FINAL, the covered amount now leaves the wallet, oldest-expiring grant first. It is recognised as revenue with no tax, because the tax was already charged 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 it twice. See Credit types & wallets.
Quotes
Pricing defined directly on a schedule appears on the quote A quote whose schedule carries its own pricing, rather than pricing that comes from a pricing plan version, showed that pricing nowhere: not on the quote overview and not on the generated PDF. It is included on both now. The quote overview also gained an “expand all” action, so a quote with several products can be read in one pass instead of opened section by section.
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 in particular is an override of the pricing plan’s currency. In the open, it invites a sales user to fill it in as though it were required. Out of sight, it is set only when it is genuinely being overridden.
Invoices
Set an invoice’s payment status directly
payment_status on an invoice was read-only and could only be moved by recording a payment. It is now patchable on a FINAL invoice, for settlement that happened outside Solvimon:
Only PAID and UNPAID can be set. PARTIALLY_PAID and OVERPAID follow from the paid and open amounts, so they are rejected. A request that sets the deprecated paid flag to something the status contradicts is rejected too. The field cannot be set while creating an invoice. See Invoice payment status.
Avalara e-invoicing moves to ELR 1.6 The Avalara integration runs on E-Invoicing and Live Reporting 1.6, which reports a business status and an authority status alongside the document status. Two refresh behaviours come with it. Statuses stamped on the same millisecond now sort by their message rather than arbitrarily. Follow-up stops once the most recently submitted e-invoice reaches a final state, instead of being kept alive by earlier attempts that failed before it. 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. It is now selected by an entitlement on your platform, and 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 come out looking two different ways. The download button is translated into the languages the customer portal supports. It 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 on a platform with years of invoices behind it. A wide range used to risk running past the request timeout and coming back as a 500. A reporting or reconciliation run can ask for a quarter or a year in one request. Payment, payment request, bulk action and subscription search filter on dates the same way, and answer faster with it. One behaviour change travels along: a bound given with sub-second precision now matches the second that contains it, where it previously matched nothing at all.
Filter invoices by the workflow that acted on them
Invoices can be filtered 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. The invoices whose workflow actions failed are now a set you can pull in one request, rather than something to reconstruct by reading the execution log and looking each invoice up.
Fixes
- A credit note against a one-off invoice can be finalised without a manual refresh. The
PATCHtoFINALreturned a 500 and the validator reported a total of 0. The sales tax rates of a one-off product were not being reloaded for a credit note the way they are for the one-off invoice itself. - AvaTax rates are loaded for the next schedule as well as the previous one. An invoice spanning two schedules, where the second prices products the first does not, was taxed without rates for those products.
Reporting
An e-invoicing report
E-invoicing status was visible per invoice and nowhere in aggregate. Answering “which invoices have not been accepted yet” meant opening 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 has a line per submission, so a failure and the resubmission that followed it are both in the extract. See E-invoicing report.
Integrations
Address an integration by its reference
The reference on an integration is what tells several integrations of the same type apart. It was usable everywhere except on the integration itself, which could only be addressed by its resource id. GET, PATCH and DELETE on /v1/integrations/{idOrReference} now accept either, and so do the deprecate and activate actions under it. Integrating code that already knows an integration by its reference no longer has to store the generated id alongside it. Draft ERP integrations can also be deleted.
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 executor available 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. The action is configurable in Desk, where the panel lists the ERP integrations connected to your platform.
Tag the emails a workflow sends
The send-email action accepts tags: up to 3 ASCII tags of at most 128 characters each. They are passed to Mailgun as o:tag, so messages can be filtered and reported on in the Mailgun dashboard. The legacy Paragon integration exposed this, and migrated templates lost it. Tags are set per workflow, so different workflows can be told 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. Answering “what did the automation do for this customer” was a search over every execution followed by a lookup per row.
An overdue reminder template of its own Selecting a trigger preselects the email template written for it, which is what 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, rather than being late. There is now an overdue reminder template. The Invoice: status set to overdue trigger preselects it, and a new sandbox is created with it 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 rendered only the latter, so a customer looking at their own subscription saw a plan that was missing the products negotiated onto it directly. Those products are now listed with the rest.
Desk
Fixes
- Deleting a customer or a subscription 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 rendered in the PDF in the font it was written in, without the right-hand edge being cut off.
- Invoice numbering settings no longer submit an invoice date determination type that you did not change. Editing a self-billing numbering format re-submitted the platform’s date determination type, defaulted it to
WHEN_INVOICE_GOES_TO_FINALand applied 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 edited per integration. With two fields carrying the same reference on different integrations, Desk matched on the reference alone. Both inputs showed the same value, and editing one changed the other.