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

# Swift API reference

> The headless public surface of the Swift SDK.

All methods are static on `ClarioDesk`. Reads are replay-last `AsyncStream`s
(a new iterator immediately receives the current value, then live updates),
with closure-based `subscribe*` variants for UIKit hosts that return a
cancellable `ClarioSubscription`.

```swift theme={null}
import ClarioDesk
```

## Lifecycle

### `initialize`

```swift theme={null}
ClarioDesk.initialize(_ options: ClarioDeskOptions)
```

Synchronous, fire-and-forget, idempotent — call from `App.init` or
`application(_:didFinishLaunchingWithOptions:)` with no `await`. Every
subsequent method awaits the shared bootstrap internally, so nothing races.
First launch generates the Secure Enclave device key and registers the device;
later launches reuse it.

`ClarioDeskOptions` — only `apiKey` is required:

```swift theme={null}
ClarioDeskOptions(
  apiKey: String,
  baseURL: URL? = nil,                 // default https://api.clariodesk.com
  appVersion: String? = nil,           // default: CFBundleShortVersionString
  experimentalCentrifugo: Bool = false, // deprecated/ignored; grant-SSE only
  pushTokenProvider: (any PushTokenProvider)? = nil,
  identityTokenProvider: (any IdentityTokenProvider)? = nil,
  onDiagnostic: ClarioDiagnosticHandler? = nil, // non-fatal event receipts
  logCollector: (@Sendable () async -> String?)? = nil,  // bug-report logs
  screenNameProvider: (@Sendable () -> String?)? = nil   // client_context.screen_name
)
```

### `identify`

```swift theme={null}
try await ClarioDesk.identify(
  externalId: String? = nil,
  name: String? = nil,
  email: String? = nil,
  traits: [String: any Sendable]? = nil,
  identityToken: String? = nil,
  identityMode: IdentityMode? = nil
)
```

Without a token, attach unverified display metadata. With a token/provider,
perform [Verified identity](/concepts/verified-identity). Proof failures never
fall back to labels.

```swift theme={null}
let thumbprint = try await ClarioDesk.deviceThumbprint()
try await ClarioDesk.clearIdentity()
```

Use `clearIdentity()` before Verified-identity logout/account switch.

### `reset`

```swift theme={null}
try await ClarioDesk.reset()
```

Acknowledged one-installation revoke, then wipe the hardware key, caches, and
outbox. It is not logout or customer erase. The next `initialize` registers a
fresh device.

### State & diagnostics

```swift theme={null}
ClarioDesk.isInitialized  // Bool
ClarioDesk.isIdentified   // Bool
ClarioDesk.isAvailable    // Bool — false after a terminal server state
                          // (device revoked / key expired / app suspended)
await ClarioDesk.diagnostics()  // ClarioDeskDiagnostics snapshot; never throws
```

## Tickets & messages

### `createTicket`

```swift theme={null}
let (ticket, message) = try await ClarioDesk.createTicket(
  subject: String,
  body: String,
  type: TicketType = .ticket,          // .ticket | .bug
  attachments: [OutgoingAttachment] = []
)
```

### `sendMessage`

```swift theme={null}
let message = try await ClarioDesk.sendMessage(
  ticketId: String,
  body: String,
  attachments: [OutgoingAttachment] = []
)
```

Optimistic: the message appears as pending immediately and settles on success.
An offline send waits in the durable Keychain outbox and auto-flushes on
reconnect.

### `retryMessage`

```swift theme={null}
try await ClarioDesk.retryMessage(message)
```

Re-send a message whose optimistic write failed (`message.failed == true`).
Tap-to-retry only; never auto-retried.

### One-shot fetches

```swift theme={null}
try await ClarioDesk.fetchTickets()      // [Ticket]
try await ClarioDesk.fetchTicket(id:)    // (ticket: Ticket, messages: [Message])
```

### Read & typing

```swift theme={null}
await ClarioDesk.markRead(ticketId:)           // best-effort, never throws
await ClarioDesk.setTyping(ticketId:, true)    // best-effort; throttle to ~1/3s
```

## Bug reports

### `submitBugReport`

```swift theme={null}
let (ticket, message) = try await ClarioDesk.submitBugReport(
  description: String,
  screenshot: OutgoingAttachment? = nil,
  attachments: [OutgoingAttachment] = []   // cap: 4 total per message
)
```

A thin wrapper over ticket-create with `type: .bug`: the subject is derived
from the description (first line, ≤60 chars — the user never types a title),
the `logCollector` output (if wired in options) is attached as
`clariodesk-logs.txt`, and the optional screenshot rides along. One-shot — no
outbox; surface failure with tap-to-retry.

