Skip to content
Tolinku
Tolinku
Sign In Start Free
Use Cases · · 5 min read

Onboarding for Enterprise Mobile Apps

By Tolinku Staff
|
Tolinku fintech deep linking dashboard screenshot for use cases blog posts

Enterprise mobile app onboarding is fundamentally different from consumer app onboarding. The user didn't choose to install the app; their IT department deployed it. They don't need to be "sold" on the product; they need to understand how to use it for their job. And the onboarding must satisfy IT administrators, security policies, and compliance requirements.

For improving completion rates, see Improving Onboarding Completion Rates. For freemium approaches, see Onboarding for Freemium Apps: Free to Paid Journey. For general onboarding principles, see Onboarding Best Practices for Mobile Apps in 2026.

Enterprise Onboarding Differences

Aspect Consumer App Enterprise App
Install motivation User chose the app IT deployed the app
Account creation Email + password signup SSO, SAML, SCIM provisioning
Configuration User preferences IT admin policies
Feature access Same for everyone Role-based
Compliance Terms of service Data handling policy, NDAs
Training Self-service May require guided training
Support Help center IT helpdesk + vendor support

SSO and Provisioned Onboarding

SSO Login Flow

Enterprise users authenticate via their company's identity provider:

async function handleEnterpriseLogin() {
  // Check if the device is managed (MDM)
  const mdmConfig = await getMDMConfiguration();

  if (mdmConfig && mdmConfig.ssoProvider) {
    // Auto-redirect to company SSO
    return startSSOFlow(mdmConfig.ssoProvider, mdmConfig.tenantId);
  }

  // Manual SSO discovery via email domain
  return (
    <Screen>
      <Heading>Sign in with your work email</Heading>
      <Input
        label="Work Email"
        type="email"
        onSubmit={async (email) => {
          const domain = email.split('@')[1];
          const ssoConfig = await discoverSSO(domain);

          if (ssoConfig) {
            startSSOFlow(ssoConfig.provider, ssoConfig.tenantId);
          } else {
            showError('Your organization has not been configured. Contact your IT admin.');
          }
        }}
      />
    </Screen>
  );
}

async function startSSOFlow(provider, tenantId) {
  // Redirect to identity provider
  const authUrl = buildSSOUrl(provider, tenantId, {
    redirectUri: 'yourapp://auth/callback',
    scope: 'openid profile email',
  });

  await openAuthSession(authUrl);
}

SCIM-Provisioned Users

Users provisioned via SCIM already have accounts:

async function handleProvisionedUser(ssoToken) {
  const userInfo = await validateSSOToken(ssoToken);
  const provisionedUser = await findProvisionedUser(userInfo.email);

  if (provisionedUser) {
    // Account exists, log in and apply role-based config
    await loginUser(provisionedUser);

    const roleConfig = await getRoleConfig(provisionedUser.role);
    return startRoleBasedOnboarding(provisionedUser, roleConfig);
  }

  // Not provisioned yet, but SSO is valid
  return showPendingProvisioningScreen(userInfo);
}

MDM-Based Onboarding

Reading MDM Configuration

When the app is deployed via Mobile Device Management:

async function readMDMConfig() {
  // iOS: App Config from MDM profile
  // Android: Android Enterprise managed configurations
  const config = await getAppManagedConfig();

  return {
    tenantId: config.tenant_id,
    ssoProvider: config.sso_provider,
    apiEndpoint: config.api_endpoint,
    features: config.enabled_features ? config.enabled_features.split(',') : [],
    complianceRequired: config.require_compliance === 'true',
    dataPolicy: config.data_handling_policy_url,
  };
}

Zero-Touch Setup

For MDM-deployed apps, the goal is zero-touch onboarding:

async function zeroTouchOnboarding() {
  const mdmConfig = await readMDMConfig();

  // 1. Configure the app
  await configureAppEndpoint(mdmConfig.apiEndpoint);

  // 2. Auto-authenticate via MDM certificate or SSO
  const user = await authenticateViaMDM(mdmConfig);

  // 3. Apply feature restrictions
  await applyFeaturePolicy(mdmConfig.features);

  // 4. Show compliance acknowledgment if required
  if (mdmConfig.complianceRequired) {
    await showComplianceAcknowledgment(mdmConfig.dataPolicy);
  }

  // 5. Navigate to the role-appropriate home screen
  navigation.navigate(getRoleHomeScreen(user.role));
}

Role-Based Onboarding

Defining Role Flows

Different roles need different onboarding experiences:

const roleOnboarding = {
  admin: {
    steps: ['dashboard_overview', 'team_management', 'settings', 'integrations'],
    firstScreen: 'AdminDashboard',
    features: ['all'],
  },
  manager: {
    steps: ['team_view', 'reports', 'approvals'],
    firstScreen: 'TeamOverview',
    features: ['reports', 'approvals', 'team_view', 'projects'],
  },
  member: {
    steps: ['task_overview', 'communication', 'time_tracking'],
    firstScreen: 'MyTasks',
    features: ['tasks', 'communication', 'time_tracking'],
  },
  viewer: {
    steps: ['dashboard_view', 'reports'],
    firstScreen: 'ReadOnlyDashboard',
    features: ['view_only'],
  },
};

function getRoleOnboarding(role) {
  return roleOnboarding[role] || roleOnboarding.member;
}

Role-Based Feature Tour

