> ## Documentation Index
> Fetch the complete documentation index at: https://docs.expys.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK differences

> What is guaranteed identical across the TypeScript, Swift, and Kotlin SDKs - and the handful of intentional, idiomatic per-language differences.

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

<Check>
  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.
</Check>

* **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`,
  `listMembers`, `removeMember`, `analyticsSummary`, `analyticsOffers`,
  `analyticsTimeseries`, `balance`,
  `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 `403`s 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)

| 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. |

<Note>
  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.
</Note>

## See also

<CardGroup cols={2}>
  <Card title="SDKs overview" icon="cubes" href="/sdks/overview">
    The three SDKs, side by side, with install commands.
  </Card>

  <Card title="Configuration" icon="sliders" href="/guides/configuration">
    Every option with its default and per-language type.
  </Card>
</CardGroup>
