Playbook: RADEK_BRAND_HEYGEN_API_KEY Rotation
Service: HeyGen (https://app.heygen.com) — AI video generation
Secret: RADEK_BRAND_HEYGEN_API_KEY
SOPS file: radekkonarski-personal-brand/secrets/radekkonarski-brand.env.sops
Rotation frequency: 90 days (or immediately on suspected exposure)
Last rotated: 2026-06-27
Next due: 2026-09-27
Key naming clarification: The real HeyGen API key is
RADEK_BRAND_HEYGEN_API_KEY, stored in theradekkonarski-personal-brandrepo SOPS. A stale 4-char placeholder calledHEYGEN_API_KEYwas removed fromvps-h1.env.sopson 2026-06-27 (PR #1723). No services on vps-h1 use HeyGen directly.
What uses this credential
| Consumer | How | Effect if missing |
|---|---|---|
| n8n workflows on bms-4 | RADEK_BRAND_HEYGEN_API_KEY env var injected via bms-4/docker-compose.yml → HTTP Request nodes | Video generation workflows fail with 401 |
mcp__heygen__* MCP tools in Claude Code | Bearer token auth to HeyGen API | MCP tool calls fail with 401 |
radekkonarski-personal-brand automation | Video creation pipeline via n8n on bms-4 | Personal brand video automation stops |
Where stored
| Location | Key name | How to update |
|---|---|---|
radekkonarski-personal-brand/secrets/radekkonarski-brand.env.sops | RADEK_BRAND_HEYGEN_API_KEY | SOPS write pattern (see below) |
| bms-4 docker-compose env | RADEK_BRAND_HEYGEN_API_KEY via compose env block | Auto-synced when bms-4 stack is redeployed |
.env.local on dev workstation | RADEK_BRAND_HEYGEN_API_KEY | Update manually (emergency fallback only) |
Path to SOPS file (local): d:\code_2026\radekkonarski-personal-brand\secrets\radekkonarski-brand.env.sops
Login credentials
- Email: radieu@gmail.com
- Password: radieu@gmail.com Google account password (stored in personal credentials — not in infra SOPS)
Automation status
Playwright-automatable — Claude can rotate this via the HeyGen web UI. HeyGen provides API key management under Account → API. Spawn a Playwright agent with the prompt below.
Rotation steps
Option A — Playwright agent (preferred)
Spawn a background agent with this prompt:
You are rotating RADEK_BRAND_HEYGEN_API_KEY for the HeyGen video generation service at https://app.heygen.com.
IMPORTANT: Never display any secret value in your response. Reference key names only.
1. Use Playwright to navigate to https://app.heygen.com
- Log in with email: radieu@gmail.com
- Password: the radieu@gmail.com Google account password
(read from .env.local: Get-Content "d:\code_2026\p24-infra\.env.local" | Select-String "^GOOGLE_ACCOUNT_PASSWORD=")
- Navigate to Account (top-right avatar) → API
- Find the existing API key — note its label (never log the value)
- Click "Regenerate" or delete and create a new API key
- Store the new key value in $env:NEW_KEY (never print it)
2. Update radekkonarski-personal-brand/secrets/radekkonarski-brand.env.sops:
$env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
$sopsPath = "d:\code_2026\radekkonarski-personal-brand\secrets\radekkonarski-brand.env.sops"
$tempPath = "d:\code_2026\radekkonarski-personal-brand\secrets\radekkonarski-brand-edit.env.sops"
$plain = sops --decrypt --input-type dotenv --output-type dotenv $sopsPath
$updated = $plain -replace "^RADEK_BRAND_HEYGEN_API_KEY=.*", "RADEK_BRAND_HEYGEN_API_KEY=$env:NEW_KEY"
[System.IO.File]::WriteAllText($tempPath, ($updated -join "`n") + "`n", [System.Text.UTF8Encoding]::new($false))
$enc = sops --encrypt --input-type dotenv --output-type dotenv $tempPath
[System.IO.File]::WriteAllText($sopsPath, ($enc -join "`n") + "`n", [System.Text.UTF8Encoding]::new($false))
# Canary
sops --decrypt --input-type dotenv --output-type dotenv $sopsPath | Out-Null
if ($LASTEXITCODE -ne 0) { throw "SOPS corrupt — do NOT commit" }
[System.IO.File]::Delete($tempPath)
$env:NEW_KEY = ""
3. Commit and open PR targeting dev in the radekkonarski-personal-brand repo:
cd d:\code_2026\radekkonarski-personal-brand
git checkout -b fix/rotate-heygen-api-key origin/dev
git add secrets/radekkonarski-brand.env.sops
git commit -m "chore: rotate RADEK_BRAND_HEYGEN_API_KEY (scheduled 90d rotation)"
git push -u origin fix/rotate-heygen-api-key
gh pr create --base dev --title "chore: rotate RADEK_BRAND_HEYGEN_API_KEY" --repo radieu/radekkonarski-personal-brand
4. Append to p24-infra/docs/secrets-rotation-log.md:
| <YYYY-MM-DD> | RADEK_BRAND_HEYGEN_API_KEY | scheduled 90d rotation | AI-agent | SOPS (radekkonarski-brand) |
Option B — Manual (fallback)
Step 1 — Log into HeyGen
URL: https://app.heygen.com
Email: radieu@gmail.com
Password: radieu@gmail.com Google account password
Step 2 — Navigate to API settings
→ Click avatar / account menu (top right)
→ Account → API
(or direct URL: https://app.heygen.com/account?action=api)
Step 3 — Regenerate API key
→ Click "Regenerate API Key" or delete old → Create new
→ Copy the new key value immediately (shown once)
Step 4 — Update radekkonarski-personal-brand SOPS
$env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
$sopsPath = "d:\code_2026\radekkonarski-personal-brand\secrets\radekkonarski-brand.env.sops"
$tempPath = "d:\code_2026\radekkonarski-personal-brand\secrets\radekkonarski-brand-edit.env.sops"
$plain = sops --decrypt --input-type dotenv --output-type dotenv $sopsPath
$env:NEW_KEY = "<paste-from-step-3>"
$updated = $plain -replace "^RADEK_BRAND_HEYGEN_API_KEY=.*", "RADEK_BRAND_HEYGEN_API_KEY=$env:NEW_KEY"
[System.IO.File]::WriteAllText($tempPath, ($updated -join "`n") + "`n",
[System.Text.UTF8Encoding]::new($false))
$enc = sops --encrypt --input-type dotenv --output-type dotenv $tempPath
[System.IO.File]::WriteAllText($sopsPath, ($enc -join "`n") + "`n",
[System.Text.UTF8Encoding]::new($false))
# Canary
sops --decrypt --input-type dotenv --output-type dotenv $sopsPath | Out-Null
if ($LASTEXITCODE -ne 0) { throw "SOPS corrupt — do NOT commit" }
[System.IO.File]::Delete($tempPath); $env:NEW_KEY = ""
Step 5 — Commit and push in the radekkonarski-personal-brand repo
cd d:\code_2026\radekkonarski-personal-brand
git checkout -b fix/rotate-heygen-api-key origin/dev
git add secrets/radekkonarski-brand.env.sops
git commit -m "chore: rotate RADEK_BRAND_HEYGEN_API_KEY (scheduled 90d rotation)"
git push -u origin fix/rotate-heygen-api-key
gh pr create --base dev --title "chore: rotate RADEK_BRAND_HEYGEN_API_KEY" --repo radieu/radekkonarski-personal-brand
Step 6 — Redeploy bms-4 n8n stack to pick up new key
ssh root@54.36.123.110 "cd /opt/p24-infra/bms-4 && docker compose up -d --no-deps n8n n8n-worker-1 n8n-worker-2 n8n-worker-3"
Step 7 — Append to p24-infra/docs/secrets-rotation-log.md
| <YYYY-MM-DD> | RADEK_BRAND_HEYGEN_API_KEY | scheduled 90d rotation | radieu | SOPS (radekkonarski-brand) |
Step 8 — Clear env
$env:NEW_KEY = ""
Verification
# 1. Confirm SOPS updated (length should be >= 32 for a real HeyGen API key)
$env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
$line = sops --decrypt --input-type dotenv --output-type dotenv `
"d:\code_2026\radekkonarski-personal-brand\secrets\radekkonarski-brand.env.sops" |
Select-String "^RADEK_BRAND_HEYGEN_API_KEY="
$val = $line.ToString().Split('=', 2)[1]
Write-Host "RADEK_BRAND_HEYGEN_API_KEY length: $($val.Length)" # expected: >= 32
# 2. Quick API health check (do NOT print the key value)
$env:K = $val
$r = Invoke-RestMethod "https://api.heygen.com/v1/user/remaining_quota" `
-Headers @{ "X-Api-Key" = $env:K }
Write-Host "HeyGen quota remaining: $($r.data.remaining_quota)"
$env:K = ""; $env:SOPS_AGE_KEY_FILE = ""
# 3. Confirm n8n on bms-4 can reach HeyGen
# Run a test workflow execution via n8n UI:
# https://n8n.bms-4.infra.zintegrowana.onlineEscalation
| Symptom | Action |
|---|---|
| HeyGen UI has moved API settings | Try https://app.heygen.com/settings or search for “API” in account settings |
| Login fails (Google SSO) | Use “Sign in with Google” button if password login is unavailable |
| n8n workflows on bms-4 still fail after rotation | Redeploy n8n stack: docker compose up -d --no-deps n8n n8n-worker-1 n8n-worker-2 n8n-worker-3 on bms-4 |
| MCP HeyGen tools fail with 401 | Restart Claude Code session; the session loads the key from SOPS at startup |
| SOPS temp file encrypt fails | Confirm path_regex in .sops.yaml covers secrets/radekkonarski-brand-edit.env.sops |
Prevention
- The
credential-rotation.ymlGH Actions workflow (Monday 06:00 UTC) opens ahuman-actionissue whennext_duepasses. - HeyGen does not send expiry warnings for API keys — only the calendar reminder and GH Actions check trigger rotation.
- After rotation, update Last rotated and Next due at the top of this playbook.
Audit Log — Log to infra_operations
After this operation completes, log it to the infra_operations audit table.
Python (Linux server — bms-4, vps-i1, vps-h1, or similar):
import sys
sys.path.insert(0, '/opt/p24-infra')
from scripts.lib.log_op import log_op
log_op(
actor="claude", # "radieu" for manual human ops, "claude" for agent
op_type="credential_rotation",
resource="HEYGEN_API_KEY",
result="success", # "success" | "failed" | "skipped"
detail="Scheduled rotation — HeyGen API key regenerated and SOPS n8n-bms4 updated",
env="bms-4",
gh_issue=2730,
)PowerShell (Windows dev machine):
$env:SUPABASE_URL = (Get-Content "C:\code_2026\p24-infra\.env.local" | Select-String "^SUPABASE_URL=").ToString().Split("=",2)[1].Trim()
$env:SUPABASE_SERVICE_KEY = (Get-Content "C:\code_2026\p24-infra\.env.local" | Select-String "^SUPABASE_SERVICE_KEY=").ToString().Split("=",2)[1].Trim()
python -c "
import os, sys
sys.path.insert(0, 'C:/code_2026/p24-infra')
from scripts.lib.log_op import log_op
log_op('claude', 'credential_rotation', 'HEYGEN_API_KEY', 'success', 'Scheduled rotation — HeyGen API key regenerated and SOPS n8n-bms4 updated', 'bms-4')
"
$env:SUPABASE_URL = ''; $env:SUPABASE_SERVICE_KEY = ''