The prebuilt `ClarioDeskWidgets.openBugReport()` auto-captures a screenshot
and calls this for you (see [prebuilt UI](/swift/prebuilt-ui), including
opt-in shake-to-report); headless hosts call it directly.

## CSAT

### `submitCsat`

```swift theme={null}
try await ClarioDesk.submitCsat(
  ticketId: String,
  score: Int,          // 1 (😔) … 5 (😄)
  comment: String? = nil
)
```

See [CSAT](/concepts/csat). The rating is cached on the `Ticket`
(`csatScore` / `csatComment` / `hasCsat`).

## Attachments

```swift theme={null}
// A file the host picked; the SDK signs, uploads, and binds it.
OutgoingAttachment(
  path: String, mime: String, kind: AttachmentKind,   // .image | .video | .file
  filename: String? = nil, width: Int? = nil, height: Int? = nil
)

// Live upload progress (0…1) for an optimistic stub, by its localId:
ClarioDesk.attachmentUploadProgress(localId:)  // AsyncStream<Double>?

// Re-presign an expired GET URL:
try await ClarioDesk.refreshAttachmentUrl(id:) // Attachment
```

See [Attachments](/concepts/attachments) for limits and the upload flow.

## Push

```swift theme={null}
ClarioDesk.setAPNSDeviceToken(token)          // AppDelegate token forward
await ClarioDesk.registerPushToken(token)     // direct registration (FCM bridge)
ClarioDesk.isClarioMessage(userInfo)          // Bool
ClarioDesk.parsePushPayload(userInfo)         // ClarioPushPayload?
ClarioDesk.openTicketFromPush(userInfo)       // parses + emits on pushOpened
try await ClarioDesk.getNotificationPreferences()
try await ClarioDesk.setNotificationPreferences(
  pushEnabled: Bool? = nil,
  quietHoursStart: String? = nil,  // "HH:MM"
  quietHoursEnd: String? = nil,
  clearQuietHours: Bool = false,
  timezone: String? = nil
)
```

See the [push guide](/swift/push).

## Reactive reads

Streams that can carry a load failure are throwing; the rest are plain
`AsyncStream`s. Subscribing before `initialize` throws `.notInitialized` on
first await.

```swift theme={null}
ClarioDesk.tickets                    // AsyncThrowingStream<[Ticket], Error>
ClarioDesk.messages(ticketId:)        // AsyncThrowingStream<[Message], Error>
ClarioDesk.typing(ticketId:)          // AsyncStream<Bool>
ClarioDesk.agentPresence              // AsyncStream<Bool>
ClarioDesk.connection                 // AsyncStream<ClarioDeskConnectionState>
                                      // connecting | connected | reconnecting
                                      // | disconnected | fatal
ClarioDesk.sync(ticketId:)            // AsyncStream<ConversationSyncState>
                                      // loading | live | recovering
                                      // | catchingUp | stale | error
ClarioDesk.availability               // AsyncStream<Bool> — isAvailable, live
ClarioDesk.pushOpened                 // AsyncStream<String> — not replayed
```

### Closure subscriptions (UIKit)

Same semantics; each returns a `ClarioSubscription` — call `.cancel()` when
done.

```swift theme={null}
ClarioDesk.subscribeTickets { result in … }
ClarioDesk.subscribeMessages(ticketId:) { result in … }
ClarioDesk.subscribeTyping(ticketId:) { isTyping in … }
ClarioDesk.subscribeAgentPresence { online in … }
ClarioDesk.subscribeConnection { state in … }
ClarioDesk.subscribeSync(ticketId:) { state in … }
ClarioDesk.subscribeAvailability { available in … }
ClarioDesk.subscribePushOpened { ticketId in … }
```

## Types

All models are `Sendable` value types: `Ticket`, `Message`, `Attachment`,
`OutgoingAttachment`, `NotificationPreferences`, `ClarioPushPayload`,
`ClarioDeskError`, and the enums above (`TicketType`, `TicketStatus`,
`MessageAuthor`, `AttachmentKind`, `MessageReadState`).

<Note>
  This SDK implements the same wire contract as the
  [Flutter](/flutter/api-reference) and
  [React Native](/react-native/api-reference) SDKs — identical bytes on the
  wire and the same state machines, expressed in Swift idioms (`initialize`
  instead of `init`, tuples for create results, `AsyncStream` reads).
</Note>
