Skip to main content
CRM+ uses a partial-versioning model: most endpoints sit under /api/ with no version segment, while a specific subset uses /api/v2/. There is no /api/v1/ prefix; the unversioned /api/ paths are effectively v1, and /api/v2/ represents newer or improved implementations on a per-endpoint-family basis. This page covers what’s known about the model today, which endpoints use /v2/, what backward-compatibility commitments to assume, and how to write integration code that survives the platform’s evolution.

The current versioning state

What’s on /v2/ today

Two endpoint families currently use the /v2/ prefix: Everything else uses /api/ with no version segment. The Contact endpoints, the (non-batch, non-v2) Gift endpoints, the Project endpoints, the RecurringGift endpoints, the Webhook endpoints, the Query endpoints for most resources — all unversioned.

Why /v2/ exists

The /v2/ prefix indicates a more recent, sometimes redesigned implementation of an endpoint family. For Gift Transactions, the /v2/ version is the recommended path for partner integrations submitting gifts. For Pledges, the /v2/ family is the only version exposed in the current spec. This means partner integrations should prefer the /v2/ variant when it exists for the operation they’re performing. There’s no documented reason to call /api/Gift/Transaction (without /v2/) for a new integration. If a non-v2 variant exists for the same operation, treat the /v2/ one as canonical.
The CRM+ spec does not explicitly enumerate the criteria under which /v2/ is used vs. unversioned /api/. The pattern documented here — that /v2/ is reserved for redesigned families and partner integrations should prefer it — is inferred from the current endpoint inventory. The platform team may evolve the versioning model over time.

Writing code that anticipates /v2/ evolution

Partner integrations have a long lifespan — measured in years. The current /v2/ inventory is unlikely to be the final inventory; new families may be promoted to /v2/ over time, and at some point a /v3/ may appear. Three patterns help:

Pattern 1: configurable base path per endpoint family

Don’t hardcode /api/ or /api/v2/ throughout your codebase. Centralize the path construction:
JavaScript
When a future spec update moves an endpoint family to a new version, the change is one constant edit instead of search-and-replace across the codebase.

Pattern 2: feature detection over hardcoded behavior

If two variants of an endpoint exist and the field shapes differ, detect what the platform accepts rather than assuming:
JavaScript
Useful for endpoints that have phased rollouts or per-customer availability. For the documented, spec-stable endpoints, this is over-engineering — just use them directly.

Pattern 3: defensive deserialization

Don’t assume response payloads have exactly the fields documented in the spec — they may have extras (added in a non-breaking spec update) or omissions (if a field was deprecated). Parse defensively:
JavaScript
For TypeScript users: define types based on the fields you actually use, not as exhaustive 1:1 mirrors of the spec. Use Pick<> types if generating from the spec.

Spec-vs-live field typing

A backward-compatibility concern specific to CRM+: the OpenAPI spec types many fields as string that the live API actually accepts (and sometimes returns) as native types — booleans, integers, dates. This mismatch is documented in Contacts: field typing, Donations / Gifts: field typing, and elsewhere. The implications for versioning:
  • Don’t trust auto-generated SDKs that strictly enforce the spec types. They’ll send "true" (string) for booleans and reject true (native) in responses, both of which break against the live behavior.
  • Send native types in requests. They work today and align with what most modern APIs do.
  • Parse defensively in responses. If you expect a boolean and get a string, coerce; if you expect an integer and get a string number, parse it.
JavaScript
The spec-vs-live typing mismatch is a known issue documented across the audit findings. The platform may eventually update the spec to match live behavior (or vice versa). Until then, write defensive parsing.⚠️ Human input required: Establish whether the spec or the live API is the canonical source for field types. Either bring the spec in line with live behavior, or document that the spec is the authoritative source and the live API will be aligned to match.

Backward compatibility commitments

Without an explicit versioning policy from the platform team, partner integrations should make conservative assumptions about what may and may not change.

What’s safe to assume stable

The following kinds of changes would be backward-breaking and are unlikely to happen silently:

What may change without notice

The following kinds of changes are not breaking and partner integrations should expect them to happen:

What’s ambiguous

A few changes sit in the middle and require explicit confirmation when they happen:

Handling deprecated endpoints

If an endpoint is deprecated in a future version, the platform will typically:
  1. Add a new endpoint that supersedes it (often under a new version prefix like /v2/ or /v3/).
  2. Continue to support the old endpoint for a deprecation window — typically months to years.
  3. Eventually remove the old endpoint.
Partner integrations that anticipate this lifecycle handle it cleanly:

Detect deprecation signals

The platform may emit deprecation warnings via response headers or via documentation. Specifically, watch for:
JavaScript
Aggregating these warnings into metrics surfaces which deprecated endpoints your integration still uses — the work list for the next migration.

Migrate proactively

When a /v2/ (or future-version) variant is introduced for an endpoint family you use, plan migration on a reasonable timeline rather than waiting for sunset:
1

Read the new endpoint's documentation

Confirm what changed — field shapes, semantics, error conditions.
2

Implement against the new variant in a feature-flagged path

Both old and new code paths are present; the flag chooses between them.
3

Test against the Seeded Sandbox

Verify behavior parity between old and new before any customer is migrated.
4

Migrate customers gradually

Per-customer flags let you migrate one customer at a time and roll back if issues surface.
5

Remove the old code path

After all customers have been migrated, delete the old code. Don’t leave dead code paths in the codebase.

Defensive coding checklist

A set of practices that make integration code more resilient to platform evolution:
  • Endpoint URLs are constants in one place, not hardcoded throughout the codebase.
  • Field access is by-name lookup with sensible defaults, not strict shape destructuring.
  • Response parsers handle both native and string forms of typed fields (booleans, integers, amounts, dates).
  • Webhook event handlers switch on known event types and ignore unknown ones, rather than failing.
  • Enum values are not hardcoded for validation — let the API reject invalid values instead of pre-validating against a stale enum list.
  • Status code handling is by status class (2xx, 4xx, 5xx) for retry logic, with explicit branches for known specific codes (401, 404, 409, 422, 429).
  • Deprecation headers and Sunset headers are logged or metric-counted, not silently ignored.
  • SDK code generated from the spec is reviewed before adoption — strict implementations break against the spec-vs-live typing differences.
Following these makes the difference between an integration that needs updates every few months and one that runs unchanged for years.

Where to go next

Error Recovery Patterns

The error-handling practices that complement the defensive coding patterns on this page.

Base URLs and Environments

The reference for the host-level URL structure and how /v2/ paths fit into it.

Sync Architecture Patterns

The architectural patterns that the migration approach on this page builds on.

Changelog

The platform’s evolution log — watch for deprecation announcements and new versioned endpoints.
Last modified on May 27, 2026