Playbook: ClickUp API Key Rotation

Service: ClickUp (https://app.clickup.com) — project management Secret: clickup_clickup_api_key SOPS file: secrets/art-agency.env.sops Rotation frequency: 90 days (or immediately on suspected exposure) Last rotated: 2026-06-27 Next due: 2026-09-27


What uses this credential

ConsumerHowEffect if missing
Art Agency appClickUp API calls to manage tasks/projects for artwork ordersTask creation and status updates fail
n8n workflows (if any)HTTP Request nodes calling ClickUp APIClickUp automation stops

Where stored

LocationKey nameHow to update
secrets/art-agency.env.sopsclickup_clickup_api_keySOPS write pattern
Art Agency .env.localclickup_clickup_api_keyUpdate manually (stale fallback only)

Login credentials

  • URL: https://app.clickup.com/settings/apps
  • Email: radieu@gmail.com
  • Auth: Google SSO or email/password

Automation status

Playwright-automatable — ClickUp API token management accessible via web UI without additional MFA.


Rotation steps

Option A — Playwright agent (preferred)

You are rotating clickup_clickup_api_key for the ClickUp project management service.

IMPORTANT: Never display any secret value in your response. Reference key names only.

1. Use Playwright to navigate to https://app.clickup.com/settings/apps
   - Sign in with Google using radieu@gmail.com
   - Find "API Token" section (may be under Settings → Apps → API)
   - Click "Regenerate" or copy-then-regenerate to get new token
   - Store new token in $env:NEW_KEY (starts with pk_; never print)
   - Close the browser

2. Update secrets/art-agency.env.sops:
   $env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
   $plain = sops --decrypt --input-type dotenv --output-type dotenv secrets\art-agency.env.sops
   $temp = "$PWD\secrets\art-agency-edit.env.sops"
   $updated = $plain -replace "^clickup_clickup_api_key=.*", "clickup_clickup_api_key=$env:NEW_KEY"
   [System.IO.File]::WriteAllText($temp, ($updated -join "`n") + "`n", [System.Text.UTF8Encoding]::new($false))
   $enc = sops --encrypt --input-type dotenv --output-type dotenv $temp
   [System.IO.File]::WriteAllText("$PWD\secrets\art-agency.env.sops", ($enc -join "`n") + "`n", [System.Text.UTF8Encoding]::new($false))
   sops --decrypt --input-type dotenv --output-type dotenv secrets\art-agency.env.sops | Out-Null
   if ($LASTEXITCODE -ne 0) { throw "SOPS corrupt — do NOT commit" }
   [System.IO.File]::Delete($temp); $env:NEW_KEY = ""

3. Commit and PR:
   git checkout -b fix/rotate-clickup-api-key origin/main
   git add secrets/art-agency.env.sops
   git commit -m "chore: rotate clickup_clickup_api_key (scheduled 90d rotation)"
   git push -u origin fix/rotate-clickup-api-key
   gh pr create --base main --title "chore: rotate ClickUp API key"
   gh pr merge --merge --delete-branch

4. Append to docs/secrets-rotation-log.md:
   | <YYYY-MM-DD> | clickup_clickup_api_key | scheduled 90d rotation | AI-agent | SOPS (art-agency) |

Option B — Manual (fallback)

Step 1 — Regenerate in ClickUp
  URL: https://app.clickup.com/settings/apps
  → Log in → API Token → Regenerate → copy value (starts with pk_)

Step 2 — Store in .env.local then hand off to Claude:
  Add to Art-Agency/.env.local (or d:\code_2026\p24-infra\.env.local):
    clickup_clickup_api_key_NEW=<value>
  Tell Claude "ClickUp key updated in .env.local" — Claude handles SOPS + PR.

Verification

$env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
$line = sops --decrypt --input-type dotenv --output-type dotenv secrets\art-agency.env.sops |
    Select-String "^clickup_clickup_api_key="
$val = $line.ToString().Split('=',2)[1]
Write-Host "clickup_clickup_api_key length: $($val.Length)"   # expected: > 20 (pk_ prefix)
 
# Quick API check
$env:K = $val
$r = Invoke-RestMethod "https://api.clickup.com/api/v2/user" `
    -Headers @{ Authorization = $env:K }
Write-Host "ClickUp user: $($r.user.username)"
$env:K = ""; $env:SOPS_AGE_KEY_FILE = ""

Escalation

SymptomAction
ClickUp UI has moved API tokenTry https://app.clickup.com/settings/apps or Settings → Integrations → API
”Regenerate” removes old token immediatelyNew token starts working right away; no dual-validity window
Art Agency app 401 after rotationCheck if art-agency.env.sops was deployed; restart Art Agency app or sync .env.local

Prevention

  • ClickUp API tokens do not expire but should be rotated on schedule.
  • The credential-rotation.yml GH Actions workflow (Monday 06:00 UTC) checks next_due and opens a human-action issue.
  • 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="CLICKUP_API_KEY",
    result="success",  # "success" | "failed" | "skipped"
    detail="Scheduled rotation — new ClickUp API key generated and SOPS updated",
    env="vps-i1",
    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', 'CLICKUP_API_KEY', 'success', 'Scheduled rotation — new ClickUp API key generated and SOPS updated', 'vps-i1')
"
$env:SUPABASE_URL = ''; $env:SUPABASE_SERVICE_KEY = ''