# Changelog

Changes are announced here. Within `v1` they never require you to change working code —
see [Versioning & deprecation](/guides/versioning/).

## November 2026

**New**

- **[Fulfil an order](/guides/fulfil-an-order/)**, a guide that takes an order from
  session creation to a verified webhook, in Node.js, PHP and Python.
- **Refund object.** `POST /gateway/sessions/{id}/refund/` now returns the
  [refund](/guides/refunds/#the-refund-object): a `re_…` id, a `status` of `PENDING`,
  `SUCCEEDED` or `FAILED`, and a `processor_message`. It replaces the earlier response
  fields (`refund_id`, `refunded_amount`, `automated_refund`, `epg_error`).
- **`Idempotency-Key` on refunds**, so a timed-out refund can be retried without
  refunding twice. See [Retrying safely](/guides/refunds/#retrying-safely-idempotency-key).
- **Refund events** `refund.succeeded` and `refund.failed`, for refunds issued through
  any channel, and a `refunds` list on every session.
- **`session.attempt_failed`**, sent for every declined payment attempt.
- **`locale` on sessions** chooses the payment page's language: `fr`, `en`, or `auto`
  (the default) to follow the buyer's browser. The card-entry page now opens in the same
  language as the payment page. See
  [Language](/guides/hosted-checkout/#language).
- **The API reference** now documents every webhook event and every error response.
  The OpenAPI specification can be downloaded at
  [`/openapi/merchant-api.yaml`](/openapi/merchant-api.yaml).
- **Markdown version of every guide**, at the page's address with `.md` in place of the
  trailing slash (for example [`/guides/webhooks.md`](/guides/webhooks.md)), so a single
  page can be handed to an AI assistant. [`/llms.txt`](/llms.txt) now links to these.
- **Send a test event** in your dashboard now covers every event, including
  `session.attempt_failed` and the refund events.

**Changed**

- The payment page can no longer be shown in an iframe. See
  [Websites and mobile apps](/guides/hosted-checkout/#websites-and-mobile-apps).
- A session created without `locale` follows the buyer's browser; the payment page used
  to open in French.
- A webhook's body `id` is now the same value as its `X-Paymee-Webhook-Id` header.
- Webhook retries now span about 3 days (up to 10 deliveries) instead of about a day.
- Webhooks are sent within seconds after the change they describe, independently of the
  buyer's checkout; events can arrive out of order.
- Requests refused by a business rule now always answer `422` (some refund refusals
  answered `400`).
- Validation errors carry specific codes such as `required`, `blank` and `max_length`
  instead of `invalid` for every case.
- Optional text fields sent as `""` count as not given, and `metadata` accepts numbers and
  booleans (stored as text).
- `webhook_url` is no longer looked up in DNS when you create a session; the address is
  checked when each event is delivered.
- An unexpected server error now answers an `internal-error` problem document.
- Every amount — in responses and in webhooks — now has exactly the currency's decimals
  (`"10.500"`). Some response fields (a fetched session's `amount`, an attempt's
  `fee_amount` and `net_amount`, and the webhook `payment` amounts) used to show five
  (`"10.50000"`); a missing `net_amount` is `null`.
- Every timestamp now ends in `Z`; some webhook timestamps used `+00:00`.
- The same data has the same name in API responses and webhooks: the buyer is a nested
  `buyer` object (replacing the `buyer_first_name`, `buyer_last_name`, `buyer_email` and
  `buyer_phone` fields of API responses); the time a session ended is `completed_at`
  (replacing `paid_at` in webhooks); an attempt's number is `attempt_number`; and an
  attempt's `failure_code` is the acquirer's decline code everywhere (it was always
  `declined` in API responses), with its reason in `failure_message`.
- `checkout.session.expired` no longer repeats the session id as `payment_session_id`:
  use `data.id`.

Source: https://docs.paymee.online/guides/changelog/
