Skip to main content
The bondify_flutter package brings Bondify’s one-tap Telegram authentication to iOS and Android. Add the PulseButton widget to your widget tree, supply your project ID, and handle the onSuccess callback — that’s the entire client-side integration. The SDK opens Telegram automatically, monitors the session in the background, and delivers a verified PulseUser to your callback once the user confirms. No manual polling, no deep-link setup, no WebView required.

Install

Add bondify_flutter to your pubspec.yaml:
pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  bondify_flutter: ^1.2.0
Then fetch the dependency:
flutter pub get

Basic usage

Import the package and drop PulseButton into your widget tree. On a successful authentication, send user.proof to your backend immediately — never use the client-side user object alone to grant access.
login_page.dart
import 'package:flutter/material.dart';
import 'package:bondify_flutter/bondify_flutter.dart';

class LoginPage extends StatelessWidget {
  const LoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFF0F0F0F),
      body: Center(
        child: PulseButton(
          projectId: 'proj_xxxxxxxx',
          onSuccess: (PulseUser user) async {
            // user.telegramId, user.name, user.username, user.proof
            // Always send user.proof to your backend for verification:
            final response = await _createSession(user.proof);
            if (response.ok) {
              Navigator.pushReplacementNamed(context, '/home');
            }
          },
          onError: (error) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('Login failed: ${error.message}')),
            );
          },
        ),
      ),
    );
  }
}
The Flutter SDK opens Telegram automatically and polls the Bondify session in the background using an isolate-safe timer. You do not need to implement any polling loop, handle deep links, or manage session tokens manually — the SDK handles all of that and delivers the result straight to onSuccess.
Never use the PulseUser object from onSuccess to grant access on the device alone. Always send user.proof to your backend and call verifyProof() there before creating a session or returning authenticated data. Client-side data can be spoofed; the cryptographic proof cannot.

PulseUser fields

The object passed to onSuccess contains the following fields:
FieldTypeDescription
telegramIdStringThe user’s unique Telegram ID — use this as your primary identifier
nameStringThe user’s display name as set in Telegram
usernameString?The user’s @handle — nullable if they have not set one
proofStringSigned JWT to send to your backend for verification

PulseButton parameters

ParameterTypeRequiredDescription
projectIdStringYour Bondify project ID (starts with proj_)
onSuccessvoid Function(PulseUser)Callback when Telegram auth completes successfully
onErrorvoid Function(PulseError)Callback when auth fails or is cancelled by the user
textStringCustom button label (default: "Sign in with Telegram")
themePulseThemePulseTheme.light, PulseTheme.dark, or PulseTheme.auto

Sending the proof to your backend

After receiving the PulseUser in onSuccess, POST the proof to your server:
session_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<void> createSession(String proof) async {
  final response = await http.post(
    Uri.parse('https://your-api.example.com/api/session'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({'proof': proof}),
  );

  if (response.statusCode != 200) {
    throw Exception('Session creation failed: ${response.body}');
  }

  // Parse your JWT / session token from the response:
  final body = jsonDecode(response.body) as Map<String, dynamic>;
  final token = body['token'] as String;
  // Store token securely with flutter_secure_storage
}
Store your session token with the flutter_secure_storage package rather than SharedPreferences. Secure storage encrypts the value using the device’s Keychain (iOS) or Keystore (Android), which prevents other apps from reading it.

Handling errors

PulseError exposes a message string and a code enum for programmatic handling:
PulseButton(
  projectId: 'proj_xxxxxxxx',
  onSuccess: (user) => _handleSuccess(user),
  onError: (error) {
    switch (error.code) {
      case PulseErrorCode.cancelled:
        // User tapped Cancel in Telegram — no need to show an error
        break;
      case PulseErrorCode.timeout:
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Session expired. Please try again.')),
        );
        break;
      default:
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Error: ${error.message}')),
        );
    }
  },
),

Theming

Match the button to your app’s design:
PulseButton(
  projectId: 'proj_xxxxxxxx',
  theme: PulseTheme.light,
  onSuccess: (user) => _handleSuccess(user),
)

Next steps

Verify proofs in Node.js

Set up @bondify/server on your backend to validate the proof JWT and issue session tokens.

React integration

Add Telegram sign-in to a companion web app using the same project ID.