Documentation

Configuration

API base URL, orgId, and environment switching for a Flutter AppMint app.

Two values drive every request: the AppEngine base URL and the orgid header. Configure them once at app start and forget about them — the rest of the SDK reads from a single source.

Environment file

The appmint_mobile reference app keeps environment selection in lib/config/environment.dart and switches automatically based on Flutter's build mode. Copy this file into your project and edit the URLs as needed.

// lib/config/environment.dart
enum Environment { development, production }

class EnvironmentConfig {
  static Environment _currentEnvironment = Environment.development;

  static Environment get currentEnvironment => _currentEnvironment;
  static void setEnvironment(Environment env) => _currentEnvironment = env;

  static bool get isDevelopment => _currentEnvironment == Environment.development;
  static bool get isProduction => _currentEnvironment == Environment.production;

  static String get appengineEndpoint {
    switch (_currentEnvironment) {
      case Environment.development:
        return 'http://localhost:3300';
      case Environment.production:
        return 'https://appengine.appmint.io';
    }
  }

  // Default org for dev only — production users must enter their own.
  static String get defaultOrgId => isDevelopment ? 'demo' : '';

  // Network
  static int get connectionTimeout => 30000;
  static int get receiveTimeout => 30000;
  static int get maxRetries => 3;

  // Feature flags
  static bool get enableDebugLogs => isDevelopment;
  static bool get enableCrashReporting => isProduction;

  // Storage keys
  static const String accessTokenKey = 'access_token';
  static const String refreshTokenKey = 'refresh_token';
  static const String userDataKey = 'user_data';
  static const String orgIdKey = 'org_id';

  // Pagination
  static const int defaultPageSize = 20;
}

Auto-switch on build mode

Wire the environment in main.dart so debug builds use development and release builds use production.

// lib/main.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'config/environment.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  if (kReleaseMode) {
    EnvironmentConfig.setEnvironment(Environment.production);
  } else {
    EnvironmentConfig.setEnvironment(Environment.development);
  }

  if (kDebugMode) {
    debugPrint('AppEngine: ${EnvironmentConfig.appengineEndpoint}');
    debugPrint('Default orgId: ${EnvironmentConfig.defaultOrgId}');
  }

  runApp(const MyApp());
}

flutter run will automatically use development. flutter build apk --release or flutter build ios --release will use production.

Where the orgId comes from

orgid identifies the tenant on every request. There are three common ways to populate it.

1. Hard-coded for single-tenant apps

If your app ships to one tenant only — a private operator app, or a white-label build — bake the orgId into a build flavor.

// lib/config/build_config.dart
class BuildConfig {
  static const String orgId = String.fromEnvironment(
    'ORG_ID',
    defaultValue: 'demo',
  );
}

Build with flutter build ios --dart-define=ORG_ID=acme-prod.

2. Entered by the user on first launch

Most consumer apps prompt the user once on the sign-in screen, then persist the value in shared_preferences.

final prefs = await SharedPreferences.getInstance();
String? orgId = prefs.getString(EnvironmentConfig.orgIdKey);

orgId ??= EnvironmentConfig.defaultOrgId;
if (orgId.isEmpty) {
  // Show "Enter your workspace" screen
}

3. Resolved from an email domain

If users sign in with their work email, ask AppEngine which org they belong to via GET /profile/who-is/{email} (the lookup is public). This avoids ever asking the user for an orgId.

The orgid header is lowercase. The customer endpoint also accepts x-org-id, but orgid is the canonical name and what every reference app uses.

Headers AppEngine expects

Every authenticated request sends three headers. Get them right once in your HTTP client and forget about them everywhere else.

Content-Type: application/json
Authorization: Bearer <jwt>
orgid: <your-org-id>

Content-Type is omitted on GET and DELETE. Authorization is omitted on public endpoints (sign-in, sign-up, password reset). orgid is always required — even on public endpoints — because the tenant boundary is enforced server-side.

If you forget orgid, AppEngine returns 400. If you send the wrong one, it returns 200 with empty results — you have just queried somebody else's tenant.

Local development against localhost:3300

The development AppEngine server runs on http://localhost:3300 by default. From a simulator/emulator the URL changes:

TargetBase URL
iOS simulatorhttp://localhost:3300
Android emulatorhttp://10.0.2.2:3300
Physical device on LANhttp://<host-lan-ip>:3300

The reference apps detect the platform and adjust:

import 'dart:io' show Platform;

static String get appengineEndpoint {
  if (isProduction) return 'https://appengine.appmint.io';
  if (Platform.isAndroid) return 'http://10.0.2.2:3300';
  return 'http://localhost:3300';
}

If you are running AppEngine on your LAN and testing on a phone, set appengineEndpoint to your host's IP (for example http://192.168.1.239:3300) — appmint_mobile does this directly.

Cleartext HTTP on Android

Android blocks cleartext HTTP by default in release builds. For local dev you'll hit a CLEARTEXT_NOT_PERMITTED error. Add a debug-only network security config in android/app/src/main/res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="true">10.0.2.2</domain>
    <domain includeSubdomains="true">localhost</domain>
  </domain-config>
</network-security-config>

Reference it from AndroidManifest.xml:

<application
  android:networkSecurityConfig="@xml/network_security_config"
  ...>

Production traffic to appengine.appmint.io is HTTPS, so this only affects debug builds.

With the environment file in place, the next page wires up the canonical AppmintClient that every screen will call.