# API versioning

One version per channel - deprecation, sunset dates, successor links and usage reports.

APIs evolve and, sooner or later, a change arrives that existing clients cannot absorb - a renamed field, a restructured response, a different resource layout. This page shows the complete lifecycle of an API version in Zato: publish a new major version, deprecate the old one, tell its callers where to go, watch who still uses it, and retire it once the traffic reaches zero.

## The pattern - one version, one channel {#the-pattern-one-version-one-channel}

Zato uses URL path versioning - the version is part of the endpoint's URL path and each major version is its own [REST channel](https://zato.io/docs/dev/rest/channels.html):

- `/api/v1/orders` - a channel pointing to the service that implements version 1
- `/api/v2/orders` - a channel pointing to the service that implements version 2

No routing code is needed - channels already map URL paths to services. Both versions run side by side and each has its own security definition, rate limiting and [audit log](https://zato.io/docs/admin/audit-log/index.html).

The two channels may point to two different services or to a shared one, depending on the size of the change:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class GetOrdersV1(Service):
    """ The original contract, kept unchanged for existing callers.
    """
    def handle(self):
        orders = self.invoke('orders.get-list')
        self.response.payload = {'orders': orders}

class GetOrdersV2(Service):
    """ The new contract - here, pagination metadata alongside the results.
    """
    def handle(self):
        orders = self.invoke('orders.get-list')
        self.response.payload = {
            'items': orders,
            'total': len(orders),
        }
```

Minor, backwards-compatible changes - a new optional field, a new query parameter - do not need a new version. Only breaking changes do.

## Deprecate the old version {#deprecate-the-old-version}

Once the new version is live, mark the old channel as deprecated. In the Dashboard, go to **Connections** > **Channels** > **REST**, edit the channel and click **Toggle options** next to **API versioning** to reveal the deprecation fields:

- **Deprecated** - the flag itself
- **Sunset date** - next to the flag, the day the channel will be retired, picked with a date-time picker, e.g. `2026-12-31`
- **Successor path** - the URL path of the replacement, e.g. `/api/v2/orders`

![Deprecation fields on a channel](https://zatosource-production.b-cdn.net/docs/gfx/rest/versioning-deprecation.webp?v=1791300195)

Dashboard menu: Connections > Channels > REST

The sunset date and the successor are optional - the flag alone already marks the channel. A deprecated channel keeps working as before - deprecation informs callers, and no request is blocked or slowed down.

The channel list shows a `Deprecated` badge next to the name of every deprecated channel, so deprecated channels are identifiable without opening each one.

The same attributes are available in [enmasse](https://zato.io/docs/admin/enmasse.html) definitions, so deprecation can be managed through configuration files too:

```yaml
channel_rest:

  - name: api.orders.v1
    service: api.get-orders-v1
    url_path: /api/v1/orders
    is_deprecated: true
    deprecation_sunset: '2026-12-31'
    deprecation_successor: /api/v2/orders
```

## What callers see {#what-callers-see}

Every response from a deprecated channel includes the standard deprecation headers, including error and rate-limited responses:

```text
Deprecation: @1767225600
Sunset: Thu, 31 Dec 2026 00:00:00 GMT
Link: </api/v2/orders>; rel="successor-version"
```

- **Deprecation** - when the channel became deprecated, per RFC 9745
- **Sunset** - when it will be retired, per RFC 8594
- **Link** - the path of the replacement, with the `successor-version` relation

Clients and API tooling that understand these headers can raise the migration with their owners before the retirement date arrives - without any coordination on your side.

The [OpenAPI console](https://zato.io/docs/dev/rest/openapi-console.html) shows the same information - a deprecated endpoint has a deprecated badge and its description names the sunset date and the successor path, so anyone browsing the live documentation learns about the migration too.

## Who still calls the old version {#who-still-calls-the-old-version}

The decision to retire a version needs evidence - who still calls it and how often. Each REST channel has a usage report built over its audit log - in the channel list, click **Usage** next to the channel.

The report shows one row per caller, where the caller is the security definition that authenticated the requests:

- **Caller** - which API client the calls came from
- **Calls** - how many calls the caller made in the selected range
- **First and last call** - when the caller's traffic started and when it was last seen

The range covers the last 24 hours, the last 7 days or the audit log's full retention window, each row links to the filtered audit log page with the underlying requests, and the table exports to CSV. When a caller's traffic reaches zero, that client has migrated - when all of them have, the version can be retired by deleting the channel.

For longer-horizon trends, the [Prometheus metrics](https://zato.io/docs/monitoring/prometheus-reference.html) expose per-channel request counters that a monitoring stack can graph over any period.

## The deprecation lifecycle {#the-deprecation-lifecycle}

1. Publish `/api/v2/orders` as a new channel pointing to the new service
2. Mark `/api/v1/orders` as deprecated, with a sunset date and `/api/v2/orders` as the successor
3. Callers see the `Deprecation`, `Sunset` and `Link` headers on every response and the deprecated entry in the OpenAPI console
4. Watch the usage report as clients migrate
5. Retire the old version by deleting its channel once the traffic reaches zero

## See also {#see-also}

- [REST channels](https://zato.io/docs/dev/rest/channels.html) - The channels each API version runs on
- [OpenAPI console](https://zato.io/docs/dev/rest/openapi-console.html) - Where callers see deprecation badges and sunset dates
- [Enmasse reference](https://zato.io/docs/dev/rest/enmasse.html) - is\_deprecated and the other channel keys in YAML

## Learn more {#learn-more}

- [REST integration overview](https://zato.io/integrations/rest/) - Start here to see how teams build REST integrations
- [In-depth API tutorial](https://zato.io/tutorials/main/01.html) - Master architectural patterns for production-ready integrations
- [Modern REST API tutorial](https://zato.io/tutorials/rest-api/python.html) - Design, build and secure REST APIs with Python
