Skip to content

API Versioning

The Partners API uses URL-based versioning to provide a stable integration surface while allowing the API to evolve.

Versioning Scheme

The API version is embedded in the URL path:

$BASE_URL/api/v1/listings/
              ^^
           version

The current version is v1. When a new version is released, it will be available at /api/v2/ while the previous version continues to operate.

When a New Version Is Created

A new API version is created when a change would break existing integrations. Breaking changes include:

  • Removing an endpoint or HTTP method
  • Removing a response field or changing its type
  • Removing or renaming a query parameter
  • Changing the meaning of an existing field (e.g., a field that was a string becoming an integer)
  • Changing authentication or authorization requirements in a way that invalidates existing tokens or scopes
  • Changing error response codes for existing error conditions (e.g., a 400 becoming a 422)
  • Changing the default behavior of an endpoint (e.g., default sort order, default page size)

Non-Breaking Changes (No New Version)

The following changes are considered backward-compatible and will be made to the current version without creating a new one:

  • Adding a new endpoint
  • Adding a new optional query parameter
  • Adding a new field to a response (your client should ignore unknown fields)
  • Adding a new optional field to a request body
  • Adding a new scope
  • Adding a new error code for a previously-unhandled condition
  • Improving error messages (the detail text may change)
  • Performance improvements or bug fixes

Warning

Your integration should be built to tolerate additive changes. Always ignore unknown fields in responses rather than failing on them.

Webhook Events

The API version and a webhook event's schema version are independent contracts. Bumping one never implies bumping the other.

Beyond's events are thin: data.attributes carries facts about something that happened (status, completed-at, base-price), and the domain object it happened to appears only as a relationship — a {type, id} pair plus a links.related URL you follow to fetch the current state. No event embeds an API resource representation. So if GET /listings/ renames a field in /api/v2/, no event body changes: the event never contained a listing in the first place.

Two consequences worth stating explicitly:

  • One thing happening emits exactly one event, regardless of which API version triggered it. If you create a managed account through /api/v1/ or a future /api/v2/, you receive a single account.created. Webhook subscriptions are configured per application, not per API version, and the webhook-id idempotency contract depends on one occurrence producing one identifier.
  • An event can fire with no API request behind it at all. listing.base_price_changed is emitted whenever a base price changes on any Beyond surface — the web app, an internal pricing job, a PMS sync. There is frequently no triggering API version to speak of.

How events are versioned

The version lives in the event type, not the envelope. There is no meta.schema-version, meta.api-version, or any other version field in a webhook payload. The identity of an event is its type, so a change that a consumer cannot absorb becomes a new type.

  • Additive changes never create a new type. A new attribute or a new optional relationship is backward-compatible. You must ignore fields you do not recognize, exactly as you would for a REST response body.
  • A breaking change ships as a new event type with a version suffix — for example listing.created.v2 alongside listing.created. Removing or renaming an attribute, changing its type, or changing what an existing field means are all breaking.
  • The two run in parallel. When a v2 of an event is introduced, the original keeps firing. You migrate your handler for that one event type on your own schedule, test it in isolation, and can fall back to the old type if the upgrade misbehaves — with no effect on any other event. The original type is then deprecated and eventually retired following the Deprecation Policy below: announced ahead of time, run in parallel through the migration window, then stopped.

This is deliberately per-event versioning, not a global one. A v2 of listing.created does not touch account.created or any other event, so you are never forced to re-test every handler because one payload changed.

A new enum value is the classic example

Adding a value to a constrained attribute — for example a third status beyond succeeded and failed — looks additive but breaks a consumer that exhaustively matches on the current values. This is exactly the kind of change that warrants a new event type. Even so, treat unrecognized enum values as a case to handle rather than an error, so a future value cannot take your handler down before you have migrated.

What your consumer must tolerate

To stay compatible across every non-breaking change we make, your handler must:

  1. Ignore unknown fields in data.attributes and data.relationships.
  2. Ignore unknown enum values rather than crash on them (see the warning above).
  3. Ignore event types you do not recognize. Webhook subscriptions are per application, not per event type — so when we introduce a new event type (including a .v2 of one you already handle), it is delivered to your endpoint immediately. Route on meta.type and silently acknowledge (2xx) any type you have no handler for, so a new event never causes a failed delivery and a retry storm.

The one place the two versions touch

links.related URLs point back into the API, and today they are /api/v1/ paths:

"links": { "related": "https://developers.beyondpricing.com/api/v1/listings/12345/" }

Treat this URL as a stable locator, not a version declaration. Because our events are thin, the link never carried a versioned representation — it only tells you where the object lives. The v1 in it is a routing prefix, not a promise about the payload you get back; the shape of that payload is governed by the API version you request when you follow the link, exactly as it is for any other call you make.

So the link does not need to track the newest API version, and it will not be rewritten per consumer. links.related stays on /api/v1/ even after /api/v2/ ships, and a partner who has otherwise migrated to /api/v2/ can still follow it — a GET on a /api/v1/ URL keeps returning the object for as long as v1 is Active.

Deprecation Policy

When a new version is released:

  1. Both versions run in parallel for a migration period
  2. Deprecation notice is communicated via email and documented here
  3. Migration guide is provided with a mapping of changes between versions
  4. Sunset date is announced at least 6 months in advance

Version Lifecycle

Phase Description
Active Fully supported, receives bug fixes and non-breaking enhancements
Deprecated Still operational, but a newer version is available. No new features.
Sunset Decommissioned. Requests return 410 Gone.

The current version, v1, is Active.