Version 0.1.14

Kallo SDK documentation

Add native SIP voice calling to your Flutter Android & iOS app — install, quickstart, and the full API reference.

Overview

Kallo SDK wraps the battle-tested baresip stack behind a small, clean Dart API so you can add real phone-grade voice calling — registration, inbound and outbound calls, mute, hold, and DTMF — to your Flutter app without writing native code. It ships as a closed binary; no C or native source is included.

  • Native SIP registration over TLS
  • Outbound and inbound calls
  • Mute, hold, and resume
  • DTMF tone sending
  • Speaker / earpiece routing
  • SRTP-encrypted media by default
  • Opus + G.711 audio codecs
  • Runtime license validation

Android (minimum API 28 / Android 9) and iOS (13+) are both supported — one Flutter package; Flutter builds the right half per target.

Requirements

  • Flutter 3.10+ (Dart 3.0+).
  • Android minSdk 28 (Android 9) or higher — the native plugin declares minSdk 28; lower values fail the build.
  • Android: the RECORD_AUDIO runtime permission must be requested and granted before placing or accepting a call.
  • iOS 13+ — add NSMicrophoneUsageDescription to your app's Info.plist. The SDK requests the microphone at call time, so do NOT pre-request it in the app (a host-app pre-request can block the call).

Quickstart

Add the SDK to your app's pubspec.yaml (use the path or git URL provided with your license), then run flutter pub get.

pubspec.yaml
dependencies:
  kallo_sdk:
    path: ../kallo-sdk   # or the git: URL provided with your license

Add the required permissions to android/app/src/main/AndroidManifest.xml:

AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

Set minSdk to 28 (or higher) in android/app/build.gradle.kts:

android/app/build.gradle.kts
android {
    defaultConfig {
        minSdk = 28   // Kallo requires API 28+ (Android 9)
    }
}

A working call

A complete flow: initialize, register, wait until registered, place a call, follow its state, and hang up. This uses the current instance-based API (KalloClient.instance).

Dart
import 'package:kallo_sdk/kallo_sdk.dart';

Future<void> demo() async {
  // 1. Initialize — validates your license, then configures the SIP stack.
  await KalloClient.instance.init(
    KalloConfig(
      domain: 'sip.example.com',
      username: '1001',
      password: 'secret',
      licenseKey: 'YOUR-LICENSE-KEY',
      // licenseServerUrl defaults to https://kallo-api.up.railway.app
    ),
  );

  // 2. Listen for registration state changes.
  KalloClient.instance.registrationStream.listen((state) {
    print('registration: $state');
  });

  // 3. Register, then wait until we are registered.
  await KalloClient.instance.register();
  await KalloClient.instance.registrationStream
      .firstWhere((s) => s == KalloRegistrationState.registered);

  // 4. Place an outbound call and follow its state.
  final call = await KalloClient.instance.calls.makeCall('1002');
  call.stateStream.listen((state) {
    print('call: $state');
  });

  // 5. Hang up later.
  await call.hangup();
}

Receiving inbound calls

Dart
// Subscribing eagerly attaches the native listener, so inbound
// calls are not dropped before you handle them.
KalloClient.instance.calls.incomingCalls.listen((call) async {
  await call.accept(); // or: await call.reject();
});

Handling errors

Wrap init / register in try/catch. Catch KalloLicenseException for license problems, or the KalloException base type for any SDK failure.

Dart
try {
  await KalloClient.instance.init(config);
} on KalloLicenseException catch (e) {
  print('license problem: ${e.message}');
} on KalloException catch (e) {
  print('SDK error: ${e.message}');
}

Start with your AI assistant

In a hurry? Copy the prompt below and paste it into your AI coding assistant (Cursor, Claude Code, or similar) to scaffold a working Kallo SDK integration in your Flutter app. It already includes the key facts and the correct instance-based API; the agent will read the rest of this page for detail and ask you for your SIP credentials and license key.

AI assistant prompt
Integrate the Kallo SDK (native SIP voice calling) into my Flutter app (Android & iOS).

About the SDK:
- Android (minSdk 28 / Android 9) and iOS (13+). One Flutter plugin package
  contains both platforms — Flutter builds the right half per target.
- Flutter 3.10+ / Dart 3.0+.
- Distributed as a closed-binary Flutter plugin package (the kallo_sdk Dart API
  plus prebuilt native binaries: an Android .aar and an iOS xcframework), NOT on
  pub.dev. Add it as a path: (or private git) dependency on the kallo_sdk package
  in pubspec.yaml — do not try to fetch it from pub.dev, and do not add the raw
  .aar/xcframework to the native projects directly.
- Android: requires the RECORD_AUDIO permission (plus INTERNET and
  MODIFY_AUDIO_SETTINGS) in AndroidManifest.xml, and the RECORD_AUDIO runtime
  permission must be requested and granted before placing or accepting a call.
- iOS: add NSMicrophoneUsageDescription to Info.plist. The SDK requests the mic
  at call time — do NOT pre-request microphone permission in the app (a host-app
  pre-request can block the call). Run pod install after flutter pub get.

