> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usehasp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Versioning

> The dated Hasp-Version header, per-key pinning, the /v1 major boundary, and the deprecation-header policy — a pinned version never breaks.

`/v1` in the URL is a **major-version boundary only** — it does not let you pin the exact response shape your integration was built against, and it gives HASP no way to evolve response shapes without either breaking every caller at once or accumulating parallel major URL versions forever. HASP solves this the way Stripe does: a **dated version**, independent of the URL, resolved per request.

## How a version resolves

Every request resolves to exactly one dated version (`YYYY-MM-DD`), in this order:

1. **The `Hasp-Version` request header**, if sent — an explicit per-request override.
2. **Your API key's pinned default**, if one is configured.
3. **The deployment's current default version**, if neither of the above applies.

```bash theme={null}
curl https://api.usehasp.com/v1/webhooks \
  -H "Authorization: Bearer hasp_api_live_<key>" \
  -H "Hasp-Version: 2026-07-12"
```

The resolved version is always echoed back on the `Hasp-Version` **response** header, so you can confirm what actually applied without guessing:

```
Hasp-Version: 2026-07-12
```

Sending a `Hasp-Version` value this deployment doesn't recognize is rejected with `400 INVALID_API_VERSION` before the request does anything else — it never silently falls back to a different version. That rejection response predates version resolution, so (like a 404 for an unrecognized route) it does not carry a `Hasp-Version` response header.

## Per-key pinning

Every API key carries its own default dated version (`pinned_api_version`) — every request made with that key resolves to the pinned version unless the request explicitly overrides it with its own `Hasp-Version` header. This is the recommended way to run a production integration long-term: pin the key once, and every deploy of your integration keeps working exactly as built, with no header required on every call. The request header always wins over the key's pin — useful for testing an upcoming version against a key still pinned to an older one.

Only one dated version exists today, so every key currently resolves to it either way. Self-serve key pinning (setting `pinned_api_version` yourself, via the dashboard or the API) ships alongside the first additional dated version — check the [changelog](/ai-api/changelog) when that lands.

## A pinned version never breaks

This is the core guarantee: once a dated version is published, HASP never changes what it returns to a caller pinned to that version — not a renamed field, not a removed field, not a changed status code for an existing condition. A change that would alter an existing version's shape or semantics always ships as a **new** dated version, together with exactly one transformer translating the new shape back to each still-supported older version. Old and new callers each keep receiving exactly the shape their pinned version promised.

What's always safe (non-breaking, ships within the current version, no new date required):

* Adding a new field to a response.
* Adding a new endpoint.
* Adding a new dated version.
* Adding a new possible value to an already-open-ended set (e.g. a new webhook event type).

What requires a new dated version:

* Renaming, removing, or changing the type of an existing field.
* Changing an existing endpoint's default behavior.
* Changing the meaning or status code of an existing error condition.
* Narrowing an existing behavior (e.g. a previously-unbounded field becoming validated).

## `/v1` and dated versions are independent axes

`/v1` is the API's **major/URL** version — it changes only for an incompatible architectural shift, not for the routine evolution dated versions absorb. Dated versions evolve *within* `/v1` and do not by themselves justify a new URL major version. In practice, expect `/v1` to stay `/v1` for a very long time, with dated versions carrying almost all forward evolution.

The OAuth 2.1 token-issuance and discovery endpoints (`/v1/oauth/*`, `/.well-known/*`) sit outside the dated-version framework entirely — they authenticate the caller rather than act as the caller, run before version resolution in the request lifecycle, and return the standard OAuth/RFC 8414 wire shapes their specs require rather than a HASP-versioned response.

## Deprecation

When a dated version is deprecated (a newer version has superseded it and its support window has begun winding down), responses served under that version carry standard deprecation headers so automated tooling — not just a human reading changelog — can detect it:

```
Deprecation: true
Sunset: 2027-01-15T00:00:00Z
```

`Deprecation: true` signals the version you're pinned to is no longer the recommended one; `Sunset` is the date after which HASP may stop accepting requests pinned to it. A deprecated version keeps returning the exact shape it always has — deprecation headers are a notice to migrate, not a behavior change. Migrate before the `Sunset` date by re-pinning your API key (or your request's `Hasp-Version` header) to a newer version and adjusting your integration for that version's documented changes.

As of this writing, only one dated version (`2026-07-12`) exists, so no request currently receives these headers — this section documents the policy that takes effect the first time a second version ships.

## Checking what version you're integrated against

```javascript theme={null}
const res = await fetch('https://api.usehasp.com/v1/webhooks', {
  headers: { Authorization: 'Bearer hasp_api_live_...' },
});

console.log(res.headers.get('Hasp-Version')); // "2026-07-12"
console.log(res.headers.get('Deprecation'));   // null, unless the resolved version is deprecated
```
