# Hide Anthropic API Key in an Expo App

> EXPO_PUBLIC_ variables and app config extra fields are inlined into every Expo build and EAS Update bundle, so an Anthropic key placed there is public. Call a RealComposer pipeline instead: it injects ANTHROPIC_API_KEY as the x-api-key header at the edge and returns just the reply text.

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

## Problem: Why Expo apps leak Anthropic keys

Developers often move keys into EXPO_PUBLIC_ variables assuming environment variables are private. Expo inlines them into the JavaScript bundle, and EAS Update serves that bundle to every device, where it can be downloaded and searched.

- Anthropic key inlined into the Hermes bundle
- OTA updates redistributing the key to every install
- Large Messages API payloads parsed on the JS thread

## Solution steps

1. **Store credentials in Secret Vault**: Save ANTHROPIC_API_KEY in RealComposer so the value never enters the Expo 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. **Call the pipeline from Expo**: Call your workspace edge URL with a restricted rc_live_ client token.

```
const res = await fetch('https://realcomposer.com/v1/workspace/hide-anthropic-api-key-expo', {
  method: 'POST',
  headers: { Authorization: 'Bearer rc_live_client_token', 'Content-Type': 'application/json' },
  body: JSON.stringify({ messages: [{ role: 'user', content: prompt }] }),
});
const { reply } = await res.json();
```

## 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 Anthropic API Key in an Expo App",
  "method": "POST",
  "cacheTtlSeconds": 0,
  "authMode": "client_token",
  "steps": [
    {
      "stepKey": "anthropic",
      "upstreamUrlTemplate": "https://api.anthropic.com/v1/messages",
      "httpMethod": "POST",
      "headersTemplate": {
        "x-api-key": "{{secrets.ANTHROPIC_API_KEY}}",
        "anthropic-version": "2023-06-01",
        "Content-Type": "application/json"
      },
      "timeoutMs": 15000
    }
  ],
  "outputMapping": {
    "version": 1,
    "fields": [
      {
        "targetPath": "reply",
        "sourceStep": "anthropic",
        "sourcePath": "content.0.text"
      }
    ]
  }
}
```

## Call the pipeline

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

## Payload before and after

Raw upstream response:

```json
{
  "id": "msg_01Xf",
  "type": "message",
  "role": "assistant",
  "model": "claude-sonnet-4-5",
  "stop_reason": "end_turn",
  "content": [
    {
      "type": "text",
      "text": "Your itinerary is ready."
    }
  ],
  "usage": {
    "input_tokens": 180,
    "output_tokens": 32
  }
}
```

Slimmed RealComposer response:

```json
{
  "reply": "Your itinerary is ready."
}
```

## FAQ

### Can I store the Anthropic key as an EAS secret?

Only for build steps. If app code reads it at runtime, it is bundled into the app and becomes public.

### Does this work in Expo Go and dev clients?

Yes. The app only makes a standard fetch to the edge URL, so it behaves the same in Expo Go, development builds, and production.
