Skip to main content

Sessions on Flutter

All session APIs are Future-returning and are reached through Relavoi.instance.sessions. Errors throw a subclass of RelavoiException — typically SessionException, ValidationException, or TierLimitException.

Create a session​

create takes named parameters. Only agentPhone and customerPhone are required; everything else has a default. Phone numbers must be in E.164 format (for example +2348012345678).

import 'package:relavoi_flutter/relavoi_flutter.dart';

Future<void> startMaskingSession(
String agent,
String customer,
String orderId,
) async {
try {
final session = await Relavoi.instance.sessions.create(
agentPhone: agent,
customerPhone: customer,
metadata: {'orderId': orderId},
gracePeriodMinutes: 15,
directionMode: DirectionMode.bidirectional,
recordingEnabled: false,
);
debugPrint('Proxy ready: ${session.proxyNumber}');
} on ValidationException catch (e) {
debugPrint('Invalid input: $e');
} on TierLimitException catch (e) {
debugPrint('Concurrent session limit reached: $e');
} on RelavoiException catch (e) {
debugPrint('Failed to create session: $e');
}
}

The full signature is:

Future<Session> create({
required String agentPhone,
required String customerPhone,
Map<String, dynamic>? metadata,
int gracePeriodMinutes = 15,
DirectionMode directionMode = DirectionMode.bidirectional,
bool recordingEnabled = false,
});

Because the two phone numbers are the only required arguments, this is equally valid:

final session = await Relavoi.instance.sessions.create(
agentPhone: agent,
customerPhone: customer,
);

:::note maxDurationMinutes is server-controlled The hard call-duration cap is enforced by the backend and surfaced on the returned Session as maxDurationMinutes. It is not a creation parameter. Likewise, if recording is enabled the server requires a consent prompt — that combination is validated server-side. :::

Fetch one​

final session = await Relavoi.instance.sessions.get('sess_a1b2c3d4');

List with pagination​

list accepts an optional state filter and returns a SessionListResponse. Advance the cursor with after, which is null on the last page.

String? cursor;
final collected = <Session>[];

do {
final page = await Relavoi.instance.sessions.list(
state: SessionState.active,
limit: 100,
after: cursor,
);
collected.addAll(page.data);
cursor = page.after;
} while (cursor != null);

SessionListResponse is shaped:

class SessionListResponse {
final List<Session> data;
final int count;
final String? after;
}

Omit state (or pass null) to list sessions in every state.

End a session​

await Relavoi.instance.sessions.end('sess_a1b2c3d4');

The session transitions to SessionState.gracePeriod. Watch the events stream for session.expired to know when it terminates.

Initiate a call from the agent's device​

initiateCall opens the OS dialer with the proxy number pre-filled, via url_launcher and a tel: URI. It does not place the call automatically — the agent taps the dial button, meeting both platforms' policies for dialer integrations.

Pass the proxy number (not the session id):

Future<void> callCustomer(Session session) async {
await Relavoi.instance.sessions.initiateCall(session.proxyNumber);
}

The Session model​

Session carries these fields:

class Session {
final String id;
final String tenantId;
final String proxyNumber;
final SessionState state; // pending / active / gracePeriod / expired / failed
final DirectionMode directionMode; // bidirectional / aToB / bToA
final Map<String, dynamic>? metadata;
final int gracePeriodMinutes;
final int maxDurationMinutes;
final bool recordingEnabled;
final String consentPrompt;
final DateTime expiresAt;
final DateTime createdAt;
final DateTime? activatedAt;
final DateTime? endedAt;
final DateTime? expiredAt;
final int callCount;
final DateTime? lastCallAt;
}

SessionState is one of pending, active, gracePeriod, expired, or failed. DirectionMode is one of bidirectional, aToB, or bToA.

Full lifecycle example​

final session = await Relavoi.instance.sessions.create(
agentPhone: '+2348012345678',
customerPhone: '+2348087654321',
metadata: {'orderId': 'ORD-9281'},
);

// UI shows session.proxyNumber to the agent
proxyNumber.value = session.proxyNumber;

// Agent taps "Call customer"
await Relavoi.instance.sessions.initiateCall(session.proxyNumber);

// ... calls happen ...

// On delivery
await Relavoi.instance.sessions.end(session.id);

Continue with Verification to surface the branded banner when an incoming call lands.