Skip to main content
The TypeScript (@expys/sdk), Swift (ExpysSDK), and Kotlin (com.expys:sdk) SDKs are designed to feel like one library. This page is the contract for that: what is guaranteed identical across all three, and the handful of differences that are intentional because they are idiomatic to the language.

Identical across all three

Method names, request and response shapes, the error taxonomy, and the reliability behavior are the same in every language. Port code between SDKs by translating syntax, not semantics.
  • Member-mode methods (same names, same shapes, called with a member token): listOffers, eligibility, wallet, getRedemption, createRedemption, listRedemptions, walletTransactions, listConversations, listMessages, sendMessage, and streamMessages (whose return type is the one intentional concurrency difference, below).
  • Server-mode methods (same names, identical behavior; server-only - they require an Org-API-Key machine credential and must run on your backend, never in a client app): exchangeToken, creditPoints, setMember, getMember, removeMember, analyticsSummary, analyticsOffers, analyticsTimeseries, createWebhook, listWebhooks, deleteWebhook. Configuring the SDK with a member token and calling one fails fast client-side (TS NotConfiguredError, Swift ExpysError.notConfigured, Kotlin ExpysException.NotConfigured) before any request; the server also 403s a member token. A credential is a machine credential iff it starts with expys_.
  • Error data: every API error carries status, a stable code, message, a coarse category (kind), requestId (the server’s x-request-id), and - on a 429 - retryAfterMs (milliseconds in all three).
  • Reliability: full-jitter exponential backoff retrying 429/5xx, honoring Retry-After bounded to 300s; automatic Idempotency-Key on writes (reused across retries); proactive plus reactive (401) token refresh with no infinite loop; a configurable per-request timeout.
  • maxRetries defaults to 2 (3 attempts total).
  • environment is an enum (live / sandbox); routing is enforced server-side by the token claim. The SDK surfaces it in the User-Agent.
  • User-Agent format: expys-sdk-<lang>/<sdk> (spec/<spec>; env=<env>[; org=<org>])[ <suffix>].

Intentional differences (idiomatic per language)

ConceptTypeScriptSwiftKotlinWhy
Constructinitialize(config)ExpysClient(configuration:)ExpysClient.create(configuration)Idiomatic construction per language.
ErrorsTyped classes (error instanceof ConflictError)enum ExpysError + APIError.kindsealed ExpysException + ApiError.kindEach language’s natural error model.
Base URLbaseUrl: stringbaseURL: URLbaseUrl: StringSwift capitalizes acronyms and uses URL.
Org idorgIdorgIDorgIdSwift acronym casing.
TimeouttimeoutMs: number (ms)timeout: TimeInterval (s)timeoutMs: Long (ms)Swift’s TimeInterval is idiomatically seconds.
Refresh skewrefreshSkewMs (ms)refreshSkew: TimeInterval (s)refreshSkewMs (ms)Same rationale as timeout.
Token expirytokenExpiresAt: Date | number | stringtokenExpiresAt: DatetokenExpiresAtMs: Long (ms)Native time type per platform.
ConcurrencyPromise + AbortControllerasync/await, Sendable, actor sessionsuspend + coroutines, Mutex sessionEach platform’s concurrency model.
Streaming (streamMessages)AsyncIterable<Message> (for await)AsyncThrowingStream<Message, Error> (for try await)Flow<Message> (collect)The native lazy async-sequence type per language; cancellation tears down the connection.
The only unit that ever differs is seconds vs milliseconds on the Swift timeout / refreshSkew config inputs (idiomatic TimeInterval). The consumer-facing error hint (retryAfterMs) is milliseconds everywhere.

See also

SDKs overview

The three SDKs, side by side, with install commands.

Configuration

Every option with its default and per-language type.