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

    • Key types
    • Production: attestation, not a bundled secret
    • Development: the Test key
    • Web keys: public, with layered restrictions
    • Limits
    • Headless usage
    • Best practices
    Question? Give us feedback Edit this page 
    SecurityAPI Key Security

    API Key Security

    Key types

    A project has three kinds of key, shown in Dashboard → Project → API Keys:

    TypePurposeVisibility
    ProductionA secret key — never embed it in an app or website. Production React Native apps don’t need one: they authenticate via device attestation instead (see below).Shown once, at creation.
    TestDevelopment and simulator/emulator use.Shown once, at creation — the CLI fetches and stores it for you.
    Web (publishable)Used by the CDN snippet, or as publishableKey when embedding with the npm package.Public by design — shown in full while active, any time in the dashboard, meant to be readable in your page source.

    Production: attestation, not a bundled secret

    In production React Native builds, the SDK does not need a Production key to authenticate. Every request is backed by a session token issued after a successful device attestation (App Attest on iOS, hardware-backed Android Key Attestation on Android). The SDK sends projectId along with the attestation challenge and the attestation submission; the backend resolves your project from projectId and checks that the attested app is registered on it — projectId is required, and it is not a secret. The app’s bundle ID and Team ID (iOS) or signing certificate (Android) must be registered on the project beforehand, or attestation fails. There is no API key to extract from a decompiled app in this path.

    If a device doesn’t support attestation, the request fails rather than falling back to an unverified path — except in development, where the SDK detects it’s running in __DEV__ and continues with a stand-in session so you can keep working in a simulator.

    Development: the Test key

    Test keys skip device attestation so simulators work — that’s why they’re tightly constrained and must never ship:

    • Strict, fixed rate limits, plus a daily call cap — independent of your plan and not adjustable from the dashboard.
    • Automatic rotation every 30 days. The previous key stops working immediately once rotated; there’s no overlap window.

    Before attestation is available, the SDK authenticates with this key. The CLI (qafka init / qafka sync / qafka refresh) writes it into .qafka/qafka-runtime.js, which is gitignored and never committed. The SDK loads it through devConfig, gated behind __DEV__ so Metro removes the require from production builds. See Quick Start and Configuration for the exact wiring.

    The dashboard only ever shows a masked value for Test (and Production) keys after creation — the plaintext appears once, in the create dialog. If a Test key leaks, delete or deactivate it on the dashboard’s API Keys page, then run qafka project to issue a fresh Test key into .qafka/qafka-runtime.js (running qafka refresh afterward will otherwise just report that the project has no active Test key).

    Web keys: public, with layered restrictions

    Web keys (qafka_pk_...) are meant to sit in your website’s HTML — they’re publishable, not secret, the same model as Stripe’s publishable keys. Several layers keep them from being useful outside your own site:

    • Origin allowlisting. A chat session can only be started from an origin registered to the project (your site’s domains, or an active embed) — other sites embedding the key can’t open a session. A key can optionally narrow this further to its own allowed domains.
    • Scoped to session bootstrap. A Web key can only fetch widget config, start a chat session, and report the widget’s own events — it’s rejected outside those endpoints, so it can’t be used to call other API endpoints.
    • Bot checks and rate limits. Sessions started from a Web key go through bot verification and their own strict rate limits.

    Limits

    How a request authenticates decides which rate-limit check applies:

    • Requests with an API key (Production or Test) are checked per minute across three axes scaled from your plan — the API key, the source IP, and the session. Test keys are the exception: they use fixed limits that can’t be raised, plus a daily cap, independent of your plan.
    • Keyless native app traffic (device attestation, no API key) is checked against a single per-IP ceiling instead of the three-axis check above.
    • Web SDK sessions (no API key, no hardware attestation) get their own stricter limits, scoped per visitor and per project.

    See Error Handling › Throttling for the 429 response shape.

    Headless usage

    The headless path also takes a projectId:

    import { QafkaSDK } from '@qafka/react-native' const sdk = QafkaSDK.getInstance() await sdk.initialize({ projectId: 'proj_abc123' })

    See Headless SDK for the full pattern.

    Best practices

    • Use the CLI for app integration — the Test key never enters your source tree.
    • Use Test keys during development so production traffic counters stay clean.
    • Restrict Web keys to the exact domains that embed them.
    • Replace or revoke Production keys you no longer use.
    • Treat all keys as sensitive in CI logs, support tickets, and screenshots.
    Last updated on October 1, 2026
    Project Settings

    © 2026 Qafka Labs OÜ