# Hide OpenAI API Key in a Flutter App

> 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.

- Canonical page: https://realcomposer.com/recipes/hide-openai-api-key-flutter
- Client platform: Flutter
- Upstream services: OpenAI
- Category: Security & Secret Vault
- JSON version: https://realcomposer.com/recipes/hide-openai-api-key-flutter.json
- Install in RealComposer (free plan, pre-fills this pipeline): https://realcomposer.com/dashboard/pipelines/new?recipe=hide-openai-api-key-flutter&via=agent

## Problem: Why OpenAI keys in Flutter builds get abused

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.

- OpenAI keys extracted from flutter_assets or env files
- Surprise usage bills from leaked keys
- Rotating the key breaking older app versions

## Solution steps

1. **Store credentials in Secret Vault**: Save OPENAI_API_KEY in RealComposer so the value never enters the Flutter project.

2. **Configure the edge pipeline**: Add the upstream steps with vault header injection, set allowed origins, and map only the fields the screen binds.

3. **Post chat messages with dio**: Call your workspace edge URL with a restricted rc_live_ client token.

```
final res = await Dio().post(
  'https://realcomposer.com/v1/workspace/hide-openai-api-key-flutter',
  data: {'messages': [{'role': 'user', 'content': prompt}]},
  options: Options(headers: {'Authorization': 'Bearer rc_live_client_token'}),
);
final reply = res.data['reply'];
```

## Pipeline config

Import this config into a RealComposer pipeline. `{{secrets.NAME}}` values are read from the encrypted Secret Vault at request time, so the client never sees them.

```json
{
  "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"
      }
    ]
  }
}
```

## Call the pipeline

```bash
curl -X POST "https://realcomposer.com/v1/workspace/hide-openai-api-key-flutter" \
  -H "Authorization: Bearer rc_live_client_token"
```

## Payload before and after

Raw upstream response:

```json
{
  "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
  }
}
```

Slimmed RealComposer response:

```json
{
  "reply": "Here is your summary."
}
```

## FAQ

### Is flutter_dotenv safe for OpenAI keys?

No. flutter_dotenv loads a .env file packaged as an asset, which ships inside the app and can be read after unpacking.

### Can I rotate the OpenAI key without an app update?

Yes. Update the secret in the RealComposer vault and every installed app keeps working because it only holds the client token.
