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

# Get Started with Chappie SDK in 10 Minutes

> Add Chappie SDK to your app, sign the user in with their ChatGPT account, create a client with a harness, and send your first message in minutes.

This guide walks you from a blank iOS app to a working ChatGPT-powered assistant in five steps. You will add the package, wire up authentication, create a client with a harness, send a message, and handle the most common error conditions. By the end, your app will be having real conversations through the user's own ChatGPT account.

<Steps>
  <Step title="Add the package">
    Open your Xcode project, go to **File → Add Package Dependencies…**, and enter the repository URL:

    ```
    https://github.com/b-nnett/chappie-sdk.git
    ```

    Select **Up to Next Major Version** starting from `0.1.0`, then add the **ChappieSDK** product to your app target. For full installation options including the Swift Package Manager manifest approach, see the [Installation](/installation) guide.
  </Step>

  <Step title="Sign the user in">
    Chappie authenticates users through their own ChatGPT account using an OAuth device-code flow. Create a shared `ChappieAuthSession` at the app level with `Chappie.authSession()`, then show `ChappieSignIn` when the user is not yet signed in. The SDK handles the onboarding sheet, credential storage, and token refresh automatically.

    ```swift theme={null}
    import ChappieSDK
    import SwiftUI

    @main
    struct MyApp: App {
        @StateObject private var authSession = Chappie.authSession()

        var body: some Scene {
            WindowGroup {
                ContentView(authSession: authSession)
            }
        }
    }

    struct ContentView: View {
        @ObservedObject var authSession: ChappieAuthSession

        var body: some View {
            if authSession.state.isSignedIn {
                ChatView()
            } else {
                ChappieSignIn(authSession: authSession)
                    .buttonStyle(.borderedProminent)
                    .padding()
            }
        }
    }
    ```

    <Tip>
      `Chappie.authSession()` must be called on the main actor, which `@StateObject` initializers satisfy automatically. The session restores previously stored credentials from the Keychain on creation, so returning users are already signed in before they see the UI.
    </Tip>
  </Step>

  <Step title="Create a client with a harness">
    A `ChappieHarness` tells the model which app surface it is running in, what capabilities are available, and which tools it can call. Chappie prepends the harness manifest to every transcript, so the model always has the right context before the user's first message. Create your client with `Chappie.client(harness:)` once — typically as a stored property or environment value — and reuse it across turns.

    ```swift theme={null}
    import ChappieSDK

    let client = Chappie.client(harness: ChappieHarness(
        name: "My App Harness",
        summary: "Native iOS assistant for my app.",
        capabilities: ["multi-turn transcript", "streaming responses"]
    ))
    ```

    For quick experiments you can pass a string literal directly, or use the built-in default harness:

    ```swift theme={null}
    // Inline instruction harness
    let pirateClient = Chappie.client(harness: "You're a pirate. Speak like it. Don't ever stray from the seas, or ship-talk.")

    // SDK default harness
    let defaultClient = Chappie.client(harness: .default)
    ```

    <Note>
      Passing an empty string (`""`) disables Chappie's default guardrail entirely and sends no harness manifest. Use a structured `ChappieHarness` value whenever you need declared capabilities or host tools.
    </Note>
  </Step>

  <Step title="Send your first message">
    With a client in hand, you are ready to talk to the model. Use `client.send(_:)` for a single-turn reply, or `client.stream(_:)` to receive a token-by-token `AsyncThrowingStream` of `ChappieStreamEvent` values. Both methods update the client's active transcript automatically, so multi-turn conversations build naturally.

    <CodeGroup>
      ```swift One-shot send theme={null}
      import ChappieSDK

      // Returns the complete assistant reply as a String
      let reply = try await client.send("Summarize what this app does.")
      print(reply)
      ```

      ```swift Streaming response theme={null}
      import ChappieSDK

      // Emits events as the model writes
      for try await event in client.stream("Write a welcome message.") {
          switch event {
          case .textDelta(let text):
              print(text, terminator: "")
          case .usageLimits(let snapshot):
              print("\nUsage snapshot: \(snapshot)")
          case .completed:
              break
          case .toolCallRequested, .toolCallCompleted, .approvalRequested, .raw:
              break
          }
      }
      ```

      ```swift Stream with cancellation theme={null}
      import ChappieSDK

      // Use a ChappieStreamHandle when you need an explicit cancel surface
      let handle = client.streamHandle("Draft a reply.")

      Task {
          for try await event in handle.events {
              if case .textDelta(let text) = event {
                  print(text, terminator: "")
              }
          }
      }

      // Cancel from a button action or lifecycle event
      handle.cancel()
      ```
    </CodeGroup>

    <Tip>
      Streaming prefers a WebSocket connection and retries up to five times before falling back to HTTP/SSE. You can force SSE-only streaming by creating the client with `streamingTransportPolicy: .httpSSEOnly`.
    </Tip>
  </Step>

  <Step title="Handle usage limits and auth errors">
    Chappie surfaces usage-limit and credential errors as typed `ChappieClientError` cases so you can give users clear feedback rather than a generic failure. Catch `usageLimitReached` to show when the user's plan resets, and catch `missingCredential` to redirect them back to the sign-in flow.

    ```swift theme={null}
    import ChappieSDK

    do {
        let reply = try await client.send("Hello")
        print(reply)
    } catch ChappieClientError.usageLimitReached(let limit) {
        // The user has hit their ChatGPT plan's message limit
        print("Plan: \(limit.plan?.displayName ?? "Unknown")")
        print("Resets at: \(limit.resetsAt as Any)")
    } catch ChappieClientError.missingCredential {
        // The user needs to sign in — route them back to ChappieSignIn
    } catch {
        print("Unexpected error: \(error)")
    }
    ```

    <Note>
      Chappie automatically backs off on `429` responses, respecting `Retry-After` when present and using bounded exponential backoff otherwise. The default policy retries before surfacing `usageLimitReached`. You can tune or disable this through `ChappieRateLimitBackoffPolicy` on the client.
    </Note>
  </Step>
</Steps>

## What's Next

Now that you have a working assistant in your app, explore authentication configuration and harness capabilities in depth.

<CardGroup cols={2}>
  <Card title="Authentication Overview" icon="key" href="/authentication/overview">
    Learn how device-code sign-in works, how to support multi-account apps with custom Keychain configurations, and how to handle manual device-code flows and auth state transitions.
  </Card>

  <Card title="Harnesses" icon="layer-group" href="/concepts/harnesses">
    Declare your app's capabilities, register executable host tools with Swift handlers, use sample harnesses for common surfaces, and bundle reusable harnesses as plugin manifests.
  </Card>
</CardGroup>
