# Changelog

## September 2026

### September 10

**The HTML form now submits a signed container.** Instead of separate invoice fields—`data` (base64url of the JSON) and `signature` (HMAC-SHA256 of the `data` string). The endpoint changed from `invoices/createFromForm` to `invoices/form`. Old forms no longer work and must be rebuilt: see [HTML form signature](./creating-invoices/html-forms/form-signature.md).

### September 8

**Numeric fields in the webhook body are now numbers.** Six fields that used to be sent as strings are now sent as numbers: `project.commissionRate`, `invoice.commissionFiatUSD`, `invoice.amountFiatUSD`, `invoice.amountFiat`, `invoice.calcAmountFiat`, `payment.amount`. The set of fields, their names, and their order have not changed.

Keep in mind: trailing zeros are dropped, so an amount of 10.00 arrives as 10. Amounts below 0.0001 arrive in exponential notation, for example 1.0e-6—standard JSON parsing returns the correct number; only manual string parsing breaks.

Notifications built before the rollout and not yet delivered arrive in the old format. The receiver must accept both variants until the queue drains—this takes no more than two days.

### September 6

**Numeric fields in API responses are now numbers.** Affects `invoices/list`, `payments/list`, and `invoices/create`.

| Field               | Before           | After  |
| ------------------- | ---------------- | ------ |
| `commissionFiatUSD` | `"0.10"`         | `0.1`  |
| `amountFiatUSD`     | `"10.00"`        | `10`   |
| `amountFiat`        | `"1000.00"`      | `1000` |
| `views`             | `"0"`            | `0`    |
| `payment.amount`    | `"10.500000000"` | `10.5` |

**A key restricted to a project now applies the restriction in all methods.** Previously, some methods ignored the project. Now it is a filter in listing methods, and a request for another project's invoice or wallet is rejected with `Restricted project`. A method that cannot honor the project restriction is closed to such a key. Details are in the “Key scope” section.

**The payment list now returns unmatched payments.** Previously, `payments/list` returned only payments tied to an invoice. Now the results include all payments across your wallets; an unmatched one has no `invoice` block. These are exactly the payments needed for manual matching via `invoices/bindPayment`.

**Invoice cancellation now distinguishes refusal reasons.** Previously, both a missing invoice and unmet conditions returned a generic `Invoice not found`. Now `Invoice not found` means only that the invoice does not exist, while unmet conditions return `Invoice cannot be canceled`.

**Malformed identifiers are now rejected.** `invoices/list` validates `invoiceId`, `invoiceUid`, and `projectId`; `payments/list`—`walletId` and `invoiceId`. A value that does not fit returns `Parameter is filled in incorrectly` with the field name. Previously, such a parameter was silently dropped and the results came back unfiltered.

**Period boundaries now include the whole named day.** `startDate` and `endDate` are interpreted from `00:00:00` to `23:59:59`. Previously, the end day was cut off entirely, and a one-day query returned only records created exactly at midnight. Affects `invoices/list`, `payments/list`, `statistics/invoices`, and `statistics/payments`.

**A project field was added to the balance history.** Every operation in the `billing/history` method now carries a `projectId` field. For account-level operations—top-ups and bonuses—it is `null`.

### September 4

**Description and service data come back in their original form.** The `description` and `serviceData` fields used to be stored encoded, and in API responses a quote arrived as `&quot;` and a less-than sign as `<`. Now exactly what the merchant submitted is returned. When rendering in HTML, escape the value on your side.

**HTML forms accept only POST.** Creating an invoice by following a link with parameters in the address bar no longer works—parameters are read only from the request body. If you used links, replace them with a form with `method="post"`.

### September 3

**Webhook notifications are now signed.** The `X-Timestamp` and `X-Signature` headers were added; the signature is computed with HMAC-SHA256 on the webhook secret. Verifying the signature lets you tell a genuine notification from a forgery. The mechanics and examples are in the “Webhook signature verification” section.

The change is backward compatible: if you do not add the check, your handler keeps working as before.
