Skip to main content

Patterns

API key rotation strategy

Two active keys, 90-day rotation, dual-validation

An API key that doesn't rotate is a compromised key on borrowed time. Scell.io pattern: each tenant can keep 2 `sk_live_*` keys simultaneously active (current key + rotation key). Rotation workflow every 90 days: (1) generate a new key via `POST /api/v1/api-keys`, (2) deploy the new key in all environments (CI, prod, secret manager), (3) wait 7 days for the new key to be used (verify via `GET /api/v1/api-keys/{id}/usage`), (4) revoke the old one via `DELETE /api/v1/api-keys/{old_id}`. The overlap window prevents outage if a service forgets the rolling. For compromised secrets (incident), bypass the window: revoke immediately and trigger a re-deploy. NEVER share a key between tenants or environments; NEVER hardcode in source; always via env var or secret manager (cf. secret-management pattern).

Key facts

  • 2 keys active simultaneously (current + rotation)
  • Scheduled rotation every 90 days
  • 7-day minimum overlap window
  • Verify usage via GET /api/v1/api-keys/{id}/usage before revocation
  • Incident: immediate revocation, no window

Code example

// Cron job mensuel — génération clé de rotation
import { ScellClient } from '@scell/sdk';
import { vault } from './vault';

async function rotateScellKey() {
  const scell = new ScellClient(await vault.read('scell/current'));

  // 1. Generate new key
  const newKey = await scell.apiKeys.create({ label: `rotation-${Date.now()}` });

  // 2. Push to vault as 'next' (services should fall back to 'next' first)
  await vault.write('scell/next', newKey.secret);

  // 3. Trigger rolling re-deploy across services
  await ci.triggerWorkflow('rolling-deploy', { reason: 'scell-key-rotation' });

  // 4. After 7 days (separate cron), promote 'next' to 'current' and revoke old
  // scheduleAfter('7 days', async () => {
  //   await vault.copy('scell/next', 'scell/current');
  //   await scell.apiKeys.delete(oldKeyId);
  // });
}

See also

Your cookie preferences

We use cookies to improve your experience. Essential cookies are always active. Cookie policy.