Skip to main content

Choosing between v1 (BackendClient) and v2 (BackendClientV2)

The SDK ships two independent backend clients — they do not replace each other (SYN-6806). Which one a call site uses is now explicit in code; there is no environment-variable switch that silently routes a call to one or the other. This guide helps you pick, wire, and read each.

v1v2
ClassBackendClientBackendClientV2 / AsyncBackendClientV2
Modulesynapse_sdk/clients/backend/synapse_sdk/clients/backend_v2/
Paginationoffset (count/next/previous/results)cursor (next/previous/results, no count)
Resource surfaceflat (client.list_projects(...))namespaced (client.projects.list(...))
Responseraw dicttyped Pydantic model (*V2List / *V2Detail)
Auth headerSynapse-Access-Token: Token <t>SYNAPSE-ACCESS-TOKEN: syn_<t>
Tenant headerSYNAPSE-Tenant: Token <c>SYNAPSE-Tenant: <c> (default; tenant_token_prefix=TrueToken <c>)

Decision rule

The call site's function decides the client, and that decision is frozen in code. Export runs on v2; to_task and dataset download run on v1.

Calling conventionFixed clientRationale
export/handlers.py (Task / Assignment / GroundTruth / GroundTruthEvent)v22-phase list → bulk_fetch/bulk_data; v2 is required (missing v2 → explicit error, no v1 fallback)
to_task/steps/fetch_tasks.pyv1v1 list_tasks
dataset/action.py _download_splitv1v1 list_ground_truth_events

For new code, pick the client that fits the operation:

  • Need to stream/export large result sets with bulk hydration and cursor pagination → v2 (the export path).
  • Need to_run an existing v1-scoped pipeline (to_task, dataset download, jobs status sync via create_backend_client) → v1.
  • Unsure / working on a v1-scoped surfacev1 (it is a fully supported, non-deprecated first-class client).

Wiring both clients (build_runtime_clients)

Execution paths wire both clients together via synapse_sdk.utils.auth.build_runtime_clients (see the auth utility docs):

from synapse_sdk.utils.auth import build_runtime_clients

clients = build_runtime_clients()
ctx = RuntimeContext(
client=clients.client, # v1 (None if no v1 credentials)
v2_client=clients.v2_client, # v2 (None if no v2 credentials)
)

The contract is "credentials present → wire; absent → None" — each client is built iff its credentials exist. Treat None as "not configured", not as an error. clients.client_v1 / clients.client_v2 are the canonical symmetric aliases of clients.client / clients.v2_client.

Credential env priority

Both v1 and v2 read a shared core source with v2-native fallbacks (intentional contract — a v1 credential wires v2 too).

Credential sourcev1v2
SYNAPSE_HOST / SYNAPSE_ACCESS_TOKEN (v1 core)✅ (shared)
SYNAPSE_PLUGIN_RUN_USER_TOKEN / SYNAPSE_PLUGIN_RUN_TENANT✅ (as drf_token/tenant)
SYNAPSE_BACKEND_V2_ACCESS_TOKEN / SYNAPSE_BACKEND_V2_DRF_TOKEN / SYNAPSE_BACKEND_V2_TENANT✅ (v2-native fallback)
~/.synapse/config.json

Priority (highest first): env vars, then ~/.synapse/config.json. v2 always forces tenant_token_prefix=True (v1-style SYNAPSE-Tenant: Token <c>).

v2-only / v1-only surfaces

  • v1 only (use v1): agents, storages, legacy serve_applications/, and the 31 v1-only endpoints in the v1↔v2 diff.
  • v2 only (use v2): membership / permission trio routes, workshops, and the 101 v2-only endpoints.

Migration note

v1 and v2 coexist — converting a call site is optional and incremental. There is no plan to remove v1 (BackendClient and its v1 factories are non-deprecated and documented). Convert a site only when it needs a v2-only surface or v2 streaming/bulk behavior.

See also