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

# Integrate Mellowtel in your Windows Desktop App

Integrate Mellowtel into your Windows desktop application to allow users to share their unused internet bandwidth in exchange for rewards or premium features.

<Warning>
  **User consent is mandatory.** The SDK throws an `InvalidOperationException` if you call `StartAsync()` without the user having opted in.
</Warning>

## Prerequisites

* A Mellowtel account and **Integration ID** from the [dashboard](https://www.mellowtel.com/mellowtel-auth/). Each desktop app gets its own unique Integration ID.
* .NET 10 SDK
* Windows 10 or Windows 11
* A .NET desktop application (Console, WPF, or Windows Forms)

<Note>
  Want to see a complete, real-world integration before wiring this into your own app? The [mellowtel-pomodoro-windows](https://github.com/mellowtel-inc/mellowtel-pomodoro-windows) WPF sample shows the full flow end-to-end: consent dialog, settings toggle, and background lifecycle.
</Note>

***

## Installation

Mellowtel for Windows ships on NuGet as [`Mellowtel.Win`](https://www.nuget.org/packages/Mellowtel.Win).

### 1. Install the Package

From your project directory:

```bash theme={null}
dotnet add package Mellowtel.Win
```

Or add a `PackageReference` to your `.csproj`:

```xml theme={null}
<PackageReference Include="Mellowtel.Win" Version="1.0.0" />
```

### 2. Add to Your Code

```csharp theme={null}
using MellowtelWin;

// Initialize Mellowtel with your Integration ID
var mellowtel = new Mellowtel("YOUR_INTEGRATION_ID", new MellowtelOptions
{
    PluginId = "your-app-id"
});

// If the user has already opted in, resume silently.
// If not, this is a no-op and returns false.
await mellowtel.StartIfOptedInAsync();

// First-time flow: show your consent UI, then opt in and start.
if (!mellowtel.GetOptInStatus() && ShowConsentDialog())
{
    mellowtel.OptIn();
    await mellowtel.StartAsync();
}

// On app shutdown
await mellowtel.StopAsync();
mellowtel.Dispose();
```

<Note>
  Replace `YOUR_INTEGRATION_ID` with the Integration ID from your Mellowtel dashboard, and set `PluginId` to a stable identifier for your app. `ShowConsentDialog()` is your own UI that returns `true` only when the user explicitly agrees. See [User Consent](#user-consent) below.
</Note>

### Configuration options

`MellowtelOptions` lets you tune a few things:

| Property       | Type     | Default           | Description                                                   |
| -------------- | -------- | ----------------- | ------------------------------------------------------------- |
| `PluginId`     | `string` | `"mellowtel-win"` | Stable identifier for your app. Set this to something unique. |
| `MaxDailyRate` | `int`    | Built-in default  | Max requests the SDK will handle per day.                     |
| `DisableLogs`  | `bool`   | `true`            | Set to `false` to enable SDK logs during development.         |

***

## Examples by Application Type

<Tabs>
  <Tab title="Console App">
    ```csharp theme={null}
    using MellowtelWin;

    class Program
    {
        static async Task Main(string[] args)
        {
            using var mellowtel = new Mellowtel("YOUR_INTEGRATION_ID", new MellowtelOptions
            {
                PluginId = "your-app-id"
            });

            if (!mellowtel.GetOptInStatus())
            {
                Console.WriteLine("User must opt in before using this service.");
                return;
            }

            using var cts = new CancellationTokenSource();
            Console.CancelKeyPress += (s, e) => { e.Cancel = true; cts.Cancel(); };

            try
            {
                await mellowtel.StartAsync(cts.Token);
                Console.WriteLine("Mellowtel running. Press Ctrl+C to stop.");
                await Task.Delay(Timeout.Infinite, cts.Token);
            }
            catch (InvalidOperationException ex)
            {
                Console.WriteLine($"Cannot start: {ex.Message}");
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("Stopping...");
            }
            finally
            {
                await mellowtel.StopAsync();
            }
        }
    }
    ```
  </Tab>

  <Tab title="WPF App">
    Add to your `MainWindow.xaml.cs`:

    ```csharp theme={null}
    using MellowtelWin;
    using System.Windows;

    public partial class MainWindow : Window
    {
        private Mellowtel? _mellowtel;

        public MainWindow()
        {
            InitializeComponent();
            Loaded += Window_Loaded;
        }

        private async void Window_Loaded(object sender, RoutedEventArgs e)
        {
            _mellowtel = new Mellowtel("YOUR_INTEGRATION_ID", new MellowtelOptions
            {
                PluginId = "your-app-id"
            });

            _mellowtel.ConnectionStateChanged += (s, connected) =>
            {
                Dispatcher.Invoke(() =>
                {
                    StatusTextBlock.Text = connected ? "Connected" : "Disconnected";
                });
            };

            // Silently resume if already opted in. Otherwise show your consent UI.
            var started = await _mellowtel.StartIfOptedInAsync();
            if (!started)
            {
                // Show your consent dialog here
            }
        }

        protected override async void OnClosing(CancelEventArgs e)
        {
            if (_mellowtel != null)
            {
                await _mellowtel.StopAsync();
                _mellowtel.Dispose();
            }
            base.OnClosing(e);
        }
    }
    ```
  </Tab>

  <Tab title="Windows Forms">
    Add to your `MainForm.cs`:

    ```csharp theme={null}
    using MellowtelWin;

    public partial class MainForm : Form
    {
        private Mellowtel? _mellowtel;

        public MainForm()
        {
            InitializeComponent();
            Load += MainForm_Load;
            FormClosing += MainForm_FormClosing;
        }

        private async void MainForm_Load(object sender, EventArgs e)
        {
            _mellowtel = new Mellowtel("YOUR_INTEGRATION_ID", new MellowtelOptions
            {
                PluginId = "your-app-id"
            });

            _mellowtel.ConnectionStateChanged += (s, connected) =>
            {
                if (InvokeRequired)
                    Invoke(() => statusLabel.Text = connected ? "Connected" : "Disconnected");
                else
                    statusLabel.Text = connected ? "Connected" : "Disconnected";
            };

            // Silently resume if already opted in. Otherwise show your consent UI.
            var started = await _mellowtel.StartIfOptedInAsync();
            if (!started)
            {
                // Show your consent dialog here
            }
        }

        private async void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_mellowtel != null)
            {
                await _mellowtel.StopAsync();
                _mellowtel.Dispose();
            }
        }
    }
    ```
  </Tab>
</Tabs>

### Observing connection state

The `Mellowtel` instance exposes a `ConnectionStateChanged` event that fires whenever the underlying WebSocket to Mellowtel's backend connects or disconnects. The WPF and Windows Forms examples above subscribe to it to drive a status indicator. The payload is a `bool` where `true` means connected and `false` means disconnected.

The event fires on a background thread, so UI frameworks that enforce thread affinity must marshal updates onto the UI thread. In WPF use `Dispatcher.Invoke`, and in Windows Forms use `Control.Invoke` (both shown in the examples above).

<Note>
  **About the WPF `async void OnClosing` pattern.** The WPF example above uses `protected override async void OnClosing`, which is the simplest form but has a subtle issue: `base.OnClosing(e)` is called synchronously while `StopAsync()` is still awaiting, so the window can close before cleanup finishes. For most apps this is fine because the process exits immediately afterwards. If you need a guaranteed graceful shutdown (for example, to flush telemetry), follow the [Microsoft-documented deferral pattern](https://learn.microsoft.com/en-us/dotnet/api/system.windows.window.closing) where you set `e.Cancel = true`, `await` your async work, and then close the window explicitly.
</Note>

***

## User Consent

<Warning>
  Displaying a consent dialog is **mandatory**. The SDK enforces this: `StartAsync()` throws an `InvalidOperationException` if the user has not opted in.
</Warning>

```csharp theme={null}
var mellowtel = new Mellowtel("YOUR_INTEGRATION_ID", new MellowtelOptions
{
    PluginId = "your-app-id"
});

if (!mellowtel.GetOptInStatus())
{
    var userAgreed = ShowConsentDialog(); // your custom dialog

    if (userAgreed)
        mellowtel.OptIn();
    else
        return; // user declined, do not start
}

try
{
    await mellowtel.StartAsync();
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}
```

### What your consent dialog must include

<Steps>
  <Step title="Explain what Mellowtel does">
    Use plain language. Example: *"This app uses Mellowtel to share your unused internet bandwidth. This allows you to \[benefit/feature]. You can opt out at any time in settings."*
  </Step>

  <Step title="Give users a clear choice">
    Include distinct Accept and Decline options.
  </Step>

  <Step title="Link to policies">
    Include links to the [Terms of Service](https://www.mellowtel.com/terms-and-conditions) and [Privacy Policy](https://www.mellowtel.com/privacy-policy).
  </Step>
</Steps>

### Let users change their consent in settings

```csharp theme={null}
// Check current status
bool isOptedIn = mellowtel.GetOptInStatus();

// Opt out
if (userWantsToOptOut)
{
    mellowtel.OptOut();
    await mellowtel.StopAsync();
}

// Opt back in
if (userWantsToOptIn)
{
    mellowtel.OptIn();
    await mellowtel.StartAsync();
}
```

For richer UI (for example, showing when the user first opted in), `GetOptInDetails()` returns the opt-in status alongside the opt-in and opt-out timestamps:

```csharp theme={null}
var (isOptedIn, optInDate, optOutDate) = mellowtel.GetOptInDetails();
```

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="&#x22;Package not found&#x22; error">
    1. Make sure your project targets **.NET 10** or later. `Mellowtel.Win` requires `net10.0`.
    2. Clear the NuGet cache and restore:

    ```bash theme={null}
    dotnet nuget locals all --clear
    dotnet restore
    ```
  </Accordion>

  <Accordion title="&#x22;InvalidOperationException: User has not opted in&#x22;">
    `StartAsync()` requires explicit consent. Call `OptIn()` after your consent dialog returns a positive result, then call `StartAsync()`. For silent resume on subsequent launches, use `StartIfOptedInAsync()` instead, which is a no-op when the user has not opted in.
  </Accordion>
</AccordionGroup>

***

Estimated time to complete: 10-15 minutes.

If you need help or have feedback, contact us at [info@mellowtel.com](mailto:info@mellowtel.com) or join our [Discord community](https://discord.com/invite/txAZp4MSDe).
