Versioning & deprecation
The API is versioned in the URL path. This page sets out what we consider a breaking change, what we can change without warning, and how to write a client that survives both.
The current version
Every endpoint lives under /api/v1/:
curl https://api.ospulse.app/api/v1/packages \
-H "Authorization: Bearer $OSPULSE_TOKEN"There is no version header and no per-request version negotiation. The path segment is the whole story — which means the version you integrate against is visible in every line of your code and every log entry.
You can confirm what you are talking to at any time:
curl https://api.ospulse.app/api/v1/version{
"product": "OSPulse",
"version": "0.1.0.0",
"buildId": "local",
"gitSha": "unknown",
"commitDate": "unknown",
"environment": "Production"
}Build version is not API version
/api/v1/version reports the deployed build, which changes with every release. The
API contract version is the v1 in the path and changes far more rarely. Use the build
version for diagnostics and support, never for feature detection.
What counts as a breaking change
A breaking change is anything that could stop a correctly-written existing client from
working. We will not make these within v1:
- Removing an endpoint, or changing its HTTP method or path
- Removing a field from a response, or renaming one
- Changing the type of an existing field, or its meaning
- Adding a new required request parameter or body field
- Making validation stricter on input that was previously accepted
- Changing the success status code an endpoint returns
- Removing a value from an enum that responses already return
If we need to do any of these, it will happen in a new version — /api/v2/ — and v1 will
continue to be served through the deprecation process below.
What we may change at any time
These are not breaking changes, and your client must tolerate them:
- Adding a new endpoint.
- Adding a new field to a response. Never assume you have seen the complete object.
- Adding a new optional request parameter.
- Adding a new value to an enum. New alert kinds, ecosystems, scan states, and severity levels appear as the platform grows.
- Changing the order of items in a collection where no order is guaranteed.
- Changing human-readable text — the
detailstring on an error, a description field. Never parse these; branch onstatusand machine-readable codes instead. - Performance and pagination internals, including how
totalEstimateis computed.
The most common client bug
A client that deserialises strictly — rejecting unknown fields, or throwing on an unrecognised enum value — will break the first time we add something. This is by far the most frequent cause of an integration failing after a routine release. Configure your deserialiser to ignore unknown properties.
Writing a forward-compatible client
Ignore unknown fields
Configure your JSON deserialiser to tolerate additions.
// System.Text.Json ignores unknown members by default — keep it that way.
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
// Do NOT set UnmappedMemberHandling.Disallow
};Handle unknown enum values gracefully
Treat an unrecognised value as "something new" rather than an error. A dashboard should show it verbatim; a policy gate should fail safe.
const KNOWN_SEVERITIES = ['low', 'medium', 'high', 'critical']
function isBlocking(severity) {
// An unfamiliar severity is treated as significant rather than ignored.
if (!KNOWN_SEVERITIES.includes(severity)) return true
return severity === 'high' || severity === 'critical'
}Branch on status codes, not messages
Use the HTTP status and the structured fields of the
problem details body. The detail string is written for humans and may
be reworded at any time.
Pin your generated clients
If you generate a client from the OpenAPI spec, commit the generated code and regenerate deliberately, reviewing the diff. That converts an upstream change into a reviewable pull request rather than a surprise at runtime.
Deprecation policy
When something must be retired, we follow a predictable sequence rather than removing it abruptly.
Announcement
The deprecation is published in the changelog with the reason, the replacement, and the earliest date the old behaviour may be removed.
Notice period
The deprecated surface keeps working throughout. Enterprise customers are notified directly. We aim to give a generous window — long enough to schedule the change into normal development, not an emergency.
Migration guidance
Anything non-trivial gets a migration note describing the replacement and the code changes required. If a replacement endpoint exists, it ships before the deprecation is announced, so there is never a period with no supported path.
Removal
Removal only happens in a new API version. Within v1, deprecated fields may be documented
as deprecated and eventually stop being populated, but the contract shape is preserved.
Stay ahead of changes
Watch the changelog and the status page. If you maintain a production integration, subscribe to status notifications so version and availability announcements reach you without polling.
If a new major version arrives
A v2 would be introduced alongside v1, not in place of it. You would be able to migrate
endpoint by endpoint, because the path prefix is per-request — nothing forces an
all-at-once cutover. v1 would remain available throughout the announced support window.
