---
sdk = "wasm-bindings"
previous_release_version = "1.11.0-dev"
---

# WASM SDK 1.11.0

## Highlights

- **Idempotency keys on sends**, so a retried send produces the same message ID instead of a duplicate.
- **The top-level `send*` helpers now take an options object** instead of a bare `optimistic` boolean.
- **`client.waitForRegistrationVisible()`** — wait until a quorum of nodes confirms a new installation is visible before using it.
- **Background worker tuning** via `workerConfig` — per-worker intervals, jitter, and enable/disable.
- **Group metadata migration APIs**: `proposalsEnabled()` and `conversation.unstable.enableProposals()`.

This release contains breaking changes. Read the Breaking Changes section before upgrading.

## What's Changed

The two API connections each client used to open have been collapsed into one, and `isSecure` is gone from the connection path. `createClient` and `createClientWithBackend` gained a `workerConfig` argument for tuning background workers, and `registerIdentity` can now optionally wait for the new installation to become visible across a quorum of nodes.

On the messaging side, sends accept an idempotency key, the top-level `send*` helpers take an options object, and `updateAppData` takes an options object so future fields can be added without another break. Groups gained `proposalsEnabled()` and a `conversation.unstable.enableProposals()` entry point for the in-progress migration to proposal-based metadata updates.

Logging initialization is now tolerant of a subscriber already installed by another crate — it warns instead of panicking.

## Breaking Changes

### `createClient` and `createClientWithBackend` take a `workerConfig` argument

**This is a breaking change.**

A `workerConfig?: WorkerConfigOptions` parameter was inserted between `deviceSyncMode` and `logOptions`. Positional callers must add it (pass `undefined` for previous behavior). The separate sync API client argument has been removed from the internal wiring — one API client now serves both paths.

### The top-level `send*` helpers take a `SendOpts` object instead of `optimistic`

**This is a breaking change.**

```ts
// Before
await conversation.sendText("hello", true);

// After
await conversation.sendText("hello", { optimistic: true });
```

This applies to `sendText`, `sendMarkdown`, `sendReaction`, `sendReadReceipt`, `sendReply`, `sendAttachment`, `sendRemoteAttachment`, `sendMultiRemoteAttachment`, `sendTransactionReference`, `sendWalletSendCalls`, `sendActions`, and `sendIntent`.

### `updateAppData` takes an options object

**This is a breaking change.**

```ts
// Before
await conversation.updateAppData("payload");

// After
await conversation.updateAppData({ value: "payload" });
```

### `registerIdentity` takes an optional second argument

`registerIdentity(signatureRequest, visibilityConfirmationOptions?)`. Existing single-argument calls continue to work.

### `prepareMessage` takes an optional third argument

`prepareMessage(encodedContent, shouldPush, idempotencyKey?)`. Existing two-argument calls continue to work.

### `getConversationById` validates the group ID

Group IDs are now fixed-size values. A hex string that does not decode to a valid group ID is rejected with an error rather than producing a conversation handle that fails later.

## New Features

### Idempotency keys on sends

`SendMessageOpts` and the new `SendOpts` both accept `idempotencyKey`. Re-sending identical content with the same key produces the same message ID and is deduplicated, so a retry after a timeout does not create a second message. Defaults to a timestamp when omitted.

### `client.waitForRegistrationVisible(options?)`

Wait until this client's registration is visible on the network. For clients on the current network this falls back to checking `isRegistered`; on the decentralized network it polls nodes directly and returns once a quorum confirms both the identity-update and key-package envelopes are visible.

Configurable with `quorumPercentage` (default 50%), `quorumAbsolute` (takes precedence when both are set), and `timeoutMs` (default 30 000 ms). Also available inline as the second argument to `registerIdentity`.

### Background worker configuration

`WorkerConfigOptions` — passed to `createClient` / `createClientWithBackend` — lets you tune the background worker scheduler:

```ts
const workerConfig = {
  defaultIntervalNs: 5_000_000_000n,
  workerIntervalsNs: [{ kind: WorkerKind.DeviceSync, intervalNs: 30_000_000_000n }],
  workerJittersNs: [{ kind: WorkerKind.CommitLog, jitterNs: 1_000_000_000n }],
  disabledWorkers: [WorkerKind.DisappearingMessages],
};
```

Worker kinds: `DeviceSync`, `DisappearingMessages`, `KeyPackageCleaner`, `CommitLog`, `TaskRunner`. Omitting the whole object preserves default behavior.

### Group metadata migration APIs

- `conversation.proposalsEnabled()` — whether this group has migrated to proposal-based metadata updates.
- `conversation.unstable.enableProposals({ force?, minVersion? })` — stage the migration. One-way and irreversible; hard-fails if any member's latest key package does not advertise the required proposal type. The `.unstable` namespace is the deliberate opt-in and will break at compile time when the API graduates.

## Bug Fixes

- **`streamMessageDeletions` never invoked `onClose`.** The stream now calls the close handler when it ends, matching the other streams.
- **Logging initialization no longer fails hard** when another crate has already installed a `tracing` subscriber — it logs a warning and continues instead of panicking.
- **Crash on the welcome stream** after a group's creator left the group and was later re-added.
- **False-positive fork detection** after a member was removed from a group.
- **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered.
- **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows.
- **Intentional transaction rollbacks** are no longer reported as telemetry errors.
- **Database disconnect errors** now propagate out of workers to the supervisor instead of being swallowed.
- **Dropped group invites for members using smart contract wallets.** A transient failure while verifying an ERC-1271/6492 signature was classified as permanent, so the welcome cursor advanced and the invite was skipped for good. Those failures are now retryable and the welcome is reprocessed on the next sync.
- **New installations reliably added to existing groups.** Reconciliation after a welcome now runs as a durable, retried task instead of one inline attempt whose per-group failures were swallowed, so a lost race no longer permanently skips a group.
- **Downgrade resilience for group intents.** Intent rows written by a newer version are now skipped rather than failing every intent query for that group, so downgrading to this version no longer wedges outbound messages in an affected group.
- **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported.
- **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in `SyncSummary` rather than replaced with a generic error.
- **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing.
- **Groups stuck below the protocol version floor** now recover automatically once the client upgrades past the required floor, instead of staying paused.
- **Reduced log noise** across retry, sync, welcome, and worker paths — steady-state and per-item logs moved from info/error to debug/warn.
