Skip to Content
    Qafka
    CTRL K
    CTRL K
    • Introduction
      • Quick Start (React Native)
      • Quick Start (Website)
      • React Native Configuration
      • Overview
      • npm & React
      • Options Reference
      • WordPress
      • React Native Widget
      • Headless SDK
      • Theming
      • Context
      • Navigation
      • External Navigation
      • Handling Tools
      • Voice Chat
      • Sub-Projects
      • Error Handling
      • CLI
      • Dashboard
      • Onboarding
      • Invitations
      • Billing
      • Usage
      • Settings
      • Sign-in & Account
      • Dashboard Assistant
        • Project
        • Overview
        • Conversations
        • Chat Test
        • Sub-Projects
        • Analysis
        • Unanswered Questions
        • Insights
        • Configuration
        • AI Behavior
        • Members
        • Documents
          • Overview
          • Create with AI
          • Versions
          • Response Channel
        • Action Logs
        • Navigation Rules
        • External Destinations
        • Chat Theme
        • PII Masking
        • Websites
        • Mobile Apps
        • API Keys
        • Project Settings
      • API Key Security
    • Introduction
      • Quick Start (React Native)
      • Quick Start (Website)
      • React Native Configuration
      • Overview
      • npm & React
      • Options Reference
      • WordPress
      • React Native Widget
      • Headless SDK
      • Theming
      • Context
      • Navigation
      • External Navigation
      • Handling Tools
      • Voice Chat
      • Sub-Projects
      • Error Handling
      • CLI
      • Dashboard
      • Onboarding
      • Invitations
      • Billing
      • Usage
      • Settings
      • Sign-in & Account
      • Dashboard Assistant
        • Project
        • Overview
        • Conversations
        • Chat Test
        • Sub-Projects
        • Analysis
        • Unanswered Questions
        • Insights
        • Configuration
        • AI Behavior
        • Members
        • Documents
          • Overview
          • Create with AI
          • Versions
          • Response Channel
        • Action Logs
        • Navigation Rules
        • External Destinations
        • Chat Theme
        • PII Masking
        • Websites
        • Mobile Apps
        • API Keys
        • Project Settings
      • API Key Security

    On This Page

    • When to Use Sub-Projects
    • The SDK Contract
    • Identifying a Sub-Project
    • Switching at Runtime
    • Voice and Sub-Projects
    • Patterns
    • Location Switcher
    • Auth-Driven Tenant Routing
    • Single-Project Fallback
    • Errors
    Question? Give us feedback Edit this page 
    GuidesSub-Projects

    Sub-Projects

    Sub-projects let one parent project serve multiple tenants from the same SDK install — same parent project, but the SDK passes a subProjectId to route each session to the right child. The mobile codebase stays the same; the AI configuration (greeting, documents, theme, conversations) varies by tenant. Tools are always the parent project’s — they don’t vary per sub-project.

    This page covers the SDK side — the prop, the runtime behavior, and the cross-feature limits. For dashboard-side configuration (creating sub-projects, inheritance, per-sub-project conversations), see Dashboard › Sub-Projects.

    When to Use Sub-Projects

    Sub-projects are the right answer when one mobile app serves multiple distinct tenants that each need their own AI personality, knowledge base, or branding — but you don’t want the engineering cost of running multiple builds or multiple integrations.

    Concrete examples:

    • Multi-location app — same app for every branch, but each branch’s AI knows its own offerings and promotions (works for a restaurant chain, a clinic network, a bank, a gym franchise)
    • Multi-brand retail — same shopping app for several brand partners, each with its own tone and product catalog
    • White-label deployment — one codebase you ship to multiple customer organizations, each with their own assistant
    • Regional variants — same app worldwide, but the AI varies by country’s documents and rules

    If your app serves a single tenant, don’t use sub-projects — they add a layer of indirection (the parent + the child) that buys nothing in single-tenant cases. Use one project.

    The SDK Contract

    Sub-project routing is a top-level prop, not part of the runtime context:

    <Qafka // projectId, endUserId, … subProjectId="branch-london" />

    This is a deliberate API decision — putting subProjectId inside context would make it look like one more piece of optional metadata, when in reality it’s a routing key that changes which project the SDK is talking to. Mistakes here ripple silently: a misnamed key in context would be ignored; a wrong subProjectId prop produces a clear Sub-project not found error.

    Identifying a Sub-Project

    The subProjectId value can be either:

    • The sub-project’s UUID (immutable, generated by the dashboard) — e7f2a4b8-3c5d-4f1a-9d8b-2e1f5c4a7b6d
    • A human-readable identifier you set in the dashboard — branch-london, branch-nyc, tenant-acme

    The backend tries UUID first, then falls back to identifier match within the parent. Prefer human-readable identifiers — they’re easier to debug from the conversations view, easier to thread through your app’s existing branch/tenant ID model, and stable across environments.

    Switching at Runtime

    When subProjectId changes, the SDK fully tears down the current session and reinitializes against the new sub-project. This means:

    • The active conversation ends — the user starts fresh in the new sub-project
    • Any in-flight message is dropped
    • Theme, greeting, documents, and instructions all reload from the new sub-project (tools stay the same — they’re always the parent project’s)
    • The voice session, if active, is closed (see voice limitation below)

    Practically, this is what you want — switching tenants mid-conversation almost never makes semantic sense (“show me Branch A’s menu — actually no, Branch B’s”). But be deliberate about when you change the prop:

    // User picks a different branch from a location switcher → re-mount with new ID <Qafka // projectId, endUserId, … subProjectId={selectedBranch.identifier} />

    If you wrap <Qafka /> in your own location-switcher UI, don’t pipe a frequently-changing value into subProjectId. Memoize or settle on the chosen tenant before passing it in.

    Voice and Sub-Projects

    Voice chat currently does not respect subProjectId — the SDK doesn’t forward it to the voice session, so voice always authenticates against whichever project the API key or attestation session resolves to (the parent), regardless of which child the rest of the SDK is configured for. Sub-project-aware voice routing is a known gap.

    If sub-project-specific voice configuration is critical for your launch, fall back to text chat for sub-project flows, or hide the voice page on sub-project screens via the voiceEnabled={false} prop until the gap is closed.

    Patterns

    Location Switcher

    User picks a branch from a list, the chat for that branch opens:

    function BranchChatScreen({ branch }) { return ( <Qafka projectId="proj_abc123" endUserId={currentUser?.id ?? 'anonymous'} subProjectId={branch.identifier} /> ) }

    When the user navigates between branch screens, the parent component remounts <Qafka /> with the new identifier and the chat re-initializes against that branch’s sub-project.

    Auth-Driven Tenant Routing

    User signs in; their tenant is on the user record:

    function ChatScreen() { const { user } = useAuth() if (!user) return <LoginPrompt /> return ( <Qafka projectId="proj_abc123" endUserId={user.id} subProjectId={user.tenantId} isAuthenticated context={{ role: user.role }} /> ) }

    Notice the user’s identity goes in endUserId (required, groups conversations/analytics per user), role is arbitrary runtime metadata so it goes in context, and tenantId is subProjectId because it’s a routing key.

    Single-Project Fallback

    If subProjectId is undefined (or omitted), the SDK falls through to the parent project — the same behavior as a single-tenant app. This is useful when your app is gradually migrating to multi-tenancy:

    <Qafka // projectId, endUserId, … subProjectId={tenant?.identifier} /* undefined for users not yet assigned to a tenant */ />

    Users on the parent fallback see the parent’s greeting, tools, and documents. Users with a sub-project assigned see their sub-project’s overrides on top.

    Errors

    The SDK surfaces sub-project resolution errors through onError. The most common ones:

    ErrorWhat it means
    Sub-project not foundThe subProjectId value didn’t match any active child of the active parent project. Check the value against the dashboard.
    Project not found for API key (API-key auth) / Project is not a parent (keyless auth)You passed subProjectId, but the project your key/session resolves to is itself already a sub-project — sub-projects can’t have sub-projects of their own.

    If the subProjectId is missing or empty, the SDK silently uses the parent — that is not an error.

    Last updated on October 1, 2026
    Voice ChatError Handling

    © 2026 Qafka Labs OÜ