Store API Keys Securely
Manage sensitive credentials with encrypted storage
Store API keys, passwords, and credentials securely. Bifrost encrypts secrets using Fernet encryption and scopes them per organization.
Two Ways to Store Secrets
Section titled “Two Ways to Store Secrets”Settings → Configuration → + Add Config
- Key:
api_key - Type: Secret
- Value:
sk_live_xxxxx(your actual secret)
Stores the value encrypted in the database automatically.
from bifrost import config
# Store a secret (encrypted in database)
await config.set("api_key", "sk_live_xxxxx", is_secret=True)Use is_secret=True to ensure the value is encrypted.
Use in Workflows
Section titled “Use in Workflows”from bifrost import config
@workflow(name="call_api")async def call_api(context: ExecutionContext): # Get secret (works for both Secret and Secret Reference types) api_key = await config.get("api_key")
headers = {"Authorization": f"Bearer {api_key}"} response = await make_api_call(url, headers=headers) return responseCommon Use Cases
Section titled “Common Use Cases”API Keys
Section titled “API Keys”Store third-party API credentials:
@workflow(name="external_api")async def external_api(context: ExecutionContext): api_key = await config.get("stripe_api_key")
response = requests.post( "https://api.stripe.com/v1/charges", headers={"Authorization": f"Bearer {api_key}"} ) return response.json()Database Credentials
Section titled “Database Credentials”Store database passwords securely:
@workflow(name="db_query")async def db_query(context: ExecutionContext): db_password = await config.get("postgres_password")
conn = await asyncpg.connect( host="db.example.com", user="bifrost", password=db_password, database="production" ) result = await conn.fetch("SELECT * FROM users") await conn.close() return resultWebhook Signatures
Section titled “Webhook Signatures”Verify incoming webhooks:
import hmacimport hashlib
@workflow(name="verify_webhook")async def verify_webhook(context: ExecutionContext, payload: str, signature: str): webhook_secret = await config.get("webhook_secret")
# Calculate expected signature expected = hmac.new( webhook_secret.encode(), payload.encode(), hashlib.sha256 ).hexdigest()
if not hmac.compare_digest(expected, signature): raise ValueError("Invalid webhook signature")
return {"verified": True}Organization Scoping
Section titled “Organization Scoping”Secrets are automatically isolated per organization:
- Org A stores
api_keywith valuesk_live_aaa - Org B stores
api_keywith valuesk_live_bbb - Workflows automatically use their org’s value
# Automatically uses current org's secretapi_key = await config.get("api_key")Each organization has completely separate secrets in the database.
Updating Secrets
Section titled “Updating Secrets”Via UI
Section titled “Via UI”- Settings → Configuration
- Find config → Click Edit
- Enter new value → Save
Via SDK
Section titled “Via SDK”# Update secret valueawait config.set("api_key", "sk_live_new_value", is_secret=True)Best Practices
Section titled “Best Practices”✅ Do This:
# Get encrypted secretapi_key = await config.get("api_key")
# Log without exposing valuelogger.info("Making API call with stored credentials")
# Don't return in responsereturn {"success": True, "user_id": user.id}❌ Don’t Do This:
# Hardcoded secretapi_key = "sk_live_123abc"
# Logged secret valuelogger.info(f"API key: {api_key}")
# Returned in responsereturn {"api_key": api_key}Error Handling
Section titled “Error Handling”Always handle missing secrets gracefully:
@workflow(name="safe_api_call")async def safe_api_call(context: ExecutionContext): try: api_key = await config.get("api_key") except KeyError: return { "success": False, "error": "API key not configured", "action": "Add 'api_key' to Configuration in Settings" }
# Use api_key... return {"success": True}Security Features
Section titled “Security Features”- Encryption: All secrets encrypted using Fernet (symmetric encryption)
- Key Management: Encryption key derived from
BIFROST_SECRET_KEYenvironment variable - Audit Logs: All secret access is logged
- Never Exposed: Secrets never returned in API responses or logs
- Org Isolation: Complete separation between organizations
Troubleshooting
Section titled “Troubleshooting”Config not found: Verify config exists in Settings → Configuration for your organization.
Decryption errors: Ensure BIFROST_SECRET_KEY hasn’t changed. Changing the key will invalidate existing encrypted secrets.
Next Steps
Section titled “Next Steps”- OAuth Integration - Connect to external OAuth providers
- Microsoft Graph - Use Microsoft Graph API
- Custom APIs - Integrate any REST API