[object Object]

A contractor leaves on Friday. Monday morning you notice they had the production OAuth client secret stored in their personal notes app. You rotate. You discover six integrations broke because nobody knew which scripts used that token. Three of them are critical. Sales is escalating. This is what no-rotation looks like in real time.

Zoho Vault solves half of this — storage. The other half — rotation cadence and discipline — is yours.

What lives where

Three categories of secrets in a typical Zoho-centric stack:

  1. Zoho OAuth client secrets and refresh tokens for internal Deluge integrations
  2. Third-party API keys consumed by Deluge or Flow (Slack, Stripe, ERP, marketing tools)
  3. User passwords and SSH keys for tools that don’t support SSO

Each has different rotation cadence and different blast radius if leaked.

Rotation cadence by class

  • Production OAuth client secrets: quarterly, plus on contractor offboarding
  • Production third-party API keys: quarterly, plus on contractor offboarding
  • Refresh tokens used by long-running integrations: rotate yearly, plus on incident
  • Service account passwords: 60-day max
  • User passwords with admin scope: 90 days, enforced by Zoho One password policy
  • SSH keys to deploy targets: yearly, plus on offboarding

If “plus on contractor offboarding” makes you nervous because you don’t know who has what access, that’s the real problem. Rotation discipline starts with an inventory.

The inventory

Build a Secrets_Inventory row in Vault (or a sister CRM custom module if Vault doesn’t accept the metadata you need):

  • Name (e.g., “Stripe Live API Key”)
  • Owner (engineer responsible)
  • Backup owner (always have one)
  • Consumed by (list of scripts, functions, or Flows that use it)
  • Rotation cadence
  • Last rotated
  • Next due
  • Severity if compromised (low / medium / high / critical)

If you can’t answer “what consumes this secret?” you can’t rotate it safely. Inventory first.

The rotation pattern with zero-downtime rollover

Most secrets support dual-active credentials briefly. Use that window.

  1. Generate new secret in the source system (Stripe, internal OAuth, etc.). Don’t deactivate old.
  2. Store new in Vault under the same name with a _pending suffix.
  3. Update all consumers to read the new secret. Test each.
  4. After 24 hours of clean operation, promote _pending to active. Move old to _retired_<date> for 30 days.
  5. After 30 days of no consumer using the retired secret, revoke at source.

Don’t compress the timeline. The 24-hour soak catches the integration nobody remembered.

A Deluge helper for reading from Vault

Don’t hardcode secrets. Don’t even hardcode the Vault reference inline. Wrap in a helper.

// vault_get: standard accessor with metric tracking
// Logs every fetch for audit
string vault_get(string secret_name)
{
  caller = thread.currentStackFrame.toString();
  
  // Audit log entry
  zoho.crm.createRecord("Secret_Access_Log", {
    "Secret_Name": secret_name,
    "Caller": caller,
    "Accessed_At": zoho.currenttime
  });
  
  // Fetch from Vault
  response = invokeurl
  [
    url: "https://vault.zoho.com/api/rest/json/v1/secrets/" + secret_name
    type: GET
    headers: {"Authorization": "Zoho-oauthtoken " + zoho.oauth.getAccessToken("vault")}
  ];
  
  if(response.get("status") != "success")
  {
    sendmail
    [
      to: "[email protected]"
      from: "[email protected]"
      subject: "Vault fetch failed: " + secret_name
      message: "Caller: " + caller + "\nResponse: " + response.toString()
    ];
    return null;
  }
  
  return response.get("secret_value");
}

// Usage in any Deluge function
stripe_key = vault_get("stripe_live_api_key");

What this gives you:

  • An audit log of every secret access. Now “which scripts use this?” is queryable.
  • A central failure path. Vault down? One alert email, not 12.
  • The inventory’s “consumed by” field can be populated automatically from the access log.

Auto-discovery of consumers

Once vault_get is logging accesses, build a weekly cron that updates the inventory:

// weekly_consumer_discovery
secrets = zoho.crm.getRecords("Secrets_Inventory");
for each s in secrets
{
  name = s.get("Name");
  last_7_days_callers = zoho.crm.searchRecords(
    "Secret_Access_Log",
    "(Secret_Name:equals:" + name + ")and(Accessed_At:after:" + subDay(zoho.currenttime, 7).toString() + ")"
  );
  
  unique_callers = Set();
  for each log in last_7_days_callers
  {
    unique_callers.add(log.get("Caller"));
  }
  
  zoho.crm.updateRecord("Secrets_Inventory", s.get("id"), {
    "Active_Consumers": unique_callers.toString(),
    "Active_Consumers_Updated": zoho.currenttime
  });
}

Now you can rotate with confidence because you know who depends on each secret.

Offboarding playbook

When a contractor or employee leaves:

  1. Disable their SSO and remove from groups within 1 hour of exit.
  2. Pull their Vault access log for the past 90 days. Identify every secret they accessed.
  3. Rotate any secret they accessed that’s in the “high” or “critical” severity tier. Always.
  4. Rotate “medium” tier secrets within 30 days, queued in the next rotation window.
  5. Leave “low” tier alone unless you have specific reason to suspect compromise.

Don’t try to rotate every secret on offboarding. You’ll burn out and skip the next one. Tier it.

What not to put in Vault

  • Personal passwords (those belong in the individual user’s password manager, not the org vault)
  • Long-lived test/staging credentials with limited blast radius (use a dev vault)
  • One-time codes (TOTP — generate fresh each time)

The vault is for production org secrets. Don’t pollute it.

Granular access

A Vault folder structure that scales:

  • /prod/integrations/ — read by service accounts only
  • /prod/oauth/ — read by automation owners
  • /staging/ — broader read access
  • /incident-response/ — break-glass access, logged and alerted

Default deny. Grant by group, not by user. When someone leaves the group, access dies with the membership.

Incident response

If a secret is suspected compromised:

  1. Rotate immediately (don’t wait for the dual-active soak).
  2. Revoke at source immediately after rotation completes for known consumers.
  3. Check access logs for that secret over the suspected exposure window.
  4. Notify security lead.
  5. If high-tier, page leadership.

A compromised Stripe key with payouts enabled is not a “deal with it Monday” problem.

Audit cadence

Quarterly:

  • Review every secret in inventory. Is it still in use? Is the consumers list up to date?
  • Confirm rotation cadence is being honored. Anything overdue?
  • Confirm severity classifications. New attack surface or business changes may shift these.

Annually:

  • Tabletop a secret-leak scenario. Walk through detection, containment, rotation, recovery. Find the gaps in your runbook.
  • Review who has Vault admin access. Should they still?

For broader security posture and the audit-log discipline that pairs with this, see Zoho audit logs for incident response. For the integration patterns that consume these secrets, see Zoho CRM automation deep dive.

Key takeaways

Storage in Vault is the easy half. Rotation cadence and discipline are the hard half. Build an inventory with consumers, severity, and rotation due dates. Use dual-active rollover with a 24-hour soak. Wrap secret fetches in a helper that logs access. Auto-populate “consumed by” from the access log. Tier rotation by severity. Always rotate critical-tier on contractor offboarding. Audit quarterly. The cost of rotation is paid in maintenance hours; the cost of no rotation is paid in incident response and reputation.

[object Object]
Share