# Quickstart

> Integrate Grovs end to end in about 30 minutes.

Source: https://docs.grovs.io/docs/getting-started/quickstart

Create a project, install and initialize the SDK, handle a deep link, and test it with a real link from the dashboard.

> **Note:** Using a coding agent? [Build with AI](https://docs.grovs.io/docs/ai#let-an-agent-integrate-the-sdk) has a prompt per platform that does steps 2 to 4 for you. New to Grovs? Read the [Introduction](https://docs.grovs.io/docs/getting-started/introduction).

## Guided setup in the dashboard

Your first project opens a four-step guided setup that follows this page. It saves progress: reopen it from **Finish setup** under the project switcher.

| Step                 | What you do                                                                                               | How it's checked                                                                                       |
| -------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| **Link domain**      | Pick a Grovs subdomain, which is ready right away, or use your own domain (paid plans).                   | Saved when you continue.                                                                               |
| **Connect your app** | For each platform (iOS, Android, Web), register the app, add the SDK and initialize it, then run the app. | Live: see below.                                                                                       |
| **Links**            | Choose where links go on each platform when the app isn't installed, and the share preview.               | Done once a fallback is set.                                                                           |
| **Try it**           | Create your first link, scan its QR code with your phone, and open it.                                    | Done once the link exists. It also shows **Opened on your phone**, live, once the link opens your app. |

If someone else writes the code, **Send to your developer** invites them with a link to the connect step.

### Is my SDK connected?

On **Run your app**, the last part of **Connect your app**, the dashboard shows **Waiting for your app to open**. It refreshes every few seconds until the SDK reports in, then shows **Connected** and how long ago.

| Platform     | Connected when                                                                                                                                |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| iOS, Android | A device running the SDK was seen in the last 30 days. The app must start with the SDK configured; opening a link in a browser doesn't count. |
| Web          | The Web SDK authenticated from one of your registered domains.                                                                                |

The check covers production and test, so a debug build with `useTestEnvironment: true` counts. Once done, the step stays done.

If it keeps waiting:

1. Check the API key is the project's key, and the environment flag matches the build.
2. Turn on debug logging (`Grovs.setDebug(level: .info)` on iOS) and look for an authentication error.
3. On the web, check the page's origin is registered exactly, including the port on `localhost`. See [Registering domains](https://docs.grovs.io/docs/how-to-guides/web/register-domains).
4. See [SDK configuration issues](https://docs.grovs.io/docs/troubleshooting/sdk-configuration-issues).

The **Try it** check uses the newest open of that link: an app open, install, reinstall or reactivation through it on a device running the SDK.

### 1. Create a project in the Grovs dashboard

Sign up at [app.grovs.io](https://app.grovs.io) and create a project. The [guided setup](#guided-setup-in-the-dashboard) starts on its own for your first project.

Copy your key from **API Key** in the dashboard sidebar. Each environment (test, production) has its own key: copy the one you build against.

Then set your [redirect rules, domain and social preview](https://docs.grovs.io/docs/getting-started/project-setup). Redirect rules need a **fallback URL** before they save.

### 2. Install the SDK

**iOS**

**Swift Package Manager**

1. In Xcode, go to **File → Add Package Dependencies…**
2. Enter the repository URL: `https://github.com/grovs-io/grovs-iOS.git`
3. Choose **Up to Next Major Version** starting at `3.1.0`
4. Click **Next**, then **Finish**

**CocoaPods**

Add to your `Podfile`, then run `pod install`:

```ruby
pod 'Grovs', '~> 3.1'
```

**Android**

Add the dependency to your app-level `build.gradle`:

```groovy
dependencies {
    implementation("io.grovs:Grovs:3.1.0")
}
```

**Flutter**

Add to `pubspec.yaml`, then run `flutter pub get`:

```yaml
dependencies:
  grovs_flutter_plugin: ^3.1.1
```

**React Native**

Install the wrapper package:

```bash
# Using npm
npm install react-native-grovs-wrapper

# Using yarn
yarn add react-native-grovs-wrapper
```

In a bare React Native project, add the Grovs Android SDK to `android/app/build.gradle` at the wrapper's version. The Expo config plugin does this for you:

```groovy
dependencies {
    implementation 'io.grovs:Grovs:3.1.0'
}
```

`pod install` adds the iOS SDK.

**Web**

```bash
npm install grovs --save
```

### 3. Initialize the SDK

**iOS**

Initialize the SDK in your `AppDelegate`:

```swift title="AppDelegate.swift" {4}
import Grovs

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Grovs.configure(APIKey: "your-api-key", useTestEnvironment: false, delegate: self) { success in
        if success {
            print("Grovs SDK is ready")
        }
    }

    return true
}
```

Without a `SceneDelegate`, forward these calls:

```swift title="AppDelegate.swift"
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    return Grovs.handleAppDelegate(continue: userActivity, restorationHandler: restorationHandler)
}

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    return Grovs.handleAppDelegate(open: url, options: options)
}
```

`SceneDelegate` apps forward different calls. See the [iOS Quick Start](https://docs.grovs.io/docs/sdk/ios/quick-start) for that variant, debug logging and user identity.

**Android**

Initialize Grovs in your `Application` class:

```kotlin title="MyApplication.kt" {4}
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Grovs.configure(this, "your-api-key", useTestEnvironment = false)
    }
}
```

Forward lifecycle events in your **launcher activity**:

```kotlin title="MainActivity.kt"
class MainActivity : AppCompatActivity() {
    override fun onStart() {
        super.onStart()
        Grovs.onStart(this)
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        Grovs.onNewIntent(intent, this)
    }
}
```

Links also need intent filters on the launcher activity. Copy them from the [Android Quick Start](https://docs.grovs.io/docs/sdk/android/quick-start).

**Flutter**

Flutter is configured in native files; there's no `configure` call in Dart. On Android, add the key and environment inside `<application>` in `AndroidManifest.xml`:

```xml title="AndroidManifest.xml"
<application>
    <meta-data
        android:name="grovs_api_key"
        android:value="YOUR_API_KEY" />
    <meta-data
        android:name="grovs_use_test_environment"
        android:value="true" />
</application>
```

On iOS, add them to `Info.plist`:

```xml title="Info.plist"
<key>GrovsApiKey</key>
<string>YOUR_API_KEY</string>
<key>GrovsUseTestEnvironment</key>
<true/>
```

Optional keys: a self-hosted base URL (`GrovsBaseURL` / `grovs_base_url`), extra clipboard link domains (`GrovsClipboardDomains` / `grovs_clipboard_domains`), and starting disabled until consent (`GrovsEnabled` / `grovs_enabled`). Set `FlutterDeepLinkingEnabled` (Info.plist) and `flutter_deeplinking_enabled` (Android manifest) to `false`, or Flutter's own deep linking also handles Grovs links. You also need Android intent filters, an iOS URL scheme and Associated Domains. See the [Flutter Quick Start](https://docs.grovs.io/docs/sdk/flutter/quick-start).

**React Native**

On Android, initialize in `MainApplication`:

```kotlin
import io.grovs.Grovs
import com.grovswrapper.GrovsConsent

override fun onCreate() {
    super.onCreate()
    Grovs.configure(
        this, "your-api-key", useTestEnvironment = false, baseURL = null,
        autoTrackScreenViews = false, clipboardDomains = null,
        enabled = GrovsConsent.isEnabled(this)
    )
}
```

On iOS, initialize in `AppDelegate.swift`:

```swift
import Grovs
import react_native_grovs_wrapper

override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Grovs.configure(
        APIKey: "your-api-key", useTestEnvironment: false,
        autoTrackScreenViews: false, clipboardDomains: nil,
        enabled: GrovsWrapperSwift.isSDKEnabled(), delegate: nil
    )
    Grovs.setDebug(level: .info)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
```

- Pass `nil` as the delegate. The wrapper registers itself and forwards links to your JS listener.
- Keep `autoTrackScreenViews` off. The native trackers only see the React Native host screen.
- Pass the wrapper's stored consent value as `enabled`, so `setSDK(false)` survives a cold start.

Both platforms need more native wiring: link forwarding, intent filters, Associated Domains. See the [React Native Quick Start](https://docs.grovs.io/docs/sdk/react-native/quick-start). With Expo and a development build, the config plugin automates it (Swift and Kotlin templates only).

**Web**

Import and configure the SDK:

```javascript
import Grovs from 'grovs';

await Grovs.configure({
    apiKey: 'your-api-key',
    testEnvironment: false,
    onDeeplink: (payload) => {
        // Called when the page was opened from a Grovs link
        console.log('Link payload:', payload);
    },
});
```

> **Warning:** Match `useTestEnvironment` (`testEnvironment` on the web) to the key: **test key → `true`**, **production key → `false`**. A link from the other environment won't resolve its payload.

### 4. Handle a deep link

Register a handler. The SDK passes it the link and its payload whenever a Grovs link opens your app.

**iOS**

Conform to `GrovsDelegate`:

```swift
class YourViewController: UIViewController, GrovsDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        Grovs.delegate = self
    }

    func grovsReceivedPayloadFromDeeplink(link: String?, payload: [String: Any]?, tracking: [String: Any]?) {
        // Route the user based on payload data
        if let screen = payload?["screen"] as? String {
            navigateTo(screen)
        }
    }
}
```

More options (past payloads, link details) in [iOS Deep Linking](https://docs.grovs.io/docs/sdk/ios/deep-linking).

**Android**

Register a listener in your launcher activity:

```kotlin
Grovs.setOnDeeplinkReceivedListener(this) { deeplinkDetails ->
    val link = deeplinkDetails.link
    val payload = deeplinkDetails.data
    val tracking = deeplinkDetails.tracking

    Log.d("Grovs", "Opened from: $link, payload: $payload")
}
```

More options (Kotlin Flow, link details) in [Android Deep Linking](https://docs.grovs.io/docs/sdk/android/deep-linking).

**Flutter**

Subscribe to `onDeeplinkReceived` in a widget that stays mounted, such as your root widget. Links that arrive earlier, including the launch link, are buffered for the first listener:

```dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:grovs_flutter_plugin/grovs.dart';
import 'package:grovs_flutter_plugin/models/grovs_link.dart';

class _AppState extends State<App> {
  final _grovs = Grovs();
  StreamSubscription<DeeplinkDetails>? _subscription;

  @override
  void initState() {
    super.initState();
    _subscription = _grovs.onDeeplinkReceived.listen((details) {
      final link = details.link;
      final payload = details.data;

      debugPrint('Opened from: $link, payload: $payload');

      // Route the user based on payload
      if (payload?['screen'] == 'product') {
        navigateToProduct(payload?['productId']);
      }
    });
  }

  @override
  void dispose() {
    _subscription?.cancel();
    super.dispose();
  }
}
```

More details in [Flutter Deep Linking](https://docs.grovs.io/docs/sdk/flutter/deep-linking).

**React Native**

Use the `onDeeplinkReceived` callback:

```typescript
import Grovs from 'react-native-grovs-wrapper';

const listener = Grovs.onDeeplinkReceived((response) => {
    console.log('Link:', response.link);
    console.log('Data:', response.data);

    // Route the user based on payload
    if (response.data?.screen === 'product') {
        navigation.navigate('Product', { id: response.data.productId });
    }
});

// When you no longer need the listener
listener.remove();
```

Set it up early, in your root component or app entry point. See [React Native Deep Linking](https://docs.grovs.io/docs/sdk/react-native/deep-linking).

**Web**

The `onDeeplink` callback you passed to `configure` receives the link's payload:

```javascript
await Grovs.configure({
    apiKey: 'your-api-key',
    onDeeplink: (payload) => {
        if (payload.screen === 'product_detail') {
            showProduct(payload.productId);
        }
    },
});
```

More details in [Web Deep Linking](https://docs.grovs.io/docs/sdk/web/deep-linking).

### 5. Create your first link and test

1. Select the environment (Production or Test) in the dashboard.
2. Go to **Links → Dynamic Links** and click **Add Link**. In the guided setup, the **Try it** step makes this link and shows its QR code.
3. Enter a **custom path** (for example `/summer-sale`) and a **Link Name** for internal use.
4. Add key-value pairs under **Data**, for example `screen: product` or `coupon: 25OFF`.
5. Click **Create**.

Link types, social previews, redirect overrides and tracking parameters are in [How to Create a Link](https://docs.grovs.io/docs/links/create-link).

Open the link on a device:

| Device            | Result                                                                                                                             |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| App installed     | The app opens and your handler receives the link and payload.                                                                      |
| App not installed | The store or fallback URL, per your redirect rules. After install, the payload still reaches your handler (deferred deep linking). |
| Desktop           | By default, a Grovs page with a QR code and download links.                                                                        |

## Where to next

- **Full platform guides:** [iOS](https://docs.grovs.io/docs/sdk/ios), [Android](https://docs.grovs.io/docs/sdk/android), [Flutter](https://docs.grovs.io/docs/sdk/flutter), [React Native](https://docs.grovs.io/docs/sdk/react-native), [Unity](https://docs.grovs.io/docs/sdk/unity), [Web](https://docs.grovs.io/docs/sdk/web)
- **How-to guides:** [setup values and configuration](https://docs.grovs.io/docs/how-to-guides)
- **Links:** [Create a link](https://docs.grovs.io/docs/links/create-link), [Project setup](https://docs.grovs.io/docs/getting-started/project-setup), [Smart banners](https://docs.grovs.io/docs/links/smart-banners)
- **Engagement:** [Push campaigns](https://docs.grovs.io/docs/engagement/push-campaigns) and [in-app messages](https://docs.grovs.io/docs/engagement/in-app-messages)
- **Build with AI:** [Agent prompts and the MCP server](https://docs.grovs.io/docs/ai)
- **REST API:** [Generate and manage links server-side](https://docs.grovs.io/docs/api-reference)
