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

# Mobile apps

> Connect a ChatGPT account from inside a native app with the Verso SDK for iOS.

The hosted connect page works in any browser, but on a phone it shows a remote browser: the device's keyboard and password manager cannot reach it and the rendering is not native. Native apps use the Verso SDK instead. The provider's login opens in the app's own WebView, with the system browser's behaviour (native keyboard, autofill, Google sign-in), and the captured session goes to Verso. Everything else is unchanged: the same signed link, the same webhooks, the same API.

iOS and Android are available today. React Native and Flutter wrappers are next.

## iOS

Requirements: iOS 15+, Swift 5.9+, Xcode 15+. Source and issues: [github.com/vrsoai/verso-connect-ios](https://github.com/vrsoai/verso-connect-ios).

### Install

Swift Package Manager. In Xcode: File › Add Package Dependencies, enter `https://github.com/vrsoai/verso-connect-ios`, dependency rule "Up to Next Major Version" from `0.1.0`. Or in `Package.swift`:

```swift theme={null}
dependencies: [
    .package(url: "https://github.com/vrsoai/verso-connect-ios", from: "0.1.0"),
],
targets: [
    .target(name: "YourApp", dependencies: ["VersoConnect"]),
]
```

### Use

Your backend signs a connect link with `signLink()` exactly as for the web (`purpose: "connect"`) and returns its `url` to the app. The app secret never ships in the app. Collect the user's consent in your own UI before this point, as with the hosted page.

```swift theme={null}
import VersoConnect

// From a UIViewController, after fetching `link` from your backend
do {
    let connection = try await VersoConnect.present(link: link, from: self)
    // connection.connectionId; your backend also receives connection.created
} catch VersoConnectError.cancelled {
    // the user closed the sheet
} catch {
    // see Errors below
}
```

A completion-handler form, `VersoConnect.present(link:from:completion:)`, exists for code that is not async.

`present` opens a sheet with the provider's login, waits for the user to log in, captures the session and dismisses the sheet itself in every case. It returns the `connectionId`; your backend receives `connection.created` with the user's `userRef` at the same time, exactly as with the hosted page, and the first sync starts within a minute.

The link is single use and valid 15 minutes: fetch a fresh one each time the user taps your button. The login itself must complete within one hour.

### Errors

| `VersoConnectError` | Meaning |
| - | - |
| `invalidLink` | The URL carries no token. |
| `cancelled` | The user closed the sheet. |
| `rejected(status:message:)` | Verso refused: an expired link (401), a link already used (403), or a ChatGPT account already connected by another user of your app (409, see [One account, one user](/guides/connect-flow#one-account-one-user)). Sign a new link. |
| `timedOut` | The provider session never became usable. |
| `network(Error)` | The request to Verso failed. Retry with a new link. |

## Android

Requirements: Android 7.0+ (API 24), Kotlin, AndroidX. Source and issues: [github.com/vrsoai/verso-connect-android](https://github.com/vrsoai/verso-connect-android).

### Install

The library is served by JitPack. Add the repository once, in `settings.gradle.kts`, then the dependency in your app module:

```kotlin theme={null}
// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven("https://jitpack.io")
    }
}
```

```kotlin theme={null}
// app/build.gradle.kts
dependencies {
    implementation("com.github.vrsoai:verso-connect-android:0.1.0")
}
```

### Use

Same contract as on iOS: your backend signs the link and returns its `url` to the app; the app secret never ships in the app; consent is collected in your UI first.

```kotlin theme={null}
import ai.tryverso.connect.VersoConnect
import ai.tryverso.connect.VersoConnectException

// From an Activity, after fetching `link` from your backend
VersoConnect.present(this, Uri.parse(link)) { result ->
    result.onSuccess { connection ->
        // connection.connectionId; your backend also receives connection.created
    }.onFailure { e ->
        when (e) {
            is VersoConnectException.Cancelled -> { /* the user closed the screen */ }
            is VersoConnectException.Rejected -> { /* e.status, e.message: sign a new link */ }
            else -> { /* network */ }
        }
    }
}
```

With the Activity Result API, register `VersoConnectContract()` and `launch(Uri.parse(link))`; the result is the same `Result<VersoConnection>`.

The screen closes itself in every case and the callback runs once, on the main thread. The link is single use and valid 15 minutes; the login itself must complete within one hour.

### Errors

| `VersoConnectException` | Meaning |
| - | - |
| `InvalidLink` | The URL carries no token. |
| `Cancelled` | The user closed the screen. |
| `Rejected(status, message)` | Verso refused: an expired link (401), a link already used (403), or a ChatGPT account already connected by another user of your app (409). Sign a new link. |
| `TimedOut` | The provider session never became usable. |
| `Network(cause)` | The request to Verso failed. Retry with a new link. |

## What the SDK does

1. Sends the link's token to `POST /api/connect-native` with `action: "start"`. Verso verifies and consumes the link, creates the user record if needed, sends `connect.started`, and answers with the provider's login URL, the name of the session cookie to watch, and a one-time capture credential.
2. Opens the provider's login in the platform's WebView presenting itself as the system browser (a non-persistent `WKWebView` with the Mobile Safari user agent on iOS, a `WebView` with the Chrome user agent and cleared storage on Android), so the provider treats it as a browser. Nothing remains on the device afterwards.
3. Watches the WebView's cookies. Once the session cookie is present and the session is usable, sends it to Verso with `action: "capture"`. Verso encrypts the credential with a key that exists only for this connection, creates the connection, folds a previous connection of the same account into it ([Reconnecting](/guides/connect-flow#reconnecting)), refuses an account already connected by another user of your app, sends `connection.created` and starts the first sync.

The SDK never sees your app secret or API key, and the session credential is sent once, over TLS, to Verso only. The endpoint is documented in the [API reference](/api-reference/connect-native) for teams building on a platform without an SDK yet.

## React Native, Flutter

Wrappers over the two native SDKs are in progress. Until then, call the native SDK from your bridge, or open the hosted connect link in the system browser; see [On mobile](/guides/connect-flow#on-mobile) in the connect flow guide.
