{
  "slug": "hide-openai-api-key-flutter",
  "title": "Hide OpenAI API Key in a Flutter App",
  "metaDescription": "Keep your OpenAI key out of Flutter builds. RealComposer proxies chat completions at the edge with vault-held keys, allowed origins, and per-client limits.",
  "headline": "Flutter AI chat without an OpenAI key in the APK",
  "category": "security",
  "categoryLabel": "Security & Secret Vault",
  "urgency": 10,
  "clientPlatform": "Flutter",
  "upstreamServices": [
    "OpenAI"
  ],
  "searchIntentQuery": "hide openai api key in flutter app",
  "directAnswerSummary": "OpenAI keys bundled in Flutter apps through env files, assets, or dart-define are extractable from release builds. Route chat requests through a RealComposer pipeline that injects OPENAI_API_KEY at the edge, applies per-client rate limits, and returns only the reply text to the app.",
  "problem": {
    "title": "Why OpenAI keys in Flutter builds get abused",
    "explanation": "Scrapers routinely unpack mobile apps looking for sk- prefixed keys. Once found, the key is used for unrelated workloads billed to your account until you notice and rotate it, which then breaks every installed copy of the app.",
    "risks": [
      "OpenAI keys extracted from flutter_assets or env files",
      "Surprise usage bills from leaked keys",
      "Rotating the key breaking older app versions"
    ]
  },
  "solutionSteps": [
    {
      "stepNumber": 1,
      "title": "Store credentials in Secret Vault",
      "description": "Save OPENAI_API_KEY in RealComposer so the value never enters the Flutter project."
    },
    {
      "stepNumber": 2,
      "title": "Configure the edge pipeline",
      "description": "Add the upstream steps with vault header injection, set allowed origins, and map only the fields the screen binds."
    },
    {
      "stepNumber": 3,
      "title": "Post chat messages with dio",
      "description": "Call your workspace edge URL with a restricted rc_live_ client token.",
      "codeSnippet": "final res = await Dio().post(\n  'https://realcomposer.com/v1/workspace/hide-openai-api-key-flutter',\n  data: {'messages': [{'role': 'user', 'content': prompt}]},\n  options: Options(headers: {'Authorization': 'Bearer rc_live_client_token'}),\n);\nfinal reply = res.data['reply'];"
    }
  ],
  "pipelineConfig": {
    "name": "Hide OpenAI API Key in a Flutter App",
    "method": "POST",
    "cacheTtlSeconds": 0,
    "authMode": "client_token",
    "steps": [
      {
        "stepKey": "openai",
        "upstreamUrlTemplate": "https://api.openai.com/v1/chat/completions",
        "httpMethod": "POST",
        "headersTemplate": {
          "Authorization": "Bearer {{secrets.OPENAI_API_KEY}}",
          "Content-Type": "application/json"
        },
        "timeoutMs": 15000
      }
    ],
    "outputMapping": {
      "version": 1,
      "fields": [
        {
          "targetPath": "reply",
          "sourceStep": "openai",
          "sourcePath": "choices.0.message.content"
        }
      ]
    }
  },
  "sampleBloatedInput": {
    "id": "chatcmpl-9x",
    "object": "chat.completion",
    "model": "gpt-4o-mini",
    "system_fingerprint": "fp_44709d6fcb",
    "choices": [
      {
        "index": 0,
        "finish_reason": "stop",
        "message": {
          "role": "assistant",
          "content": "Here is your summary."
        },
        "logprobs": null
      }
    ],
    "usage": {
      "prompt_tokens": 212,
      "completion_tokens": 48,
      "total_tokens": 260
    }
  },
  "sampleSlimmedOutput": {
    "reply": "Here is your summary."
  },
  "curlSnippet": "curl -X POST \"https://realcomposer.com/v1/workspace/hide-openai-api-key-flutter\" \\\n  -H \"Authorization: Bearer rc_live_client_token\"",
  "faqs": [
    {
      "question": "Is flutter_dotenv safe for OpenAI keys?",
      "answer": "No. flutter_dotenv loads a .env file packaged as an asset, which ships inside the app and can be read after unpacking."
    },
    {
      "question": "Can I rotate the OpenAI key without an app update?",
      "answer": "Yes. Update the secret in the RealComposer vault and every installed app keeps working because it only holds the client token."
    }
  ],
  "relatedRecipeSlugs": [
    "protect-openai-api-key-swift-ios",
    "hide-openai-api-key-react",
    "call-stripe-from-flutter-securely"
  ],
  "links": {
    "page": "https://realcomposer.com/recipes/hide-openai-api-key-flutter",
    "markdown": "https://realcomposer.com/recipes/hide-openai-api-key-flutter.md",
    "json": "https://realcomposer.com/recipes/hide-openai-api-key-flutter.json",
    "install": "https://realcomposer.com/dashboard/pipelines/new?recipe=hide-openai-api-key-flutter&via=agent"
  }
}