Versioning and changes
How the API is versioned, which changes we treat as breaking and which as additive, how to write clients that keep working, and where changes are announced.
The API is versioned in its path, and the version only changes for breaking changes. Within a version, we add to the API without taking anything away. This page explains what that means for your code.
The version is in the path
Every endpoint lives under /api/v1:
https://api.elaraone.app/api/v1/productsThere is no version header to send. The current version is v1.
How we treat changes
Additive changes can ship at any time within v1:
- new endpoints;
- new optional query parameters, request fields and headers;
- new fields in responses, including new fields inside existing objects;
- new keys in
metaanddetails; - new error codes, for new situations;
- new values in lists that are documented as open, such as
decline_codeon a declined payment; - new event types for webhooks you can subscribe to.
Breaking changes are the ones we avoid within v1. We treat these as breaking:
- removing or renaming an endpoint, a field, a parameter or a header;
- changing a field's type or the meaning of a value;
- making an optional parameter or field required;
- changing which
error.codean existing situation returns.
When a security change has to tighten what existing credentials can do, it is phased in rather than switched on overnight. The storefront API lock is an example: each store ran in report-only mode first, with the calls that would be refused shown in the admin, before the lock was enforced. See stores and keys.
Write clients that keep working
Because additive changes can arrive at any time, build clients that tolerate them:
- Ignore fields you do not know. Do not fail on unexpected keys, and do not use strict schemas that reject them.
- Treat unknown values as a safe default. An unknown
decline_codeisunknown; an unknown orderstatusshould still showstatus_label; an unknown promotionkindcan be shown by itslabel. - Handle unknown error codes. Fall back on the HTTP status and show
error.message. - Do not depend on the order of keys in an object, or on the order of groups in search suggestions (use the order the search config gives you).
- Read optional fields defensively. Fields documented as nullable can be
null.
type DeclineCode =
| 'card_declined' | 'card_expired' | 'insufficient_funds'
| 'incorrect_card_details' | 'authentication_required' | 'try_another_method' | 'unknown'
const known = new Set<DeclineCode>([
'card_declined', 'card_expired', 'insufficient_funds',
'incorrect_card_details', 'authentication_required', 'try_another_method', 'unknown',
])
export function declineCode(value: unknown): DeclineCode {
return typeof value === 'string' && known.has(value as DeclineCode) ? (value as DeclineCode) : 'unknown'
}Webhook payload versions
CRM change webhooks carry an api_version in every body, so a receiver can tell which payload shape it is reading:
{
"id": "01JA5W9Q3V8K2YB7N6T4M0H1XC",
"type": "contact.updated",
"created_at": "2026-10-13T09:30:00+00:00",
"store": "hearth-and-oak",
"api_version": "2026-10-01",
"data": { "object": "contact", "id": 12, "changed": ["email", "lifecycle_stage"], "actor": { "type": "api" } }
}The current payload version is 2026-10-01. Read it to tell which payload shape you are handling. New fields and new event types can still be added under the same version, so ignore what you do not recognise.
Deprecations you can detect
Some changes are also signalled in responses. For example, after a store key is rotated, responses to calls made with the old key carry X-Tenant-Key-Deprecated: true until the overlap ends. Log headers like this in your integration so they show up before anything stops working. See stores and keys.
The changelog
Changes to the API are announced in the changelog. Check it when you upgrade a starter storefront or an integration.