Use the instance-based API (KalloClient.instance — NOT a static/global API):

  import 'package:kallo_sdk/kallo_sdk.dart';

  // 1. Initialize — validates the license, then configures the SIP stack.
  await KalloClient.instance.init(
    KalloConfig(
      domain: '<SIP domain>',
      username: '<SIP username>',
      password: '<SIP password>',
      licenseKey: '<Kallo license key>',
    ),
  );

  // 2. Listen to registration state.
  KalloClient.instance.registrationStream.listen((state) {
    // state is a KalloRegistrationState:
    // none | registering | registered | failed | expired
  });

  // 3. Register and wait until registered.
  await KalloClient.instance.register();
  await KalloClient.instance.registrationStream
      .firstWhere((s) => s == KalloRegistrationState.registered);

  // 4. Place an outbound call and follow its state.
  final call = await KalloClient.instance.calls.makeCall('<destination>');
  call.stateStream.listen((state) {
    // state is a KalloCallState:
    // idle | calling | ringing | active | held | ended | failed
  });

  // 5. Hang up.
  await call.hangup();

Build a minimal call screen (single StatefulWidget is fine) that:
- Calls init(...) then register() on start, and shows the current registration
  state in the UI.
- Has a text field for the destination extension/number and a "Call" button
  that invokes KalloClient.instance.calls.makeCall(<destination>).
- Shows the live call state from the returned call's stateStream.
- Has a "Hang up" button that calls call.hangup().
- Wraps init / register / makeCall in try/catch with error handling on the
  Kallo exception types: catch KalloLicenseException (bad/expired license),
  KalloRegistrationException (registration failed), KalloCallException (call
  failed), and KalloNetworkException, falling back to the KalloException base
  type. Show a readable message from e.message.

Important:
- Do NOT hardcode credentials. Ask me for my SIP domain, username, password,
  and Kallo license key, and wire them in from config / secure storage.
- Do NOT invent SDK methods. The full API reference (KalloConfig fields,
  KalloClient / KalloCall / KalloCallManager members, enums, exceptions) and
  install steps are on this same docs page — read it for anything not shown
  above, and ask me if a signature is unclear rather than guessing.

API reference

KalloConfig

Immutable configuration passed to KalloClient.instance.init(). Throws ArgumentError on an empty domain/username or an out-of-range port.

FieldTypeDescription
domainStringSIP domain / registrar host (required).
usernameStringSIP account username / extension (required).
passwordStringSIP account password (required).
licenseKeyStringCommercial license key issued for your app (required).
displayNameString?Optional display name shown to the remote party.
useTLSbool = trueUse TLS transport for SIP signalling (default true, port 5061).
useSRTPbool = trueUse SRTP for media encryption (default true).
stunServerString?Optional STUN server URI for NAT traversal.
caBundlePathString?Path to a PEM CA bundle to verify the server's TLS certificate (for a private CA or pinned cert).
tlsAcceptSelfSignedbool = falseAccept the server's TLS certificate without verification — testing only; never enable in production.
portint?Signalling port. Defaults to 5061 (TLS) or 5060 (UDP).
licenseServerUrlStringBase URL of the Kallo license backend; override only for self-hosted validators.

KalloClient

The shared SIP client, accessed via KalloClient.instance. forTesting() builds an isolated instance for unit tests.

KalloClient.instanceinit(KalloConfig config, {String? appId, http.Client? httpClient})register()unregister()dispose()registrationStreamcurrentRegistrationStatecalls

KalloCall

Represents a single call. Returned by makeCall and emitted on incomingCalls.

callIdremoteUridirectionaccept()reject()hangup()setMuted(bool muted)setHold(bool onHold)setSpeaker(bool speaker)sendDtmf(String digit)stateStreamcurrentStateisMutedisOnHold

KalloCallManager

Manages calls, reachable via KalloClient.instance.calls (also KalloCallManager.instance).

makeCall(String destination)incomingCallsactiveCall

Enums

KalloRegistrationState

Registration lifecycle states emitted on registrationStream.

noneregisteringregisteredfailedexpired
KalloCallState

Per-call lifecycle states emitted on a call's stateStream. ended and failed are terminal.

idlecallingringingactiveheldendedfailed
CallDirection

Whether a call was placed by this device (outbound) or received (inbound).

inboundoutbound

Exceptions

All SDK errors extend KalloException. Catch the base type to handle any failure, or a subtype for finer control.

KalloExceptionBase type for every error thrown by the SDK.
KalloLicenseExceptionLicense validation failed or could not be completed.
KalloRegistrationExceptionSIP registration failed (bad credentials, unreachable server, or timeout).
KalloCallExceptionA call operation failed (place, accept, reject, hangup, or an in-call control).
KalloNetworkExceptionA transport/network-level failure talking to native or the license backend.

Get the SDK

Request access, then download the closed-binary SDK from your dashboard once you have an active license.

Request the SDKLatest release: kallo_sdk-0.1.14.tar.gz