# Upgrade to v3 (/data-api/changelog/upgrade-guide-v3)

Migration guide for upgrading the Fiskil Data API from v2 or v1 to v3



This guide covers breaking changes when upgrading from major version `v2` to
`v3`. Fiskil v3 reworks the two Core Resources list endpoints — consents and
end users — to return a paginated response object instead of a bare array, and
renames consent fields to camelCase.

Coming from `v1`? See [Coming from v1?](#5-coming-from-v1) below — you apply
the `v1` -> `v2` changes plus everything on this page.

<Callout type="warning" title="Before upgrading your pinned version">
  Upgrade your pinned version in Console only after thorough testing with
  `X-Fiskil-Version`. Console upgrades are one-way (for example, `v2` -> `v3`)
  and cannot be downgraded in settings.
</Callout>

<Callout type="success" title="What hasn't changed">
  * **No URL changes** — every endpoint path and HTTP method is identical - **No
    Banking or Energy changes** — all `/banking/*` and `/energy/*` endpoints are
    identical to `v2` - **No auth changes** — authentication and security are
    unchanged - **Only two GET responses are affected** — `GET /consent` and `GET
      /end-users`; see below for exact schema changes
</Callout>

1. Opt into v3 with a header [#1-opt-into-v3-with-a-header]

`v1`, `v2`, and `v3` are served on the same base URL. Select `v3` per request
with the `X-Fiskil-Version` header:

```bash
curl https://api.fiskil.com/v1/consent \
  -H 'X-Fiskil-Version: v3' \
  -H 'Authorization: Bearer {token}'
```

In `v3`, the `X-Fiskil-Version` header is documented as **required** on the two
changed endpoints — always send it explicitly while migrating rather than
relying on your Console pin.

For the full header and Console-pin semantics, see [API Versioning](/data-api/guides/core-concepts/versioning#selecting-a-version).

2. Migration checklist [#2-migration-checklist]

<Steps>
  <Step title="Pin production requests to your current version">
    Deploy a one-line change adding `X-Fiskil-Version: v2` (or `v1`) header to
    your requests. This freezes your current response shape while you prepare the
    upgrade.
  </Step>

  <Step title="Check whether affected endpoints are used">
    If your integration uses either of these endpoints, review the exact schema
    changes in the sections below:

    * `GET /consent`
    * `GET /end-users`

    If you use neither, `v3` needs no parser changes.
  </Step>

  <Step title="Update affected parsers">
    Work through the breaking changes below: read the new response envelopes,
    rename consent fields to camelCase, and stop sending `expires_before`.
  </Step>

  <Step title="Test v3 with production traffic">
    Run `X-Fiskil-Version: v3` in production for a period of time and verify both
    endpoints and downstream parsing before changing the Console pin.
  </Step>

  <Step title="Upgrade the Console pinned version">
    After production validation, upgrade your account's pinned API version in
    Console to `v3`.
  </Step>
</Steps>

3. Breaking changes [#3-breaking-changes]

<Callout type="info" title="What this section covers">
  Only **breaking** changes are documented below (for example type changes,
  structural replacements, or removed fields). **Additive** updates such as the
  new pagination parameters are covered in [New in
  v3](#4-new-in-v3-non-breaking). For full request and response shapes, see each
  endpoint's API reference and OpenAPI specification.
</Callout>

GET /consent - response envelope changed [#get-consent---response-envelope-changed]

In `v3`, the response body is no longer a bare JSON **array** of consents. It
is an **object** with a `consents` array and an optional `links` object for
pagination.

| v1 / v2                 | v3                                             | Notes             |
| ----------------------- | ---------------------------------------------- | ----------------- |
| top-level array `[...]` | object `{ "consents": [...], "links": {...} }` | Structure changed |
| —                       | `links.next` / `links.prev` string URLs        | New, optional     |

**Example v3 response:**

```json
{
  "consents": [
    {
      "arrangement_id": "94549a73-a554-4b76-b824-d96898829751",
      "active": true
      ...
    }
  ],
  "links": {
    "next": "/consent?page[after]=eyJpZCI6...",
    "prev": "/consent?page[before]=eyJpZCI6..."
  }
}
```

**Migrate:** Read the consent list from `consents` array instead of
iterating the top-level array. Treat `links` as optional — it carries cursor
URLs when more pages exist.

GET /end-users - response envelope changed [#get-end-users---response-envelope-changed]

In `v3`, the response body is an **object** with an `end_users` array and an
optional `links` object, instead of a bare array. Note the wrapper key is
snake\_case (`end_users`), unlike `consents`. The end-user item fields (`id`,
`email`, `name`, `phone`) are unchanged.

| v1 / v2                 | v3                                              | Notes             |
| ----------------------- | ----------------------------------------------- | ----------------- |
| top-level array `[...]` | object `{ "end_users": [...], "links": {...} }` | Structure changed |
| item fields             | unchanged (`id`, `email`, `name`, `phone`)      | No renames        |

**Example v3 response:**

```json
{
  "end_users": [
    {
      "email": "john_starmer@gmail.com",
      "id": "482c0e2b-5866-46b1-b795-220b7bba45b5",
      "name": "John Starmer",
      "phone": "+614123456789"
    }
  ],
  "links": {
    "next": "/end-users?page[after]=eyJpZCI6..."
  }
}
```

**Migrate:** Read the list from `end_users` array instead of the top-level
array. Item parsing is unchanged.

GET /consent & GET /end-users - error responses changed [#get-consent--get-end-users---error-responses-changed]

In `v3`, the two list endpoints return a simpler JSON error body. The
`temporary`, `timeout`, and `fault` fields are removed.

| v1 / v2                                            | v3                      |
| -------------------------------------------------- | ----------------------- |
| `{ id, message, name, temporary, timeout, fault }` | `{ id, message, name }` |

**Example v2 error response:**

```json
{
  "name": "invalid_field_type",
  "id": "Iinv0AfL",
  "message": "invalid value ...",
  "temporary": false,
  "timeout": false,
  "fault": false
}
```

**Example v3 error response:**

```json
{
  "id": "3GFbIj0u9muOrByxbdINUACrYRa",
  "name": "bad request",
  "message": "invalid active value ..."
}
```

**Migrate:** Handle `400` and `500` on both list endpoints and parse the
error body as `{ id, message, name }`. Stop relying on the `fault`,
`temporary`, and `timeout` fields for these endpoints.

4. New in v3 (non-breaking) [#4-new-in-v3-non-breaking]

Both `GET /consent` and `GET /end-users` gain cursor-based pagination:

| Parameter      | Type    | Description                                  |
| -------------- | ------- | -------------------------------------------- |
| `page[size]`   | integer | Number of items per page, capped at 1000     |
| `page[after]`  | string  | Cursor — return results after this position  |
| `page[before]` | string  | Cursor — return results before this position |

```bash
curl 'https://api.fiskil.com/v1/end-users?page[size]=100' \
  -H 'X-Fiskil-Version: v3' \
  -H 'Authorization: Bearer {token}'
```

Follow `links.next` / `links.prev` from the response to walk pages. Requests
without `page[...]` parameters keep working — pagination is opt-in. See the
[Pagination](/data-api/api-reference/pagination) guide for more details.

5. Coming from v1? [#5-coming-from-v1]

`v3` uses the same Banking and Energy service versions as `v2`, so upgrading
from `v1` directly to `v3` means applying **both** sets of breaking changes:

1. **All `v1` -> `v2` changes** — banking products/accounts rates and fees,
   transactions `extended_data`, and the removed identity `customer` field.
   Work through the [Upgrade to v2](/data-api/changelog/upgrade-guide) guide.
2. **All `v2` -> `v3` changes** — the consents and end-users changes on this
   page.

You can validate both sets in one pass: test with `X-Fiskil-Version: v3` and
follow the [migration checklist](#2-migration-checklist) above, treating the
endpoints from both guides as your affected list. There is no need to pin to
`v2` along the way.

6. Reference [#6-reference]

**Version switcher in docs:** Toggle between `v1`, `v2`, and `v3` in the top
navigation to compare API reference shapes. This only affects docs — runtime
behavior is controlled by the `X-Fiskil-Version` header or your Console pin.

**How versioning works at Fiskil:** For the versioning model, breaking-change
policy, and support timeline, see [API Versioning](/data-api/guides/core-concepts/versioning).

**Need help?** Use the AI chat in the bottom-right corner — it has context on
all specs. For anything it can't resolve, reach out to Fiskil support.
