# Migrate from 2024-03-18 to 2025-04-01 (https://docs.chargeflow.io/docs/api/2025-04-01/migration)





This guide describes every change between API version `2024-03-18` and `2025-04-01` for both the Merchants API and the Platforms API. The deltas below come from comparing the two OpenAPI specs directly, so each item reflects an actual difference in paths, request bodies, response schemas, enums, or required fields.

Version `2025-04-01` introduces breaking changes. Read the sections that apply to your integration before you switch.

<Callout type="warn" title="This is not a drop-in upgrade">
  Several endpoints from `2024-03-18` are not present in `2025-04-01`, and shared endpoints changed
  their request and response shapes. Plan to update your code, not just the version string in the
  URL.
</Callout>

## How to set the version [#how-to-set-the-version]

Chargeflow uses date-based versioning in the request path. The version is the date segment after `/public/`. To move to `2025-04-01`, replace the date in every request path.

Authentication did not change. Both versions use the `x-api-key` request header with the same production server, `https://api.chargeflow.io`.

<Tabs items="['2024-03-18', '2025-04-01']">
  <Tab value="2024-03-18">
    ```bash title="Terminal"
    curl https://api.chargeflow.io/public/2024-03-18/disputes \
      -H "x-api-key: YOUR_API_KEY"
    ```
  </Tab>

  <Tab value="2025-04-01">
    ```bash title="Terminal"
    curl https://api.chargeflow.io/public/2025-04-01/disputes \
      -H "x-api-key: YOUR_API_KEY"
    ```
  </Tab>
</Tabs>

