SALAF EMSDevelopers
salafems.com

Versioning

What we may change without warning, and what we may not.

The version is in the path. Everything served under /ext/v1 keeps its promises for as long as v1 exists.

https://api.salafems.com/ext/v1/orders

There is no version header, no date pinning per request, and no account-level version. One product team and a modest surface do not justify that machinery — a path segment is the whole strategy, and it is legible from a log line.

What we may change inside v1

These are additive and can ship any time. Your integration must tolerate them.

ChangeExample
A new field on an existing objectAn order gains delivery_window
A new endpointGET /ext/v1/returns
A new query parameter or filterupdated_after arriving on customers
A new value in an existing enumAn order status we do not have today
A new error codeA more specific failure than the one you get now
A new webhook event typeAn endpoint subscribed to * starts receiving it

What we will not change inside v1

These are breaking and require a new major version:

  • Removing a field, or renaming one
  • Changing a field's type — a money string will never become a number
  • Changing what a field means while keeping its name
  • Removing an endpoint or a query parameter
  • Removing an enum value
  • Making an optional parameter required
  • Changing the meaning of an HTTP status or an error code

A v2 would mount beside v1 at /ext/v2, never over it. v1 does not change underneath you the day v2 appears.

Writing an integration that survives

  • Ignore fields you do not recognise. Never validate a response against a closed schema that rejects unknown keys.
  • Default-branch every enum. See the warning above.
  • Do not depend on field order, or on the absence of a field.
  • Do not depend on the exact wording of message. Branch on code.
  • Do not parse ids. They are opaque strings. Their format is not a contract, and code that assumes a shape will break when a resource adopts a different one.
  • Do not assume a field is missing forever because it is null today.

Deprecation

If something in v1 ever has to go, it goes slowly and loudly:

  1. Announced in the changelog with the replacement and a date.
  2. Deprecation and Sunset headers appear on the affected endpoints, so your logs can tell you that you are using something on the way out even if nobody read the changelog.
  3. At least six months between the announcement and the shutdown.
HTTP/1.1 200 OK
Deprecation: true
Sunset: Sat, 11 Aug 2027 00:00:00 GMT

Nothing is deprecated today.

The version tag

Responses and this documentation carry a date-based version tag — currently 2026-08. For the REST endpoints it is informational: the path segment is what governs compatibility.

For webhooks it is load-bearing. Each webhook endpoint is pinned to a version when it is created, and nothing afterwards changes it — not editing the endpoint, not rotating its secret. A change to payload shape mints a new date tag, and existing endpoints keep receiving the shape they were built against. Every delivery states its version twice: in the X-Salaf-Api-Version header and as api_version in the body.

Event names are governed by a stricter rule still: they are additive forever. A new event type is a new name, and an existing name is never renamed and never changes meaning — a rename would be a silent outage that no deprecation header could reach, because merchants subscribe by string. See the event catalog.

What is not covered by any of this

Data is not a contract. A store can rename a product, archive a category or change a price at any moment; a status your integration has never seen may appear because a merchant started using a workflow they had not used before. Versioning governs the shape of the API, not the values flowing through it.

Rate limits are also outside the versioning promise — they are plan configuration, and a higher plan changing your quota is not a breaking change. Read the limit from X-RateLimit-Limit rather than hard-coding it.