Secret Offloading
MirApi provides two ways to keep upstream API credentials out of your application code and out of your logs. Both approaches inject the Authorization header into the upstream request at the proxy edge — your backend code never needs to hold plaintext secrets.
Option 1: Ephemeral Pass-Through (X-Identity-Key)
Section titled “Option 1: Ephemeral Pass-Through (X-Identity-Key)”Pass your API key in the X-Identity-Key header. MirApi reads it into memory, injects it as the Authorization header into the upstream request, and never writes it to any log or persistent storage.
curl -X POST https://proxy.mirapi.io/ \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.stripe.com/v1/charges" \ -H "X-Identity-Key: Bearer sk_live_xxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"amount": 2000, "currency": "usd", "source": "tok_visa"}'What happens:
X-Identity-Key: Bearer sk_live_...is read from your request header- The header is stripped from the request (never forwarded as
X-Identity-Key) Authorization: Bearer sk_live_...is injected into the upstream request- Stripe receives a standard
Authorizationheader - The key value is never written to any log
Your application logs may show the outbound header name but the value should be kept in your secrets manager. MirApi’s internal logs never record header values.
Use Cases for X-Identity-Key
Section titled “Use Cases for X-Identity-Key”# Stripe paymentcurl -X POST https://proxy.mirapi.io/ \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.stripe.com/v1/charges" \ -H "X-Identity-Key: Bearer sk_live_..." \ -d '{"amount": 2000, "currency": "usd", "source": "tok_visa"}'
# OpenAI chat completioncurl -X POST https://proxy.mirapi.io/ \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.openai.com/v1/chat/completions" \ -H "X-Identity-Key: Bearer sk-proj-..." \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'
# Twilio (with Basic auth format)curl -X POST https://proxy.mirapi.io/ \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.twilio.com/2010-04-01/Accounts/$ACCOUNT_SID/Messages" \ -H "X-Identity-Key: Basic $TWILIO_B64_CREDENTIALS" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "To=+1234567890&From=+0987654321&Body=Hello"
# Any API that uses a custom API key header — use the header directly insteadcurl https://proxy.mirapi.io/ \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.sendgrid.com/v3/mail/send" \ -H "Authorization: Bearer SG.xxx..." # Pass directly if the key isn't secret from logsOption 2: Encrypted Database Storage (X-Proxy-Master-Key)
Section titled “Option 2: Encrypted Database Storage (X-Proxy-Master-Key)”Store your upstream credentials encrypted in the MirApi database. At request time, provide the decryption passphrase in X-Proxy-Master-Key. The gateway decrypts the credential and injects it as Authorization — your passphrase and the plaintext credential are never stored together.
This approach is more secure for production environments because:
- Your application only stores the master passphrase (not the upstream API keys)
- Rotating upstream credentials is done in the MirApi dashboard — no code deploys needed
- Multiple credentials can be managed centrally
Setting Up Encrypted Credentials
Section titled “Setting Up Encrypted Credentials”- Open your MirApi dashboard → Credentials → Add Credential
- Enter:
- Name: A label (e.g.,
stripe-production) - Target host pattern:
api.stripe.com(or a substring match) - Value: Your API key (
sk_live_...) - Encryption passphrase: Your master key
- Name: A label (e.g.,
- The dashboard encrypts the value using AES-GCM and stores only the ciphertext
Using Encrypted Credentials
Section titled “Using Encrypted Credentials”# Gateway matches credential by target host ("api.stripe.com" matches the stored pattern)curl -X POST https://proxy.mirapi.io/ \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.stripe.com/v1/charges" \ -H "X-Proxy-Master-Key: my-master-passphrase" \ -H "Content-Type: application/json" \ -d '{"amount": 2000, "currency": "usd", "source": "tok_visa"}'The gateway:
- Reads
X-Proxy-Master-Keyfrom the request - Looks up credentials in the database that match the target hostname
- Decrypts the stored ciphertext using the provided passphrase
- Injects
Authorization: Bearer sk_live_...into the upstream request - The passphrase and plaintext are discarded from memory after the request
Overriding Header and Format (Custom Header & Template)
Section titled “Overriding Header and Format (Custom Header & Template)”By default, the proxy injects the decrypted credential into the Authorization header with a Bearer prefix. You can customize this behavior using two optional headers:
X-Proxy-Auth-Header: Overrides the destination header name (e.g.X-API-Key).X-Proxy-Auth-Template: Customizes the value format. The placeholder{{secret}}will be replaced by your decrypted key.
Example: Custom API Key Header (Anthropic)
Section titled “Example: Custom API Key Header (Anthropic)”If the upstream API expects the raw key in X-API-Key:
curl -X POST https://proxy.mirapi.io/v1/messages \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.anthropic.com/v1/messages" \ -H "X-Proxy-Master-Key: my-master-passphrase" \ -H "X-Proxy-Auth-Header: X-API-Key" \ -H "X-Proxy-Auth-Template: {{secret}}" \ -H "Content-Type: application/json" \ -d '{"model": "claude-3-5-sonnet", "messages": [{"role": "user", "content": "Hello!"}]}'- Upstream receives:
X-API-Key: <decrypted_secret>
Example: Basic Authentication
Section titled “Example: Basic Authentication”If the upstream API requires Basic auth:
curl -X GET https://proxy.mirapi.io/v1/accounts \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.somebank.com/v1/accounts" \ -H "X-Proxy-Master-Key: my-master-passphrase" \ -H "X-Proxy-Auth-Template: Basic {{secret}}"- Upstream receives:
Authorization: Basic <decrypted_secret>
Explicit Credential Selection
Section titled “Explicit Credential Selection”If you have multiple credentials matching the same hostname, specify which one to use:
# Production credentialcurl -X POST https://proxy.mirapi.io/ \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.stripe.com/v1/charges" \ -H "X-Proxy-Master-Key: my-master-passphrase" \ -H "X-Credential-ID: 4a2e5d18-df99-4d66-a212-3cb5d9f0f9b3" \ -d '{"amount": 2000}'
# Staging credential (different UUID, same passphrase)curl -X POST https://proxy.mirapi.io/ \ -H "X-MirApi-Key: $MIRAPI_KEY" \ -H "X-Target-URL: https://api.stripe.com/v1/charges" \ -H "X-Proxy-Master-Key: my-master-passphrase" \ -H "X-Credential-ID: 9b7c3f21-aa44-4e9d-bc01-1234567890ab" \ -d '{"amount": 100}'Credential Rotation
Section titled “Credential Rotation”Rotating a compromised key requires only a dashboard update — no code change or deployment:
- In the dashboard, find the credential
- Update the value with the new API key (re-encrypted with the same passphrase)
- All future requests immediately use the new key
Route Target Auth in Cascade Routing
Section titled “Route Target Auth in Cascade Routing”When using cascade routes (X-Route-Key), different targets in the cascade may require separate authorization credentials. Instead of using a single identity key, you can link each route target to its own credential_id in your database.
During request execution, if you provide X-Proxy-Master-Key in the request header, the gateway will decrypt the specific target credentials dynamically using this master passphrase and apply the custom auth_header and auth_template configured for that target. See the Cascade Routing guide for details.
Comparison
Section titled “Comparison”X-Identity-Key | X-Proxy-Master-Key | |
|---|---|---|
| Storage | Passed per-request, never stored | Stored encrypted in DB |
| Setup | None — just include the header | Requires dashboard setup |
| Key rotation | Change value in your app/secrets manager | Change only in MirApi dashboard |
| Best for | Development, simple setups, or keys already in your secrets manager | Production multi-service environments |