For background on how versions work, see [API versioning](https://docs.chargeflow.io/docs/reference/concepts/api-versioning).

## Merchants API changes [#merchants-api-changes]

### Endpoint changes [#endpoint-changes]

| Method and path                           | 2024-03-18 | 2025-04-01 | Change                                                 |
| ----------------------------------------- | ---------- | ---------- | ------------------------------------------------------ |
| `GET /disputes`                           | Yes        | Yes        | Response shape changed, query parameters reduced       |
| `GET /disputes/{disputeId}`               | No         | Yes        | New endpoint                                           |
| `PATCH /disputes/{disputeId}`             | No         | Yes        | New endpoint (Enrich Dispute)                          |
| `POST /disputes`                          | Yes        | No         | Removed                                                |
| `POST /disputes/{disputeId}/evidence`     | Yes        | Yes        | Unchanged                                              |
| `POST /disputes/{disputeId}/subscription` | No         | Yes        | New endpoint                                           |
| `GET /alerts`                             | No         | Yes        | New endpoint                                           |
| `GET /alerts/{alertId}`                   | No         | Yes        | New endpoint                                           |
| `POST /alerts/{alertId}/outcome`          | No         | Yes        | New endpoint                                           |
| `POST /customer-communication`            | No         | Yes        | New endpoint                                           |
| `POST /data-subject/removal`              | No         | Yes        | New endpoint                                           |
| `GET /webhooks`                           | Yes        | Yes        | Response shape changed                                 |
| `POST /webhooks`                          | Yes        | Yes        | Unchanged (`{event, url}`, one event per registration) |
| `DELETE /webhooks/{id}`                   | Yes        | Yes        | Unchanged                                              |
| `GET /health-check`                       | Yes        | Yes        | Unchanged                                              |
| `GET /health-check/access-key`            | Yes        | Yes        | Unchanged                                              |

### Removed endpoints [#removed-endpoints]

These endpoints exist in `2024-03-18` but are not in `2025-04-01`. If you call them, you need to stop or move to a replacement.

| Removed endpoint | Notes                                                                                                                                                                       |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /disputes` | No direct equivalent in the Merchants API for `2025-04-01`. To enrich an existing dispute, use `PATCH /disputes/{disputeId}` and the per-resource `POST` endpoints instead. |

### `GET /disputes` response changed [#get-disputes-response-changed]

The list response was restructured. The top-level fields and the per-item shape both changed.

| 2024-03-18                | 2025-04-01                                                                 |
| ------------------------- | -------------------------------------------------------------------------- |
| `totalCount` (number)     | moved under `pagination.totalCount` (integer)                              |
| `hasMore` (boolean)       | removed                                                                    |
| `data` (array of objects) | renamed to `disputes` (array)                                              |
| no pagination object      | new `pagination` object with `totalCount`, `offset`, `limit`, `totalPages` |

In `2024-03-18`, each item in `data` had a fixed shape with `chargeflowDisputeId`, a nested `dispute` object (with `processorId`, `processor`, `amount`, `status`, `reason`, `responseDueDate`), and a nested `transaction` object. In `2025-04-01`, the `disputes` array items use the flat snake\_case dispute shape (`id`, `source`, `source_id`, `account_id`, `created_at`, `reason`, `due_by`, `amount`, `currency`, `status`, `stage`, `closed_at`), with related resources referenced rather than embedded - do not rely on the old `data[].dispute` and `data[].transaction` structure. To read a single dispute with full detail, use the new `GET /disputes/{disputeId}` endpoint.

The query parameters on `GET /disputes` were also reduced.

| Query parameter   | 2024-03-18   | 2025-04-01    |
| ----------------- | ------------ | ------------- |
| `offset`          | Yes (number) | Yes (integer) |
| `limit`           | Yes (number) | Yes (integer) |
| `dateCreatedFrom` | Yes          | Removed       |
| `dateCreatedTo`   | Yes          | Removed       |
| `amountFrom`      | Yes          | Removed       |
| `amountTo`        | Yes          | Removed       |
| `statusNames`     | Yes (enum)   | Removed       |
| `reason`          | Yes (enum)   | Removed       |
| `processorNames`  | Yes (enum)   | Removed       |

<Callout title="Filtering">
  If you relied on date, amount, status, reason, or processor filters in the query string, that
  filtering is no longer available on `GET /disputes` in `2025-04-01`. Plan to filter on your side
  after fetching, or fetch individual disputes by ID.
</Callout>

### `GET /webhooks` response changed [#get-webhooks-response-changed]

The webhook list items are simpler in `2025-04-01`.

| Field            | 2024-03-18                                                                                                                                         | 2025-04-01            |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| `success`        | boolean                                                                                                                                            | boolean               |
| `data[].id`      | string                                                                                                                                             | string                |
| `data[].event`   | enum (`webhook.test`, `dispute.created`, `evidence.ready`, `evidence.error`, `alerts.created`, `alerts.updated`, `alerts.transaction.linked`, `*`) | plain string, no enum |
| `data[].url`     | string                                                                                                                                             | string                |
| `data[].active`  | boolean                                                                                                                                            | removed               |
| `data[].version` | string                                                                                                                                             | removed               |

### Unchanged Merchants endpoints [#unchanged-merchants-endpoints]

`POST /disputes/{disputeId}/evidence`, `DELETE /webhooks/{id}`, `GET /health-check`, and `GET /health-check/access-key` have the same request and response shapes in both versions. The evidence upload still accepts the same two `oneOf` variants and the same `evidenceUploadCategory` enums (`tracking_information`, `customer_communication`, `invoice` for the first variant, and `additional_evidence`, `additional_notes` for the second).

### New Merchants endpoints [#new-merchants-endpoints]

`2025-04-01` adds several endpoints. The notable request and response details are below.

`GET /disputes/{disputeId}` returns a single dispute with nested `transaction`, `subscription`, and `order` objects. Note that its `reason` enum is the smaller set `fraud`, `not_received`, `not_as_described`, `canceled_recurring_billing`, `duplicate_charge`, `credit_not_processed`, `other`, and `status` is one of `needs_response`, `under_review`, `won`, `lost`. It also includes a `stage` enum (`Chargeback`, `Inquiry`, `Pre_arbitration`, `Arbitration`) and a `source` enum listing supported processors.

`POST /disputes/{disputeId}/subscription` creates subscription context for a dispute. Required fields: `source_id`, `created_at`, `start_date`, `frequency` (`day`, `week`, `month`, `year`), and `frequency_count`.

`GET /alerts` and `GET /alerts/{alertId}` return alert records, including a nested `network_transaction` object and an optional `transaction`. `GET /alerts` wraps results in `alerts` plus the same `pagination` object used by `GET /disputes`.

`POST /alerts/{alertId}/outcome` records an alert outcome. Required field: `outcome`, one of `refunded`, `previously_refunded`, `duplicate`, `decline`, `error`. It returns `202` with no body.

`POST /customer-communication` submits communication tied to one or more disputes. Required fields: `source_id`, `disputes` (array of strings), and `messages`. Each message requires `sender`, `date`, and `body_plain_text`, and may set a `type` of `email`, `chat`, `transcript`, `form`, or `sms`.

## Platforms API changes [#platforms-api-changes]

The Platforms API changed more than the Merchants API. Every `2024-03-18` Platforms endpoint was removed, and `2025-04-01` introduces a new set of endpoints with a new naming and data model.

### Endpoint changes [#endpoint-changes-1]

| Method and path              | 2024-03-18 | 2025-04-01 | Change                                   |
| ---------------------------- | ---------- | ---------- | ---------------------------------------- |
| `GET /disputes`              | Yes        | No         | Removed                                  |
| `POST /disputes`             | Yes        | No         | Removed                                  |
| `POST /platform/disputes`    | No         | Yes        | New endpoint (replaces dispute creation) |
| `POST /accounts`             | No         | Yes        | New endpoint                             |
| `GET /accounts/{accountId}`  | No         | Yes        | New endpoint                             |
| `POST /evidence`             | No         | Yes        | New endpoint                             |
| `GET /evidence/{evidenceId}` | No         | Yes        | New endpoint                             |

<Callout type="warn" title="No shared Platforms endpoints">
  There are no Platforms endpoints common to both versions. The `2025-04-01` Platforms API is a new
  surface, so treat the migration as a rewrite of your Platforms integration rather than a field
  rename.
</Callout>

### Removed Platforms endpoints [#removed-platforms-endpoints]

| Removed endpoint | 2025-04-01 direction                                                                                           |
| ---------------- | -------------------------------------------------------------------------------------------------------------- |
| `GET /disputes`  | No list endpoint in `2025-04-01`. Fetch a single record instead, for example via `GET /evidence/{evidenceId}`. |
| `POST /disputes` | Replaced by `POST /platform/disputes` with a new request body.                                                 |

### Dispute creation model changed [#dispute-creation-model-changed]

The old `POST /disputes` and the new `POST /platform/disputes` both create a dispute, but the request bodies are different. The data model moved from a flat, processor-oriented payload to a nested model with `transaction`, `subscription`, and `order` objects, and the field names changed from camelCase to snake\_case.

| Old field (`POST /disputes`)                               | New field (`POST /platform/disputes`)                                     |
| ---------------------------------------------------------- | ------------------------------------------------------------------------- |
| `disputeId` (required)                                     | not used as input; the response returns `id`                              |
| `creationDate` (required)                                  | `created_at` (required)                                                   |
| `due_by` (required)                                        | `due_by` (required)                                                       |
| `disputeAmount` (required)                                 | `amount` (required)                                                       |
| `currency` (required)                                      | `currency` (required)                                                     |
| `processor` (required)                                     | not present; use `source_id` (required) plus the response `source` enum   |
| `isChargeRefundable` (required)                            | removed                                                                   |
| `handleByChargeflow` (required)                            | removed                                                                   |
| `reason` (required, large enum)                            | `reason` (required, smaller enum)                                         |
| `status` (enum `needs_response`, `warning_needs_response`) | `status` (required, enum `needs_response`, `under_review`, `won`, `lost`) |
| no `account_id`                                            | `account_id` (required)                                                   |
| `products`, `pastPayments` (flat arrays)                   | nested under `order` and `transaction` objects                            |

New required fields on `POST /platform/disputes`: `source_id`, `account_id`, `created_at`, `reason`, `due_by`, `amount`, `currency`, `status`.

The `reason` enum is smaller and uses different values.

| Old `reason` values                                                                                                                                                                                                                                                                                                                                                  | New `reason` values                                                                                                            |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `general`, `fraudulent`, `duplicate`, `subscription_canceled`, `product_unacceptable`, `product_not_received`, `unrecognized`, `credit_not_processed`, `incorrect_account_details`, `insufficient_funds`, `bank_cannot_process`, `debit_not_authorized`, `goods_services_returned_or_refused`, `goods_services_cancelled`, `transaction_amount_differs`, `retrieved` | `fraud`, `not_received`, `not_as_described`, `canceled_recurring_billing`, `duplicate_charge`, `credit_not_processed`, `other` |

Map your old reason values to the new set before you send them. There is no automatic conversion.

The new request body also supports rich nested objects:

* `transaction` with `payment_method` (including `brand`, `wallet`, and a `checks` object for 3-D Secure and AVS results) and `billing_address`.
* `subscription` with `frequency`, `frequency_count`, `collection_method`, `pricing_model`, and `status`.
* `order` with `customer`, `products` (each requiring `name`, `type`, `quantity`, `price`), `shipping_address`, and `fulfillments`.

The response is a full dispute object with `id`, a `source` enum, `status` (`needs_response`, `under_review`, `won`, `lost`), `stage`, and the nested `transaction`, `subscription`, and `order` objects.

### New Platforms endpoints [#new-platforms-endpoints]

`POST /accounts` creates a platform account. Required fields: `owner_name`, `business_name`, `business_url`, `email`. The `status` enum is `active` or `inactive`. The response returns the account with a generated `id`.

`GET /accounts/{accountId}` returns a single account in the same shape as the create response.

`POST /evidence` generates evidence. Required fields: `account_id` and `dispute`. The `dispute` field is an `anyOf`: either `{ "dispute_id": "..." }` to reference an existing dispute, or `{ "dispute_data": { ... } }` to pass the full dispute payload inline. The inline `dispute_data` uses the same nested `transaction`, `subscription`, and `order` model as `POST /platform/disputes`. The response returns an evidence record with `id`, `status` (`in_progress`, `completed`, `error`), `file_url`, and `file_version`.

`GET /evidence/{evidenceId}` returns an evidence record. Its `dispute` field is an `anyOf` of a string ID or the full nested dispute object.

## Upgrade checklist [#upgrade-checklist]

<Steps>
  <Step>
    Update the version segment in every request path from `/public/2024-03-18/` to
    `/public/2025-04-01/`. Keep the `x-api-key` header as is.
  </Step>

  <Step>
    For Merchants `GET /disputes`, read results from `disputes` instead of `data`, and read counts
    from the new `pagination` object instead of `totalCount` and `hasMore`. Remove any reliance on
    the old `data[].dispute` and `data[].transaction` structure.
  </Step>

  <Step>
    Replace removed Merchants query filters (`dateCreatedFrom`, `dateCreatedTo`, `amountFrom`,
    `amountTo`, `statusNames`, `reason`, `processorNames`) with client-side filtering or per-dispute
    lookups via `GET /disputes/{disputeId}`.
  </Step>

  <Step>
    For Merchants `GET /webhooks`, stop reading `data[].active` and `data[].version`, and treat
    `data[].event` as a plain string rather than a fixed enum.
  </Step>

  <Step>
    Stop calling the removed Merchants endpoint `POST /disputes`. Adopt the new endpoints (`GET
        /disputes/{disputeId}`, `PATCH /disputes/{disputeId}`, which carries both `order` and
    `transaction` data, `POST /disputes/{disputeId}/subscription`, the `alerts` endpoints, and `POST
        /customer-communication`) where they fit your flow.
  </Step>

  <Step>
    For Platforms, rebuild your dispute creation against `POST /platform/disputes`. Rename camelCase
    fields to snake\_case, send the new required fields (`source_id`, `account_id`, `created_at`,
    `reason`, `due_by`, `amount`, `currency`, `status`), and map old `reason` values to the new
    seven-value enum.
  </Step>

  <Step>
    For Platforms, send order, transaction, and subscription data in the nested objects of the
    dispute body.
  </Step>

  <Step>
    Adopt the new Platforms endpoints you need: `POST /accounts` and `GET /accounts/{accountId}` for
    account management, and `POST /evidence` and `GET /evidence/{evidenceId}` for evidence
    generation.
  </Step>

  <Step>
    Confirm connectivity and credentials on the new version with `GET /health-check` and `GET
        /health-check/access-key`, which are unchanged.
  </Step>
</Steps>

## Next steps [#next-steps]

* [API reference for 2025-04-01](https://docs.chargeflow.io/docs/api/2025-04-01/get-started)
* [API versioning](https://docs.chargeflow.io/docs/reference/concepts/api-versioning)
