@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, andstreamMessages(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 (TSNotConfiguredError, SwiftExpysError.notConfigured, KotlinExpysException.NotConfigured) before any request; the server also403s a member token. A credential is a machine credential iff it starts withexpys_. - Error data: every API error carries
status, a stablecode,message, a coarse category (kind),requestId(the server’sx-request-id), and - on a 429 -retryAfterMs(milliseconds in all three). - Reliability: full-jitter exponential backoff retrying
429/5xx, honoringRetry-Afterbounded to 300s; automaticIdempotency-Keyon writes (reused across retries); proactive plus reactive (401) token refresh with no infinite loop; a configurable per-request timeout. maxRetriesdefaults to2(3 attempts total).environmentis an enum (live/sandbox); routing is enforced server-side by the token claim. The SDK surfaces it in theUser-Agent.User-Agentformat:expys-sdk-<lang>/<sdk> (spec/<spec>; env=<env>[; org=<org>])[ <suffix>].
Intentional differences (idiomatic per language)
| Concept | TypeScript | Swift | Kotlin | Why |
|---|---|---|---|---|
| Construct | initialize(config) | ExpysClient(configuration:) | ExpysClient.create(configuration) | Idiomatic construction per language. |
| Errors | Typed classes (error instanceof ConflictError) | enum ExpysError + APIError.kind | sealed ExpysException + ApiError.kind | Each language’s natural error model. |
| Base URL | baseUrl: string | baseURL: URL | baseUrl: String | Swift capitalizes acronyms and uses URL. |
| Org id | orgId | orgID | orgId | Swift acronym casing. |
| Timeout | timeoutMs: number (ms) | timeout: TimeInterval (s) | timeoutMs: Long (ms) | Swift’s TimeInterval is idiomatically seconds. |
| Refresh skew | refreshSkewMs (ms) | refreshSkew: TimeInterval (s) | refreshSkewMs (ms) | Same rationale as timeout. |
| Token expiry | tokenExpiresAt: Date | number | string | tokenExpiresAt: Date | tokenExpiresAtMs: Long (ms) | Native time type per platform. |
| Concurrency | Promise + AbortController | async/await, Sendable, actor session | suspend + coroutines, Mutex session | Each 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.