API Contract Guarantees
Versioning, deprecation, breaking-change, and stability rules partners can rely on when integrating with the Sleeved API.
This page defines what partners can rely on when integrating with the Sleeved API: which changes Sleeved considers safe to ship without warning, which changes require advance notice, and how long that notice period is.
If you read nothing else: /v1 endpoints are stable, additive changes ship without notice, breaking changes ship through /v2 with at least 6 months of overlap and Deprecation + Sunset response headers on the /v1 endpoints.
Versioning
All public API routes are prefixed with a major version: /v1, /v2, etc. The current version is /v1.
- The version prefix is part of the URL. There is no
Acceptheader content negotiation; the version is determined by the URL path. - The OpenAPI spec's
info.versionfield tracks the major + minor + patch shape (1.0.0today). Patch and minor bumps are additive and do not change the URL prefix; major bumps move to a new URL prefix (/v2). - New games added to the public allowlist are additive. They do not trigger a new major version.
Additive vs Breaking Changes
The following changes are additive and may ship without notice:
- New endpoints added under the current version prefix
- New optional request fields, query parameters, or headers
- New optional response fields
- New enum values in response payloads (for fields whose enums are documented as open-ended — e.g. game slugs as new games are onboarded)
- New error subcodes or finer-grained error messages, as long as the existing error string and status code are still produced for the same condition
- New examples or documentation prose in the OpenAPI spec
- Performance improvements that do not change response shape
- Data-correctness fixes to previously incorrect or defaulted response field values, where the field's type and shape are unchanged — see Zone-Resolution Correctness Fix below for a concrete example
The following changes are breaking and require advance notice (see Deprecation Timeline):
- Removing an endpoint, response field, or query parameter
- Renaming a field or operationId
- Changing a field's type, format, or null-tolerance
- Narrowing the set of accepted values for a request field, query parameter, or path parameter
- Adding a new required request field
- Removing an enum value from a response or changing its meaning
- Changing the URL of an existing endpoint (including version-prefix changes — see
/v2precedent below) - Changing the meaning of an existing status code on an existing endpoint
If you are unsure whether a planned change is additive or breaking from your integration's perspective, write engineering@endlessgalaxy.dev before deploying against it.
Deprecation Timeline
When a breaking change ships, the old behavior remains available for at least 6 months from the announcement date. During the deprecation window:
- The deprecated endpoint or field continues to work and return correct responses
- The deprecated endpoint emits an RFC 8594
Deprecationresponse header containing the date deprecation was announced - The deprecated endpoint emits a
Sunsetresponse header containing the date after which it will stop working - The replacement endpoint or field is documented with a migration link from the deprecated entry's documentation
Example response headers on a deprecated endpoint:
Deprecation: Sun, 01 Jan 2026 00:00:00 GMT
Sunset: Wed, 01 Jul 2026 00:00:00 GMT
Link: <https://docs.sleeved.gg/api/contract-guarantees>; rel="sunset"Partners are notified by email at the announcement and again 30 days before sunset. The current public changelog is also updated.
/v2 Precedent
When /v2 ships, all /v1 endpoints continue to work for the full 6-month deprecation window — partners are not forced to migrate every endpoint at once.
Sleeved uses per-endpoint coexistence for the major-version transition: /v1/games/{slug}/cards and /v2/games/{slug}/cards may serve different shapes simultaneously during the transition window. This means partners can migrate one endpoint at a time rather than rewriting their entire integration in a single deploy.
The transition shape is:
/v2is announced. All/v1endpoints remain unchanged for the full deprecation window./v1endpoints begin emittingDeprecationandSunsetheaders pointing to their/v2equivalents.- After the sunset date,
/v1endpoints are removed.
Game additions and removals do not trigger /v2. The game allowlist is part of the /v1 contract; new games appear in /v1/games/{slug}/... paths additively.
Game Allowlist Policy
The set of games available via the public API is governed by PUBLIC_API_GAME_SLUGS (canonical source: packages/lib/src/games.ts). The current allowlist is published in the API overview.
- Adding a game is additive. New games appear under existing endpoints (
/v1/games/{newSlug}/cards, etc.) without a version bump. - Removing a game is breaking. A game removal goes through the deprecation timeline above: 6 months of
Deprecation/Sunsetheaders on the affected paths, with the removal date announced in advance. - Internal/playtest games (e.g.
chrono-core-playtest) are intentionally excluded from the allowlist and are not part of the contract. They may appear and disappear without notice.
Status Codes Contract
The following status codes are stable parts of the contract — partner SDKs may switch on them:
| Status | Meaning | Stability |
|---|---|---|
200 | Request succeeded | Stable |
400 | Bad request — invalid parameters, malformed cursor, unsupported format | Stable |
401 | Authentication failed (missing or invalid API key) | Stable |
404 | Resource not found, or share token failed (token does not exist, expired, or for a different deck) | Stable |
429 | Rate limit exceeded | Stable |
500 | Internal server error — retry with exponential backoff | Stable category; specific upstream errors may bubble through |
Within the stable categories, the body error string is also stable for the documented cases. See Authentication for the three authentication error shapes and Rate Limiting for the 429 retry contract.
We may add new 4xx subcodes (e.g., a more specific 422-style validation error) as additive changes — the existing 400 path continues to be produced for the same conditions.
Pagination Contract
Cursor pagination on browseCards follows these rules:
- Cursors are opaque. Do not parse them, base64-decode them, or assume any internal structure. The cursor format may change between versions or even within a single version (cursors are server-generated and partner-consumed only).
- Cursors expire. A cursor is valid for the same parameters that produced it (game slug, hitsPerPage). Restarting pagination with a different
hitsPerPageinvalidates any stored cursor — request a fresh page-1 instead. - Cursors are not stable across resyncs. A cursor returned today may not be valid tomorrow if the underlying index has been rebuilt. If you receive a
400with a cursor-related error, restart pagination from page 1. - Ordering is stable within a single pagination walk (start at page 1, follow cursors to the end). The order is not guaranteed to be stable across separate pagination walks — the underlying Algolia index may reorder records between walks. If your integration depends on a specific order across runs, sort the results client-side after collection.
- Adding fields to hits is additive. New fields may appear in
hits[i]records without notice; existing fields keep their shape and meaning per the breaking-change rules above.
ChronoCore Public-Decks Crawl Profile (Advisory)
This section describes typical request volume for the List public decks + Get deck details pair when used by ChronoCore's public-corpus crawl. It is advisory, not a version-pinned commitment — the numbers describe a supported usage pattern under the existing generic rate-limit contract (see Rate Limiting), not a separately guaranteed quota.
Expected steady-state profile, using updatedSince and bulk-detail:
- Full corpus crawl — a forward-looking headroom estimate: ≈20 listing pages (at
limit=50) + ≈10 bulk-detail calls (50 ids each), sized for a ~1,000-deck corpus. The real chrono-core public corpus is ~65 decks today; the estimate deliberately sizes for future growth, not current load. - Incremental refresh (steady state, quiet corpus) — ≈1 listing GET with
updatedSinceset to the last-observed maxupdatedAt, plus 0–1 bulk-detail calls (only fired when the delta listing returns any ids).
Legacy ceiling (tolerated, not sanctioned). Before updatedSince and bulk-detail existed, a full refresh meant one GET /decks/:deckId call per deck. That pattern — up to ~20 listing pages plus up to ~1,000 individual detail GETs per 30-minute refresh — still works and is not being deprecated, but it is a tolerated upper bound, not the recommended pattern going forward. Both the legacy profile and the new profile comfortably fit inside a single per-key rate-limit window (typically 3,000 requests / 15 minutes); the new profile just leaves a much larger operating margin. New integrations should use the listing + bulk-detail pair described above rather than the legacy per-deck GET loop.
Zone-Resolution Correctness Fix (Chrono Core)
Chrono Core decks returned by Get deck and Get deck details now resolve each card's zoneId against the Chrono Core card catalog instead of trusting only the raw stored value. This is a data-correctness fix, not a breaking change:
zoneIdis an open string field (not an enum) on both endpoints — the breaking-change clause covering "removing an enum value or changing its meaning" does not apply here, because there is no enum to change the meaning of.- Only cards with an absent or unrecognized stored zone are affected. A card whose stored zone was already a recognized (even if factually wrong) value keeps that stored value — this is a deliberate "savedZone wins" limitation, not an oversight. The fix closes the gap for missing/garbage zone data; it does not attempt to correct every possible misclassification.
- The field's type and shape are unchanged (
zoneId: string, same position in the samecards[]array entry) — only the value for a subset of previously-defaulted entries can change.
Transient-consistency note for delta-crawl consumers. If your integration dedupes incremental List public decks results by (id, updatedAt) (as recommended in the API Overview), a deck whose zone was affected by this fix keeps serving its old, pre-fix defaulted zone in your local cache until that deck's updatedAt changes again and you re-fetch it via Get deck details. This is expected — the corrected value becomes visible the next time you observe that deck through the normal update-driven refresh cycle, the same as any other field correction would.
What Is NOT Part of the Contract
The following are not stable and may change without notice:
- The internal storage path scheme for card images (the
cardImageUrlfield is stable; the URL it contains may change CDN, bucket, or path layout) - Internal log format, including the
reason=missing|not_found|revokedoperator-side log distinctions - The exact wording of human-readable error messages outside the documented set in Authentication and Rate Limiting — partners should switch on status code + the documented
errorstrings, not on free-text error prose - Performance characteristics (latency, throughput, exact cache TTLs), as long as documented rate-limit quotas are honored
- Internal Sleeved-specific endpoints under paths other than
/v1(or future/v2) — those are not part of the partner API - The fumadocs-openapi rendering of these docs (UI may change without contract impact)
Reporting Contract Violations
If you observe behaviour that contradicts a guarantee on this page — a breaking change shipped without notice, a Deprecation header missing on a now-deprecated endpoint, a status code changing meaning — please write engineering@endlessgalaxy.dev with the request you sent, the response you received, and the contract clause you believe was violated. Sleeved treats contract regressions as bugs, not negotiable trade-offs.
Rate Limiting
Fixed 15-minute windows, per-key quotas, 429 response handling, and recommendations for backoff strategies.
Query Syntax Reference
Complete reference for Sleeved's search query syntax — operators, field aliases per game, smart routing, flag expansion, ranges, regex, and graceful degradation.