function RoleFeatureTour({ role }) {
  const config = getRoleOnboarding(role);

  const tourSteps = config.steps.map(step => ({
    ...featureTourContent[step],
    visible: config.features.includes('all') || config.features.includes(step),
  })).filter(s => s.visible);

  return (
    <FeatureTour
      steps={tourSteps}
      onComplete={() => navigation.navigate(config.firstScreen)}
    />
  );
}

Enterprise deep links typically involve team invitations. For a broader look at deep linking in B2B contexts, see Deep Linking for B2B Apps.

async function createTeamInviteLink(admin, inviteeEmail, role) {
  const inviteToken = await createInviteToken({
    tenantId: admin.tenantId,
    inviterId: admin.id,
    inviteeEmail: inviteeEmail,
    role: role,
    expiresIn: '7d',
  });

  const link = await Tolinku.createLink({
    path: `/join/${admin.tenantId}`,
    params: {
      invite: inviteToken,
      role: role,
      org: admin.organizationName,
    },
    ogTitle: `Join ${admin.organizationName} on [App]`,
    ogDescription: `${admin.name} invited you to join as ${role}.`,
  });

  return link.url;
}
async function handleEnterpriseDeepLink(deferred) {
  if (deferred === null) return startStandardLogin();

  const params = deferred.params;

  if (params.invite) {
    // Validate invite token
    const invite = await validateInviteToken(params.invite);

    if (invite === null) {
      return showError('This invite link has expired. Ask your admin for a new one.');
    }

    if (invite.expired) {
      return showError('This invite link has expired. Ask your admin for a new one.');
    }

    // Pre-configure based on invite
    return {
      ssoProvider: invite.ssoProvider,
      tenantId: invite.tenantId,
      role: invite.role,
      organizationName: invite.organizationName,
    };
  }
}

Enterprise users often receive deep links to specific tasks, documents, or projects:

function handleResourceDeepLink(url) {
  const path = new URL(url).pathname;

  // Authenticate first
  if (user.isAuthenticated === false) {
    pendingDeepLink.save(url);
    navigation.navigate('SSOLogin');
    return;
  }

  // Check access
  const resource = parseResourcePath(path);
  const hasAccess = checkResourceAccess(user, resource);

  if (hasAccess === false) {
    showAccessDenied(resource);
    return;
  }

  navigation.navigate(resource.screen, resource.params);
}

Compliance and Policy Acknowledgment

Required Acknowledgments

Enterprise apps often require users to acknowledge policies before using the app. For compliance-specific guidance, see Onboarding and Compliance: Handling Regulations.

async function checkComplianceOnboarding(user) {
  const requiredPolicies = await getRequiredPolicies(user.tenantId);
  const acknowledged = await getAcknowledgedPolicies(user.id);

  const pending = requiredPolicies.filter(
    policy => acknowledged.includes(policy.id) === false
  );

  if (pending.length > 0) {
    return pending;
  }

  return null;
}

function PolicyAcknowledgmentScreen({ policies, onComplete }) {
  const [currentPolicy, setCurrentPolicy] = useState(0);

  const policy = policies[currentPolicy];

  return (
    <Screen>
      <Heading>{policy.title}</Heading>
      <ScrollView>
        <PolicyContent content={policy.content} />
      </ScrollView>

      <Checkbox
        label={`I have read and acknowledge the ${policy.title}`}
        onChange={(checked) => {
          if (checked) {
            acknowledgePolicy(policy.id);
            if (currentPolicy < policies.length - 1) {
              setCurrentPolicy(currentPolicy + 1);
            } else {
              onComplete();
            }
          }
        }}
      />
    </Screen>
  );
}

Data Classification

Enterprise apps may need to inform users about data handling:

function DataClassificationNotice({ tenantConfig }) {
  return (
    <Notice>
      <Heading>Data Handling Notice</Heading>
      <Text>
        Data entered in this app is classified as {tenantConfig.dataClassification}
        and is stored in {tenantConfig.dataRegion}.
      </Text>
      <Text>
        Do not enter {tenantConfig.restrictedDataTypes.join(', ')} in this app.
      </Text>
      <Button onPress={acknowledge}>I Understand</Button>
    </Notice>
  );
}

Measuring Enterprise Onboarding

Admin-Facing Metrics

Metric Definition Target
Deployment completion Users who completed setup / Users provisioned > 90%
Time to first use Provisioned to first action < 24 hours
SSO success rate Successful SSO logins / Attempts > 95%
Policy acknowledgment rate Policies acknowledged / Required 100%
Support ticket rate Tickets during onboarding / Total users < 5%

Deployment Tracking

async function deploymentReport(tenantId) {
  const provisioned = await countProvisionedUsers(tenantId);
  const completed = await countCompletedOnboarding(tenantId);
  const active = await countActiveUsers(tenantId, '7d');
  const tickets = await countSupportTickets(tenantId, 'onboarding');

  return {
    provisioned,
    onboardingCompletion: (completed / provisioned * 100).toFixed(1) + '%',
    activeRate: (active / provisioned * 100).toFixed(1) + '%',
    supportTicketRate: (tickets / provisioned * 100).toFixed(1) + '%',
  };
}

For deep linking features, see Tolinku deep linking. For onboarding use cases, see the onboarding documentation.

Get deep linking tips in your inbox

One email per week. No spam.

Ready to add deep linking to your app?

Set up Universal Links, App Links, deferred deep linking, and analytics in minutes. Free to start.