# How AppFlowy Handles Deep Links for Login, Payment, and Invitation Callbacks

> Learn how AppFlowy integrates a flexible deep link framework for login, payment, and invitation callbacks. Discover its plug-in architecture and handler registry.

- Repository: [AppFlowy-IO/AppFlowy](https://github.com/AppFlowy-IO/AppFlowy)
- Tags: internals
- Published: 2026-03-03

---

**AppFlowy implements a plug-in-style deep link framework using an abstract `DeepLinkHandler` class, a singleton `DeepLinkHandlerRegistry`, and platform-specific listeners to route login, payment, and invitation URIs to specialized handlers.**

AppFlowy leverages a modular deep link architecture to manage external callbacks for authentication, billing, and workspace invitations. According to the AppFlowy-IO/AppFlowy source code, the system delegates URI processing to discrete handlers registered during application startup, enabling clean separation between deep link detection and business logic.

## Core Architecture Components

Three fundamental building blocks power AppFlowy's deep link handling:

- **`DeepLinkHandler<T>`**: An abstract base class defined in `frontend/appflowy_flutter/lib/startup/tasks/deeplink/deeplink_handler.dart` that declares `canHandle(Uri)` and `handle(...)` methods. Each concrete implementation decides whether it can process a URI and executes the associated logic.

- **`DeepLinkHandlerRegistry`**: A singleton registry that maintains a list of all active handlers. When `processDeepLink` is invoked, the registry iterates through registered handlers and dispatches the URI to the first handler returning `true` from `canHandle`.

- **`AppFlowyCloudDeepLink`**: The bootstrap component in `frontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dart` that initializes system-wide listeners and forwards incoming URIs to the registry. It also exposes a `ValueNotifier` so the UI can react to loading states.

## Deep Link Registration and Listening

### Handler Registration During Startup

During application initialization, the `AppFlowyCloudDeepLink` constructor instantiates the registry and registers concrete handlers for each supported flow:

```dart
_deepLinkHandlerRegistry = DeepLinkHandlerRegistry.instance
  ..register(LoginDeepLinkHandler())
  ..register(PaymentDeepLinkHandler())
  ..register(InvitationDeepLinkHandler())
  ..register(ExpireLoginDeepLinkHandler())
  ..register(OpenAppDeepLinkHandler());

```

This registration pattern appears in `frontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dart`, ensuring all handlers are available before the first URI arrives.

### Platform-Specific URI Listening

The system adapts to platform conventions for intercepting external links:

- **Mobile**: Uses the `AppLinks` library to emit a `Uri?` for every custom-scheme launch.
- **Windows**: Registers the `appflowy-flutter://` protocol via `registerProtocolHandler(appflowyDeepLinkSchema)`.

The listener forwards captured URIs to `_handleUri`, which delegates processing to the registry:

```dart
await _deepLinkHandlerRegistry.processDeepLink(
  uri: uri,
  onStateChange: ...,
  onResult: ...,
  onError: ...,
);

```

## Concrete Deep Link Handlers

Each specialized handler resides in `frontend/appflowy_flutter/lib/startup/tasks/deeplink/` and implements specific business logic:

**`LoginDeepLinkHandler`** (`login_deeplink_handler.dart`)
Detects URIs where `uri.fragment` contains `access_token` (e.g., `appflowy-flutter://login-callback#access_token=...`). Upon handling, it dispatches a `UserEventOauthSignIn` to the backend, updates the loading state, and returns the signed-in `UserProfilePB`.

**`InvitationDeepLinkHandler`** (`invitation_deeplink_handler.dart`)
Matches hosts equal to `invitation-callback` with query parameters `workspace_id` and `email`. It extracts these values and pushes a `WorkspaceNotifyValue` to `openWorkspaceNotifier`, triggering the UI to open the invited workspace.

**`PaymentDeepLinkHandler`** (`payment_deeplink_handler.dart`)
Handles `appflowy-flutter://payment-success?...` URIs by reading the `plan` query parameter and notifying `SubscriptionSuccessListenable` that the purchase succeeded.

**`OpenAppDeepLinkHandler`** (`open_app_deeplink_handler.dart`)
Serves as a fallback for any `appflowy-flutter://` URI that does not match specific handlers. It invokes `runAppFlowy()` to bring the application to the foreground without additional processing.

**`ExpireLoginDeepLinkHandler`** (`expire_login_deeplink_handler.dart`)
Processes token-expiry callbacks and surfaces error toasts to inform users of authentication failures.

## State Management and UI Feedback

The framework propagates execution progress through typed callbacks:

- **`DeepLinkState`**: An enum with values `none`, `loading`, `finish`, and `error` emitted via `onStateChange`.
- **ValueNotifier**: `AppFlowyCloudDeepLink` exposes `subscribeDeepLinkLoadingState`, which UI components use to display loading indicators during login flows (the only flow requiring explicit UI feedback).

Final results arrive through `onResult` (success or failure for recognized handlers) or `onError` (for malformed or unknown URIs).

## Implementing Custom Deep Link Handlers

Developers can extend the system by subclassing `DeepLinkHandler`:

```dart
class MyCustomHandler extends DeepLinkHandler<void> {
  @override
  bool canHandle(Uri uri) => uri.host == 'my-custom';

  @override
  Future<FlowyResult<void, FlowyError>> handle({
    required Uri uri,
    required DeepLinkStateHandler onStateChange,
  }) async {
    // Custom logic here
    return FlowyResult.success(null);
  }
}

// Registration during startup
DeepLinkHandlerRegistry.instance.register(MyCustomHandler());

```

## Summary

- AppFlowy uses a **registry pattern** (`DeepLinkHandlerRegistry`) to route URIs to the first capable handler.
- **Five concrete handlers** manage login, payment, invitation, token expiry, and generic app-opening deep links.
- Platform listeners in `AppFlowyCloudDeepLink` capture URIs on mobile and Windows, forwarding them to `processDeepLink`.
- State changes propagate through callbacks and `ValueNotifier` instances, enabling UI feedback during asynchronous operations like OAuth sign-in.
- Unit tests in `frontend/appflowy_flutter/test/unit_test/deeplink/deeplink_test.dart` verify handler routing and error handling.
- The architecture supports **extensibility** via the abstract `DeepLinkHandler` base class.

## Frequently Asked Questions

### How does AppFlowy distinguish between different types of deep links?

Each handler implements `canHandle(Uri)` with specific matching logic. For example, `LoginDeepLinkHandler` checks for `access_token` in the fragment, while `PaymentDeepLinkHandler` verifies the host equals `payment-success`. The registry iterates through registered handlers in order and selects the first returning `true`.

### Where is the deep link listening logic implemented for Windows and mobile platforms?

The `AppFlowyCloudDeepLink` class in `frontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dart` initializes platform-specific listeners. It uses the `AppLinks` library for mobile and `url_protocol` with `registerProtocolHandler(appflowyDeepLinkSchema)` for Windows to capture `appflowy-flutter://` scheme launches.

### Can I add support for custom deep link schemes in AppFlowy?

Yes. Create a class extending `DeepLinkHandler<T>` in `frontend/appflowy_flutter/lib/startup/tasks/deeplink/`, implement `canHandle` to match your URI pattern, and register the instance via `DeepLinkHandlerRegistry.instance.register()` during application startup.

### How does the UI know when a login deep link is being processed?

The `AppFlowyCloudDeepLink` class exposes `subscribeDeepLinkLoadingState`, a `ValueNotifier` that emits `DeepLinkResult` objects containing the current state (`loading`, `finish`, or `error`). UI widgets listen to this notifier to display progress indicators or error messages during the OAuth handshake.