Mode: operator-assisted Last validated: 2026-04-24 UTC Validation method: read-only-docs (sandbox-rotation safe — paper account has no real-money impact) Average duration: 5m Required role: ops
Applies to: ALPACA_PAPER_API_KEY_ID, ALPACA_PAPER_API_SECRET_KEY (and any per-environment paper keys, e.g., dev/staging variants).
Important gotcha surfaced during research: Alpaca's "Reset Paper Account" button on the dashboard also regenerates the API key/secret. If an operator clicks "Reset" intending to clear simulated balance, they will silently invalidate the running paper credentials. The community has requested this behavior change for years; it is unresolved as of 2026-04-24. Always rotate via the explicit "Regenerate" / "View" path on the API Keys panel, never via the Reset button.
https://app.alpaca.markets/paper/dashboard/overview)# Confirm current key still works against the paper endpoint
curl -sS -H "APCA-API-KEY-ID: $CURRENT_KEY_ID" \
-H "APCA-API-SECRET-KEY: $CURRENT_SECRET" \
https://paper-api.alpaca.markets/v2/account | jq '.status'
# Expect: "ACTIVE"
Snapshot any open paper orders/positions in case a downstream restart cancels them.
There is no Alpaca API to rotate keys programmatically. The dashboard is the only path.
https://app.alpaca.markets/paper/dashboard/overview.NEW_KEY_ID="..."
NEW_SECRET="..."
curl -sS -H "APCA-API-KEY-ID: $NEW_KEY_ID" \
-H "APCA-API-SECRET-KEY: $NEW_SECRET" \
https://paper-api.alpaca.markets/v2/account | jq '{status, account_number, buying_power}'
# Expect: status = "ACTIVE", account_number unchanged from pre-rotation snapshot.
infisical secrets set ALPACA_PAPER_API_KEY_ID="$NEW_KEY_ID" \
--projectId="$INFISICAL_PROJECT_ID" --env=prod
infisical secrets set ALPACA_PAPER_API_SECRET_KEY="$NEW_SECRET" \
--projectId="$INFISICAL_PROJECT_ID" --env=prod
| Consumer | How |
|---|---|
| Raptor (raxx-api-prod, raxx-api-staging) | heroku config:set ALPACA_PAPER_API_KEY_ID=... ALPACA_PAPER_API_SECRET_KEY=... -a raxx-api-prod |
| Local dev environments | DM operator via Slack D0AJ7K184TV to update local .env |
| GitHub Actions (integration tests against paper) | gh secret set ALPACA_PAPER_API_KEY_ID -b ...; gh secret set ALPACA_PAPER_API_SECRET_KEY -b ... |
# Hit the Raptor endpoint that proxies Alpaca paper /v2/account
curl -sS https://api.raxx.app/api/trading/account | jq '.alpaca_status'
# Expect: "ACTIVE" (or whatever wrapper the Raptor route emits on success)
Place a paper test order via the Raptor API and verify it appears in the Alpaca paper dashboard:
curl -sS -X POST https://api.raxx.app/api/trading/orders \
-H "Content-Type: application/json" \
-d '{"symbol":"AAPL","qty":1,"side":"buy","type":"market","time_in_force":"day","mode":"paper"}'
# Confirm order ID appears in https://app.alpaca.markets/paper/dashboard/orders
The Alpaca dashboard's "Generate New Keys" / "Regenerate" action already invalidates the old key pair atomically — no separate revoke step is needed. Confirm:
curl -sS -o /dev/null -w "%{http_code}\n" \
-H "APCA-API-KEY-ID: $OLD_KEY_ID" \
-H "APCA-API-SECRET-KEY: $OLD_SECRET" \
https://paper-api.alpaca.markets/v2/account
# Expect: 401 or 403
action: secret.rotate.completed
actor: <admin_id>
context: {"secret_name": "ALPACA_PAPER_API_KEY_ID", "method": "operator-assisted-dashboard", "rotated_at": "..."}
No rollback exists. Once "Generate New Keys" is clicked the old keys are dead. Options: