MonetizeKit versions the API by URL path and gives every deprecation a minimum warning window before removal — but a smooth migration is still on you: you need to know what you're using, verify the replacement, and roll the change out without a big-bang cutover. This guide covers all three.
How versioning works
| Aspect | Detail |
|---|---|
| Version scheme | URL-path versioning — /api/v1/, with /api/v2/ served alongside it once introduced. |
| Unsupported version | 404 with error: "unsupported_api_version". |
| Current version header | X-API-Version: v1 is present on every response, success or error. |
| Minimum deprecation window | At least one minor release with active warnings before removal; enterprise plans may get an extended window. |
1. Find what you're using that's deprecated
Deprecated endpoints and GraphQL fields are marked in the API reference and enumerated with removal dates on the Deprecations page. There is no per-call response header that flags deprecated usage — the signal lives in the docs, not the wire protocol — so cross-reference the paths your integration actually calls (from your own access logs) against that page:
# There is no runtime Deprecation/Sunset response header to grep for — the# only version-related header on every response is X-API-Version. Detect# deprecated usage by cross-referencing every endpoint/field your integration# calls against the Deprecations page (/docs/changelog/deprecations) instead.# Example entry as of this writing:## item: "GET /integrations/legacy-webhooks"# removalVersion: v2.4# removalDate: 2026-09-30# migrationInstructions: "Use GET /webhooks/endpoints for endpoint management and replay operations."grep -A1 "GET /integrations/legacy-webhooks" your-access-log.txtAnything already returning 404 unsupported_api_version means you're calling an unsupported version prefix outright, not merely a deprecated field — fix that first.
2. Add a dual-read fallback
Don't cut over in one deploy. Read from the new shape first and fall back to the deprecated one only if the new call fails, logging every fallback so you can measure how much traffic still depends on the old path.
// Neither the deprecated endpoint nor its replacement has an// @monetizekit/node SDK method (webhook endpoint management is REST-only —// see the Webhooks guide), so call both directly.async function getWebhookIntegrations() { const [legacy, current] = await Promise.allSettled([ fetch(`${baseUrl}/integrations/legacy-webhooks`, { headers: authHeaders }).then((r) => r.json()), fetch(`${baseUrl}/webhooks/endpoints`, { headers: authHeaders }).then((r) => r.json()), ]); // Prefer the new shape; fall back only while the legacy path is still live. if (current.status === "fulfilled") return normalize(current.value.data); console.warn("Falling back to deprecated GET /integrations/legacy-webhooks", legacy); if (legacy.status === "fulfilled") return normalize(legacy.value.data); throw new AggregateError([current.reason, legacy.reason], "Both webhook integration APIs failed");}3. Roll out in stages
Once the new path works, move traffic over gradually and deterministically — the same workspace should stay on the same side of the gate across requests, so a problem surfaces as a contained blast radius rather than a random flicker.
// Route a percentage of traffic to the new integration path first,// keyed deterministically so a given workspace always lands on the same side.function useNewWebhooksApi(workspaceId, rolloutPercent) { const bucket = hashToPercent(workspaceId); // stable 0-99 bucket per workspace return bucket < rolloutPercent;}const rolloutPercent = 10; // start small: 10% -> 50% -> 100% over successive deploysconst path = useNewWebhooksApi(workspaceId, rolloutPercent) ? "/webhooks/endpoints" : "/integrations/legacy-webhooks";await fetch(`${baseUrl}${path}`, { headers: authHeaders });Hold each rollout percentage long enough to observe error rates and fallback volume before widening it. A migration that silently regresses at 10% and isn't caught until 100% is far more expensive to unwind than one caught early.
Migration checklist
Before you remove the old code path
Common pitfalls
Waiting until the removal date
A removal date is a hard cutoff, not a target to start migrating on. Start as soon as a deprecation is announced — the minimum warning window is the floor, not a comfortable buffer.
Big-bang cutover
Switching every workspace at once means every migration bug ships to every customer at once. A staged percentage rollout turns that into a contained, quickly-reversible blast radius.
No fallback logging
Without logging when the dual-read fallback triggers, you can't tell when it's safe to delete the legacy path — you're guessing instead of measuring.
Ignoring GraphQL field deprecations
Deprecated GraphQL fields (like legacyFlags) keep working until removal, so nothing breaks today — but they carry the same removal-date risk as REST endpoints. Check the GraphQL reference for deprecationReason annotations, not just REST responses.
FAQ
How much notice do I get before a breaking change?
At least one minor release with active deprecation warnings before removal. See Breaking Changes for the running list with exact removal dates.
Will /api/v1 stop working once /api/v2 ships?
No — a new major version is served alongside the previous one, not as a replacement for it. /api/v1 keeps working until it is explicitly deprecated and removed under the same policy as any other endpoint.
Can I test against a future API version before it's generally available?
Watch the Changelog for early-access announcements. Until a version is documented in the API reference, treat any behavior you observe from it as unstable and don't build against it in production.
Related guides
Audit and Trace
Correlate migration-related errors with request IDs.
Webhook Verification and Retries
Migrating a webhook integration follows the same dual-read pattern.