<!-- AI agents: this is the Markdown version of a Reception documentation page. Index: /llms.txt -->

# Swift API reference

The complete public ReceptionKit API for configuration, identity, chat presentation, notifications, observable state, appearance, events, and deletion errors. Signatures and behavior on this page follow the current Swift sources.

## Overview

Use this reference to look up an individual method, property, or type. The [iOS SDK guides](/docs/ios-sdk) walk through integrating those APIs into your app, with examples for presentation, unread badges, account changes, and push notifications.

Import `ReceptionKit` in your app. Import `SwiftUI` for views and appearance values, and `Foundation` when using `URL` or `Data` without SwiftUI. ReceptionKit requires iOS 17 or later. See [Get started](/docs/get-started) to add the package.

Configuration, instance members, and `ReceptionChatView` require the main actor. Static logging settings and `version` can be accessed from any thread. The reference shows inherited `@MainActor` isolation explicitly. From asynchronous code outside the main actor, use an actor hop; do not move a notification's untyped payload across actors without following the [push integration guidance](/docs/push-notifications).

Only `deleteData()` is asynchronous and throwing. The other facade methods return synchronously; completion does not confirm a server update. Device registration begins on the first valid send attempt. Configuration and staging identity do not register an unused device. The configured SDK or a visible chat may independently fetch public project appearance before the first message.

| Area | Public members |
| --- | --- |
| Configure | [configure](#configure), [isConfigured](#is-configured) |
| Chat behavior | [Connection and retry](#retries) |
| Logging | [logLevel](#log-level), [logHandler](#log-handler), [version](#version) |
| Identity | [identify](#identify), [identify(token:)](#identify-token), [setMetadata](#set-metadata) |
| Present | [openChat](#open-chat), [closeChat](#close-chat), [ReceptionChatView](#reception-chat-view) |
| Push | [setPushToken](#set-push-token), [handlePushNotification](#handle-push-notification), [Reception.PushEnvironment](#push-environment) |
| Paywall cards | [paywalls](#paywalls), [onPaywall](#on-paywall), [paywallAvailability](#paywall-availability), [availablePaywalls](#available-paywalls), [refreshPaywalls](#refresh-paywalls), [ReceptionPaywall](#reception-paywall) |
| Lifecycle | [logout](#logout), [deleteData](#delete-data) |
| State and settings | [shared](#shared), [unreadCount](#unread-count), [isChatOpen](#is-chat-open), [updatesAppBadge](#updates-app-badge), [appearance](#reception-appearance), [usesRemoteAppearance](#uses-remote-appearance), [languageOverride](#language-override), [onEvent](#on-event) |
| Value types | [Reception.Appearance](#appearance-type), [Reception.CloseIcon](#close-icon), [ReceptionEvent](#reception-event), [ReceptionError](#reception-error) |

### Reception

Declaration from `Reception.swift`:

```swift
@MainActor @Observable
public final class Reception
```

Use `Reception.configure` once during startup, then access chat and instance settings through `Reception.shared`. The facade has no public initializer. Logging settings and the SDK version are static members of `Reception`.

## Configuration

### configure

```swift
@MainActor
public static func configure(appId: String)
```

Call once during app startup, before supplying identity or creating support views. `appId` is your public Reception App ID, starting with `app_`, from **Settings → Apps → your app → Setup** in the dashboard. It is safe to include in your app. Reception hosts the service, so your app needs no other configuration.

```swift
import ReceptionKit

@MainActor
func configureSupport() {
    Reception.configure(appId: "app_YOUR_APP_ID")
}
```

Configuration does not register an unused device or load its chat. Support registration starts with the first valid send attempt. Published appearance can load before that. Repeating the same App ID is a no-op. A different App ID switches the local support session, clears the displayed unread count, and dismisses the SDK sheet; it does not delete stored server data.

A malformed App ID is ignored with a developer error log. An identity secret is not an App ID and must never be included in your app. Use [`isConfigured`](#is-configured) to check whether configuration was accepted; it does not test connectivity.

### isConfigured {#is-configured}

```swift
@MainActor
public static var isConfigured: Bool { get }
```

It is `true` once a valid `configure(appId:)` call has been accepted in this process. It does not mean a device has registered or that chat is connected. Use it before calling methods that require configuration, such as `identify` or `openChat`.

## Identity and lifecycle {#identity}

### identify

Facade declaration:

```swift
@MainActor
public func identify(userId: String?, name: String? = nil, email: String? = nil)
```

`userId` is required; `name` and `email` are optional and default to `nil`. `userId` is your app's stable account identifier, `name` is a display name, and `email` is the user's email address. Pass `nil` for unavailable values. Values are not trimmed. The limits are 255 characters for user ID, 200 for name, and 254 for email. Some emoji count as more than one character. ReceptionKit leaves out a longer value and logs an error.

Replaces the three locally staged identity fields, leaving staged metadata and push registration intact. It does nothing before configuration and does not initiate a request. Values travel with registration or the next device update, such as a chat load, send, or foreground synchronization after support has started.

A `nil` value is omitted from the request; it does **not** clear an existing server field. Identification labels this device and does not authenticate your host account, merge devices, or restore another device's conversation. Call `logout()` before switching accounts. On a chat verified through [identify(token:)](#identify-token), these values no longer change the identity; a different user ID only logs an error. See [Identity](/docs/accounts#metadata) and [Login and logout](/docs/accounts#sign-out).

Place this in your main-actor session-restoration or successful-login handler, after configuration:

```swift
Reception.shared.identify(userId: "user_123", name: "Alex", email: "alex@example.com")
```

### identify(token:) {#identify-token}

Facade declaration:

```swift
@MainActor
public func identify(token: String)
```

Stages an identity token that your server signed for [identity verification](/docs/accounts#verified-users). ReceptionKit keeps it in memory only and sends it with the next registration or device update, such as a chat load, send, or foreground synchronization after support has started. Passing the same token as last time does nothing. Call it after sign-in and on every launch while a user is signed in. It does nothing before configuration. Switching users also requests remote sign-out of the old session.

The call reads the token's `user_id` right away, without verifying it. If that user differs from the verified or identified user of the current chat, ReceptionKit immediately ends the old session and resets local state as `logout()` does; only the new token and the push token carry over. The next send starts a new chat for the token's user.

An `ids_` secret, an overlong token, or a token whose `user_id` the SDK can't read is ignored locally without an event. Other invalid tokens may reach Reception and emit `identityRejected`.

If Reception rejects the token as invalid, expired, or unconfigured, ReceptionKit drops it, logs an error, and emits [`identityRejected`](#reception-event-cases). Existing verification is unchanged; unverified chats remain subject to the app’s verified-only setting. A rejected registration leaves the send ready to retry. If the current registration belongs to another user, ReceptionKit resets the old session and keeps the new token for the next send.

`logout()`, `deleteData()`, and configuring another App ID forget the token. It never replaces `logout()` at sign-out.

Place this in your main-actor sign-in and session-restoration handlers, after configuration, with a token from your server:

```swift
Reception.shared.identify(token: token)
```

### setMetadata {#set-metadata}

Facade declaration:

```swift
@MainActor
public func setMetadata(_ metadata: [String: String])
```

The unlabeled argument is required. Replaces the entire staged dictionary; it does not merge keys. Pass `[:]` to replace metadata with an empty dictionary on the next device update. Identity and push values remain staged separately.

Does nothing before configuration and makes no immediate request. Keys and values must be strings; convert numbers and booleans to your intended display strings. Up to 20 entries are allowed, with keys of 1–64 characters and values up to 512 characters. Entries outside those length limits are omitted. Supplying more than 20 entries replaces the dictionary with an empty one, clearing saved metadata on the next successful synchronization. There is no completion callback or flush method.

Place this in your main-actor account or subscription update handler. Supply the full set of fields you want to retain:

```swift
Reception.shared.setMetadata(["plan": "Pro", "provider": "RevenueCat"])
```

### logout

Facade declaration:

```swift
@MainActor
public func logout()
```

Immediately clears this device's local support session, identity, metadata, push registration, chat history, draft, pending sends, and unread state, and dismisses the SDK sheet. Server history remains.

ReceptionKit also asks the service to end the old session. Returning from this synchronous method does not confirm that request completed; notifications already in flight can still arrive. See [Login and logout](/docs/accounts#logout).

The next send starts a new session. Reapply the current identity, metadata, and APNs token. Configuration, appearance, language, badge preference, and event handlers remain. Logout emits no event.

Place this in your main-actor sign-out handler, before identifying a different user:

```swift
Reception.shared.logout()
```

### deleteData {#delete-data}

Facade declaration:

```swift
@MainActor
public func deleteData() async throws
```

Asks Reception to delete the current device's support data, including conversations and images. It does not delete your app's user account or support data on other devices.

Completion resets local state, dismisses the SDK sheet, and emits `.dataDeleted`. With no session, or when Reception no longer recognizes the session or app, completion can be local only: it is not a receipt proving that historical server data was erased. Before configuration, the method does nothing.

Failures throw [ReceptionError](#reception-error) and keep the current session available for retry. A failed response does not establish whether deletion finished on the server. If the active session changes during deletion, its replacement is preserved. Await deletion before continuing your account transition.

> **Important:** When an account is deleted, call `deleteData()` instead of `logout()`, and await it before discarding the current support session. On failure, keep the session and offer retry. Calling logout first ends the session needed to delete that device's data.

Place this helper in your host account-deletion flow. Its caller must catch failures, present retry, and advance the account flow only after successful return:

```swift
import ReceptionKit

@MainActor
func deleteSupportDataBeforeContinuing() async throws {
    try await Reception.shared.deleteData()
}
```

See [Delete data](/docs/accounts#delete-data) for the complete host-owned flow and deletion limits.

## Presentation

### openChat {#open-chat}

Facade declaration:

```swift
@MainActor
public func openChat()
```

No arguments, return value, or thrown errors. Presents the SDK chat sheet over the current screen in SwiftUI and UIKit apps. If no screen is ready yet, such as during a cold start, ReceptionKit opens it after the app becomes active. A call while chat is already visible does nothing.

Configure first. `openChat()` does not confirm visibility, emit `.chatOpened` by itself, or control a host-owned navigation path. Use `Reception.shared.isChatOpen` for visibility. Host-owned sheets and navigation use SwiftUI dismissal or host state.

Place this view below your configured app root. The host supplies the entry-point label and layout:

```swift
import SwiftUI
import ReceptionKit

@MainActor
struct AppRoot: View {
    var body: some View {
        Button("Support") { Reception.shared.openChat() }
    }
}
```

### closeChat {#close-chat}

Facade declaration:

```swift
@MainActor
public func closeChat()
```

Dismisses the chat sheet presented by `openChat()` or a notification tap. It does nothing when the chat is not presented by the SDK; a `ReceptionChatView` placed by the host is dismissed by the host.

### ReceptionChatView {#reception-chat-view}

View declaration:

```swift
@MainActor
public struct ReceptionChatView: View
```

Use directly for a host-owned sheet or navigation destination. When presented, it supplies a navigation stack, native drag indicator, and the optional appearance-controlled close button. Embedded content uses the host's navigation context. The view applies SDK localization, layout direction, and appearance; visible active chat manages message loading, read state, and live updates.

Your host controls direct presentation and dismissal. Do not also call `Reception.shared.openChat()` for the same action: that method and forwarded push taps always use the SDK sheet. Unread monitoring already starts with configuration.

### ReceptionChatView.init {#reception-chat-view-init}

Initializer declaration:

```swift
@MainActor
public init()
```

No arguments and no thrown errors. Configure Reception before creating the view. Constructing it does not mean it is visible or that a device has registered.

Place this navigation destination in your host's SwiftUI screen after configuration:

```swift
NavigationStack {
    NavigationLink("Support") {
        ReceptionChatView()
    }
}
```

### ReceptionChatView.body {#reception-chat-view-body}

Property declaration:

```swift
@MainActor
public var body: some View { get }
```

Read-only SwiftUI content. SwiftUI evaluates this property; compose `ReceptionChatView()` rather than invoking `body` directly.

## Push

### setPushToken {#set-push-token}

Facade declaration:

```swift
@MainActor
public func setPushToken(
    _ token: Data,
    environment: Reception.PushEnvironment = .automatic
)
```

`token` is the required raw APNs token received by your app delegate. Do not pass a UTF-8 encoding of its hexadecimal description. `environment` defaults to `.automatic`; see [Reception.PushEnvironment](#push-environment) for resolution and explicit overrides.

After configuration, stages a lowercase hexadecimal token and its resolved environment. Empty tokens, unresolved environments, and calls without a configured session are ignored. There is no fixed APNs token byte count enforced by the method.

If a stored session exists, schedules a device update and unread refresh, subject to support having started. An unused device is not registered just to store push details. This method does not request notification permission, register with APNs, or confirm delivery; the host owns those steps. Call again when APNs supplies a token and after resetting the local session.

Place this statement in the host app's main-actor APNs registration callback, where `deviceToken` is the callback's `Data` argument:

```swift
Reception.shared.setPushToken(deviceToken)
```

### handlePushNotification {#handle-push-notification}

Facade declaration:

```swift
@MainActor @discardableResult
public func handlePushNotification(
    userInfo: [AnyHashable: Any],
    openChat: Bool = true
) -> Bool
```

`userInfo` is required. Returns `true` only when `userInfo["reception"]` casts to `[String: Any]` and contains a nonempty String `conversationId`. Other payloads return `false` without requesting presentation or refreshing unread state.

For a recognized payload, presents the SDK sheet if `openChat` is `true`, including after a cold start, and schedules an unread refresh if support has started. Use the default for notification taps and `false` for foreground receipt. The result means the payload shape was recognized, not that the chat appeared, a request succeeded, or the identifier belongs to the current device. The ID is not used to navigate to a historical conversation; the SDK opens its current chat. The method does not require an `aps` dictionary and does not apply its badge value directly.

The host still controls notification banners, sound, completion handlers, and routing for other notifications. See [Push notifications](/docs/push-notifications) for delegate examples and actor-safe forwarding.

### Reception.PushEnvironment {#push-environment}

Type declaration:

```swift
public enum PushEnvironment: Sendable
```

A value type with three cases and no associated values. It has no public raw-value conversion or environment-resolution method, and is not declared `@MainActor`.

### Reception.PushEnvironment cases {#push-environment-cases}

Case declarations:

```swift
case automatic, sandbox, production
```

| Case | Behavior |
| --- | --- |
| `.automatic` | Default token setting. On a physical device, reads `aps-environment` from the embedded provisioning profile: `development` means sandbox, `production` means production. No embedded profile means production. An unreadable, malformed, or unrecognized profile resolves to no update. On the simulator, resolves to no update. |
| `.sandbox` | Explicitly sends `SANDBOX` for development provisioning. |
| `.production` | Explicitly sends `PRODUCTION` for distribution provisioning. |

Explicit overrides bypass automatic detection. They do not create a valid token or prove APNs delivery; verify push with a real device and matching provisioning.

## Observable state and settings

### shared

Property declaration:

```swift
@MainActor
public static let shared: Reception
```

The single shared facade, created by the SDK. Read instance state through this property; it cannot be replaced. Static settings and methods operate on the same instance.

### unreadCount {#unread-count}

Property declaration:

```swift
@MainActor
public private(set) var unreadCount: Int = 0
```

Read as `Reception.shared.unreadCount`. Observable in SwiftUI and not writable by the host. Starts at zero and is clamped to a nonnegative value. Represents the current device's latest-conversation unread count from SDK synchronization, not a cross-device total. Before synchronization, zero is not proof that the server has no unread messages.

After marking displayed messages read, the count reflects any replies still unread; a new reply can keep it above zero. Configuration and local resets set it to zero. ReceptionKit updates it automatically as support activity and app state change. See [Unread messages](/docs/unread-messages) for a badge example.

### isChatOpen {#is-chat-open}

Property declaration:

```swift
@MainActor
public var isChatOpen: Bool { get }
```

Read as `Reception.shared.isChatOpen`. Initially `false`; becomes `true` when chat content appears and `false` when it disappears. SwiftUI observes its underlying state. It reflects visibility, including direct presentation, and is not a connection or app-foreground indicator. A pending `openChat()` presentation does not make it true.

### updatesAppBadge {#updates-app-badge}

Property declaration:

```swift
@MainActor
public var updatesAppBadge: Bool = true
```

When enabled, SDK unread synchronization sets the app icon badge, even if the count did not change. At zero it also removes delivered Notification Center entries whose `userInfo` contains `reception`; other entries remain. Badge-setting errors are ignored.

Set `false` during app initialization when the host owns the icon badge and notification cleanup. It disables both SDK actions without changing `unreadCount`. Registration includes this preference; changes are synchronized asynchronously for an active support session, with retries after failures and on foreground. Once saved, subsequent support pushes omit `aps.badge` entirely while retaining their alert and configured sound. Offline, the server uses the last successfully saved preference; already prepared or delivered notifications are not changed. Assigning the flag does not clear or recalculate an existing badge. Set it on each app launch; its default is `true`. Reception does not combine counts from other host features. See [Unread messages](/docs/unread-messages).

### appearance {#reception-appearance}

Property declaration:

```swift
@MainActor
public var appearance: Appearance { get set }
```

Defaults to `Reception.Appearance()`. Read, replace, or mutate local styling. The getter returns local values, without the published remote overrides used by the displayed chat. The [14-property index](#appearance-properties) links to each canonical option, its default, and behavior.

Each appearance option accepts values from your Swift variables or theme objects when their types match. Reassign values when your app changes its theme; these assignments do not create bindings. See [Use your app’s theme and variables](/docs/appearance#app-themes-and-variables) for an example, adaptive colors, and dashboard precedence.

### usesRemoteAppearance {#uses-remote-appearance}

Property declaration:

```swift
@MainActor
public var usesRemoteAppearance: Bool { get set }
```

Defaults to `true`. Enables published remote appearance overrides on top of local values. Set `false` to use local appearance only. Published changes refresh while the app is active and become visible on a later chat opening. If an appearance update can't be downloaded, the chat keeps its previous styling. See [Remote appearance](/docs/remote-appearance) and [precedence](/docs/appearance#precedence).

### languageOverride {#language-override}

Property declaration:

```swift
@MainActor
public var languageOverride: String? { get set }
```

Defaults to `nil`, following the iOS-selected localization of the host app. Set a BCP 47 tag for a custom in-app language picker; assign `nil` to restore automatic selection. The getter returns the supplied override, not the resolved SDK language. Assigning the same value has no effect.

The SDK canonicalizes language tags, matches its 48 supported localizations, and falls back to English for invalid or unsupported selections. Built-in labels, formatting, direction, default appearance text, and available remote translations follow the selection. Host-supplied strings and existing messages remain unchanged. An unused device is not registered by changing language; started sessions synchronize the selection through device updates with retry. See [Language](/docs/language) for all supported codes and matching rules.

## Events

### onEvent {#on-event}

Property declaration:

```swift
@MainActor
public var onEvent: ((ReceptionEvent) -> Void)?
```

Defaults to `nil`. Assign one synchronous handler; another assignment replaces it. Set `nil` to remove it. SDK call sites invoke it on the main actor. Keep the handler short and schedule lengthy work separately. There is no event history, subscription token, or replay. Local resets do not remove this handler.

Place this in main-actor startup code to observe successful SDK sends:

```swift
Reception.shared.onEvent = { event in
    switch event {
    case .messageSent:
        print("Support message confirmed")
    default:
        break
    }
}
```

### ReceptionEvent {#reception-event}

Type declaration:

```swift
public enum ReceptionEvent: Hashable
```

Seven cases. Only `identityRejected` carries a value: the [ReceptionError](#reception-error) of a rejected identity token. The type is `Equatable` and `Hashable`, so comparisons such as `event == .messageSent` work. Events carry no user identifiers or message bodies. See [Events](/docs/events) for analytics integration.

### ReceptionEvent cases {#reception-event-cases}

Case declarations:

```swift
case chatOpened, messageSent, imageSent, reviewOpened, paywallOpened
case dataDeleted
case identityRejected(ReceptionError)
```

| Case | Exact trigger |
| --- | --- |
| `.chatOpened` | Chat content's `onAppear`, including host-owned presentation. May fire again on a later appearance. Calling `openChat()` alone does not emit it. |
| `.messageSent` | A successful send response for the current session, including image messages and successful retries. Does not fire when merely queuing a draft or imply an agent read it. |
| `.imageSent` | Immediately after `.messageSent` if the confirmed draft contains images. Once per send, not once per image. |
| `.reviewOpened` | When the review-card action is invoked, immediately before opening its URL. Does not confirm URL-opening success or a submitted review. |
| `.paywallOpened` | When an available paywall-card action is invoked. Reports the tap, not successful presentation or purchase. |
| `.dataDeleted` | Completion of `deleteData()`, including local-only completion, unknown or ended sessions, and an unavailable app. Does not certify server-side erasure. Not emitted by failed deletion or `logout()`. Still emitted if a replacement session was preserved during deletion. |
| `.identityRejected(_:)` | Reception rejected the staged identity token with `identity_token_invalid`, `identity_token_expired`, or `identity_not_configured`. ReceptionKit has dropped the token; existing verification is unchanged, and unverified chats remain subject to the verified-only setting. Fetch a new token for `identity_token_expired`; the other codes need a fix on your server or in the dashboard. |

## Appearance

### Reception.Appearance {#appearance-type}

Type declaration:

```swift
public struct Appearance
```

A value type with 14 public writable properties. Only `title`, `welcomeTitle`, and `welcomeText` are individually declared `@MainActor`; accessing any option through `Reception.shared.appearance` requires the main actor. Detailed property signatures, defaults, examples, and constraints live on [Appearance](/docs/appearance).

### Reception.Appearance.init {#appearance-init}

Initializer declaration:

```swift
public init()
```

No arguments and no thrown errors. Creates the default local appearance. There is no public memberwise initializer accepting appearance fields; create a value and mutate its properties. Assigning a fresh value to `Reception.shared.appearance` restores local defaults but does not remove active remote overrides.

### Reception.Appearance property index {#appearance-properties}

See what each option changes, then follow its link for defaults, allowed values, and a Swift example. All 14 properties are readable and writable.

![Native chat with a purple outgoing bubble and violet incoming bubbles](/docs/images/ios-appearance-colors.webp "Colors: accentColor sets outgoing fills; incomingBackground sets incoming fills.")

![Native chat with the optional close button in the top-right corner](/docs/images/ios-close-xmark.webp "Dismissal: showsCloseButton reveals the button; closeIcon selects its symbol.")

| Property | Type | What changes |
| --- | --- | --- |
| `accentColor` | `Color` | [Outgoing bubble fills and accented controls](/docs/appearance#accentcolor) |
| `onAccentColor` | `Color?` | [Text and symbols on accent surfaces](/docs/appearance#onaccentcolor) |
| `title` | `String` (`@MainActor`) | [Navigation title at the top of chat](/docs/appearance#title) |
| `welcomeTitle` | `String` (`@MainActor`) | [Heading shown in an empty chat](/docs/appearance#welcometitle) |
| `welcomeText` | `String` (`@MainActor`) | [Supporting text below the welcome heading](/docs/appearance#welcometext) |
| `showsCloseButton` | `Bool` | [Visibility of the presented chat’s close button](/docs/appearance#showsclosebutton) |
| `chatBackground` | `Color?` | [Background surface behind the conversation](/docs/appearance#chatbackground) |
| `incomingBackground` | `Color?` | [Incoming bubble, review card, and paywall card fills](/docs/appearance#incomingbackground) |
| `incomingForeground` | `Color?` | [Incoming message text and review/paywall card body text](/docs/appearance#incomingforeground) |
| `preferredColorScheme` | `ColorScheme?` | [Follow the app, or force light or dark mode](/docs/appearance#preferredcolorscheme) |
| `closeIcon` | `Reception.CloseIcon` | [Close button symbol: cross, chevron, or arrow](/docs/appearance#closeicon) |
| `fontDesign` | `Font.Design?` | [System font design, such as rounded or serif](/docs/appearance#fontdesign) |
| `fontFamily` | `String?` | [Custom font bundled by your app](/docs/appearance#fontfamily) |
| `fadesOlderMessages` | `Bool` | [Lighter fills for older outgoing text bubbles](/docs/appearance#fadesoldermessages) |

### Reception.CloseIcon {#close-icon}

Type declaration:

```swift
public enum CloseIcon: String, Sendable
```

Selects the symbol used when the chat's close button is shown. No actor isolation is declared on the type. Changing the icon does not make the button visible; configure [showsCloseButton](/docs/appearance#showsclosebutton) separately.

### Reception.CloseIcon cases {#close-icon-cases}

Case declarations and their exact SF Symbol raw values:

```swift
case xmark
case chevronDown = "chevron.down"
case arrowDown = "arrow.down"
```

The raw value of `.xmark` is `"xmark"`. `Reception.Appearance.closeIcon` defaults to `.xmark`.

### Reception.CloseIcon.rawValue {#close-icon-raw-value}

Synthesized property declaration:

```swift
public var rawValue: String { get }
```

Read-only raw string for the selected case. The raw values are exactly `"xmark"`, `"chevron.down"`, and `"arrow.down"`.

### Reception.CloseIcon.init(rawValue:) {#close-icon-init}

Synthesized initializer declaration:

```swift
public init?(rawValue: String)
```

The required, case-sensitive string must match a raw value exactly. Unknown strings return `nil`; the initializer does not throw or choose a fallback icon.

## Errors

### ReceptionError {#reception-error}

Type declaration:

```swift
public struct ReceptionError: Error, Hashable, Sendable
```

Thrown by `Reception.shared.deleteData()` when completion cannot be established, and carried by the [`identityRejected`](#reception-event-cases) event. It has two read-only public properties and no public initializer. It is not an enum; error codes are an open set of strings. Other public facade methods do not throw. Chat request failures are handled by the built-in chat UI.

### ReceptionError.code {#error-code}

Property declaration:

```swift
public let code: String
```

The server error code, when available, or an SDK-generated failure code. Match codes you need and retain a fallback for unfamiliar ones. No localized user-facing error message is exposed by this property.

### ReceptionError.status {#error-status}

Property declaration:

```swift
public let status: Int
```

HTTP status, or zero for transport, storage, or configuration failures without a valid HTTP response. A response-decoding error retains the HTTP status, including 200.

### Deletion error codes {#deletion-error-codes}

| Code | Status and meaning |
| --- | --- |
| `connection_failed` | `0`: transport failure or another unmapped deletion failure. |
| `cancelled` | `0`: a request failure while the task is cancelled. Cancellation does not prove the server stopped deletion. |
| `secure_storage_unavailable` | `0`: the Keychain could not be read, for example before the first unlock after a restart. The session stays; retry later. |
| `invalid_configuration` | `0`: the SDK could not use its service address to construct a request. Contact Reception if this occurs. |
| `invalid_url` | `0`: the endpoint URL could not be constructed. |
| `invalid_response` | `0` for a non-HTTP response, or the received HTTP status for a response that cannot be decoded. |
| `http_error` | The received non-200 HTTP status when no server error envelope could be decoded. |
| `delete_failed` | Server `503`: deletion could not be completed. Keep the session and retry; some data may already have been removed. |
| Other server codes | The server's code and status pass through, including `internal_error` with `500`. |

Keep the current local session on failure and offer retry. Contact Reception for configuration errors; retry temporary transport or server failures when the service is available. Do not infer successful deletion from a failed request or clear the session in an error handler. See [Delete data](/docs/accounts#delete-data) and [Troubleshooting](/docs/troubleshooting).

## Connection and retry {#retries}

ReceptionKit handles connection recovery and temporary sending pauses. A message is **Sent** only after Reception confirms it. If a message shows **Not delivered**, follow the notice in chat and use **Retry** when available. Previously submitted messages can resume when the connection recovers or chat is reopened; an unsent draft is not sent automatically.

Your app does not need its own retry loop. Closing chat or leaving the app does not guarantee completion of a pending send. See [Chat features](/docs/chat-features) for visible states and [Troubleshooting](/docs/troubleshooting) if a problem persists.

## Logging {#logging}

Use developer logs in Xcode to investigate an integration issue. Ordinary chat errors are shown in the chat interface; your users do not need to collect logs.

### logLevel {#log-level}

```swift
nonisolated public static var logLevel: Reception.LogLevel { get set }
```

Minimum level that is emitted. Defaults to `.info`. Set it before `configure`:

```swift
Reception.logLevel = .debug
```

### Reception.LogLevel {#log-level-cases}

| Case | Emits |
| --- | --- |
| `.off` | No ordinary SDK logs; requested setup-check messages remain visible. |
| `.error` | Errors that need attention. |
| `.info` | Errors and significant SDK lifecycle events. Default. |
| `.debug` | Additional diagnostics for development and troubleshooting. |

Levels are `Comparable`; `.debug` includes `.info` and `.error`.

### logHandler {#log-handler}

```swift
nonisolated public static var logHandler: (@Sendable (Reception.LogLevel, String) -> Void)? { get set }
```

Defaults to `nil`. When set, ordinary SDK log lines go to the handler instead of the console. Requested setup-check messages still appear in Xcode and are not forwarded to this handler. Use it to forward SDK logs to your own logging system. The message has no `[Reception]` prefix; your handler owns the format. Both `logLevel` and `logHandler` may be read from any thread.

### version {#version}

```swift
nonisolated public static var version: String { get }
```

The SDK version, for example `0.1.0`. Include it when asking for integration help; it is also shown in dashboard device details.

### Log privacy {#what-is-never-logged}

SDK logs omit message text, image URLs, identity and metadata values, and authentication secrets. They can contain diagnostic identifiers. Review logs before sharing them and apply your app's own logging policy to a custom handler.

## Version and package

The library product and import name are `ReceptionKit`. The current manifest uses Swift tools version 6.0, Swift language mode 5, strict concurrency checking, and iOS 17 as the minimum platform. It has no external package dependencies. Use the setup instructions in [Get started](/docs/get-started) for the package URL and supported integration workflow.

## Paywall cards

Connect chat cards to purchase screens your app already owns. Reception records card opens; purchases stay with your app and payment provider. Follow [Paywall cards](/docs/paywalls) for a complete integration.

### paywalls {#paywalls}

```swift
@MainActor
public var paywalls: [ReceptionPaywall] { get set }
```

Defaults to `[]`. Assign your registered destinations during app setup, with stable IDs and titles your team recognizes. Titles appear only in the dashboard. Assignment replaces the list. IDs and titles are trimmed. IDs must contain 1–64 characters: ASCII letters, digits, underscores, dots, or hyphens. Titles must contain 1–60 characters. The SDK keeps the first 20 valid destinations with unique IDs and omits the rest.

```swift
Reception.shared.paywalls = [
    ReceptionPaywall(id: "plans", title: "Plans")
]
```

### onPaywall {#on-paywall}

```swift
@MainActor
public var onPaywall: ((String) -> Void)?
```

Defaults to `nil`. Receives the ID of an available card the customer tapped. Use the handler to open your existing purchase screen above chat. A card needs both an available destination and this handler; otherwise it appears unavailable. See [Present your paywall](/docs/paywalls) for host and provider examples.

### paywallAvailability {#paywall-availability}

```swift
@MainActor
public var paywallAvailability: (@MainActor (String) async -> Bool)?
```

Defaults to `nil`, which makes every registered destination available. Set a handler that returns whether the current user can see each destination, for example based on their subscription. Assigning a non-`nil` handler clears `availablePaywalls` while the new check runs. Assigning `nil` immediately makes all registered destinations available. Keep account and purchase decisions in your app.

### availablePaywalls {#available-paywalls}

```swift
@MainActor
public private(set) var availablePaywalls: [ReceptionPaywall]
```

Read-only available destinations, in registered order. Initially empty. Without an availability handler, this follows `paywalls`; with one, it contains the destinations the handler approved. The list determines which cards agents can send and customers can open.

### refreshPaywalls {#refresh-paywalls}

```swift
@MainActor
public func refreshPaywalls(invalidate: Bool = false)
```

Starts an availability refresh. With an availability handler, the default keeps current destinations available while it runs; pass `true` to clear them until the result is ready. Without a handler, all registered destinations remain available. The method returns synchronously; observe `availablePaywalls` for the result.

Call this from your main-actor subscription-change handler:

```swift
Reception.shared.refreshPaywalls(invalidate: true)
```

ReceptionKit also refreshes during normal app and chat lifecycle changes. See [Paywall cards](/docs/paywalls) for complete examples.

### ReceptionPaywall {#reception-paywall}

```swift
public struct ReceptionPaywall: Sendable, Hashable {
    public let id: String
    public let title: String
    public init(id: String, title: String)
}
```

A destination's stable identifier and the title agents see in the dashboard. Customers never see the title. Both initializer arguments are required. Register values through `paywalls` so the supported input limits apply.
