Skip to main content
If you already have a working Microsoft 365 Delegation Credential, you can let Cal.com replace its Microsoft Entra client secret before it expires, instead of rotating it by hand every few months. This page covers the setup for a credential that already exists — including credentials created before automatic rotation was available.
Automatic rotation applies to Microsoft 365 delegation credentials only. Google Workspace delegation credentials authenticate with a service account key that doesn’t expire, so there’s nothing to rotate.

What you need

  • Organization admin access in Cal.com
  • Microsoft Entra admin access for the tenant that owns the app registration, in a work or school tenant (this setup cannot be completed with a personal/consumer Microsoft account)
  • The Azure CLI

Step 1: Grant the rotation permission

Cal.com rotates the secret by calling Microsoft Graph addPassword on your app registration, which requires the Application.ReadWrite.OwnedBy application permission.
1

Add the permission

In the Microsoft Entra admin center, open your app registration, go to API permissions → Add a permission → Microsoft Graph → Application permissions, and add Application.ReadWrite.OwnedBy.
2

Grant admin consent

Click Grant admin consent for <your tenant> and confirm that the permission shows as granted.

Step 2: Make the app an owner of itself

Application.ReadWrite.OwnedBy only lets the application manage app registrations it owns. Granting the permission is not enough on its own — the application’s own service principal has to be an owner of its app registration. This is the most common reason rotation looks configured but keeps failing. The Owners blade in the portal only accepts users, so the ownership has to be added with the Azure CLI. You need two different object IDs:

Step 3: Check your tenant secret lifetime policy

Cal.com creates each replacement secret with a 180-day lifetime. If your tenant has an app management policy that caps client secret lifetimes below 180 days, Microsoft rejects every attempt and rotation never succeeds. If that applies to you, either exempt this app registration from the policy, relax the cap to at least 180 days, or keep rotating the secret manually by opting the credential out of automatic rotation.

Step 4: Make sure the credential isn’t opted out

Automatic rotation is on by default. If the credential was explicitly opted out, turn that off with API v2 — there’s no toggle for this in the dashboard yet:
A GET on the same endpoint shows the current value.

Step 5: Hand the current secret over to Cal.com

Cal.com can only rotate a secret it created itself — it has no way to learn the expiry date of a secret you pasted in. So a credential that has never had a Cal.com-managed secret needs one staged once, which happens when a credential is enabled:
1

Disable the credential

Go to Settings → Organization → Delegation Credential and toggle the credential off.
2

Enable it again

Toggle it back on. Cal.com verifies the credential, then creates a Cal.com-managed secret on the app registration and stages it as pending — your own secret stays the active one, so nothing breaks in the meantime.
3

Wait for promotion

Within about an hour, Cal.com validates the pending secret with an app-only token request and promotes it to active. From then on, Cal.com knows the expiry and rotates it automatically about a week before it runs out.
If you’d rather replace the secret at the same time, updating the client secret through API v2 disables the credential as part of the update — enabling it again then stages the managed secret.

Step 6: Verify it worked

GET /v2/organizations/{orgId}/delegation-credentials/{credentialId} returns the rotation state:
  • secret.expiresAt filled in and roughly 180 days out means a Cal.com-managed secret is active and rotation is set up.
  • pendingSecret.createdAt set means a replacement is staged and waiting for validation.
  • secretRotationErrorCode tells you why the last attempt failed — see below.
In the Microsoft Entra admin center you’ll see the old secret still listed under Certificates & secrets. That’s expected: Cal.com leaves it to expire on its own rather than deleting it, so requests already in flight aren’t disrupted.

When rotation fails

Cal.com retries a failing rotation for 48 hours. After that it stops trying, sets secretRotationBlocked to true and fires a DELEGATION_CREDENTIAL_SECRET_ROTATION_FAILED webhook whose error.code is SECRET_MINT_FAILED (Microsoft refused to create the replacement) or SECRET_PROMOTION_FAILED (the replacement was created but never authenticated successfully). Treat any value you don’t recognize as unknown — new codes can be added over time. Once you’ve fixed the underlying problem, clear the block from Settings → Organization → Delegation Credential → ⋯ → Reset rotation (only selectable while a credential is blocked), or with POST /v2/organizations/{orgId}/delegation-credentials/{credentialId}/reset-rotation. Rotation then resumes on the next scheduled run.
A Conditional Access policy that blocks app-only (client_credentials) token requests breaks rotation even when the permissions and ownership are correct: the new secret is created but can never be validated, so it shows up as a promotion failure.

Audit rotations with webhooks

Three webhook triggers cover the rotation lifecycle:
  • DELEGATION_CREDENTIAL_SECRET_ROTATED — a managed secret was validated and promoted to active. Useful as an audit trail.
  • DELEGATION_CREDENTIAL_SECRET_ROTATION_FAILED — rotation reached a terminal failure and the credential is blocked. Subscribe to this if you rely on automatic rotation.
  • DELEGATION_CREDENTIAL_ROTATION_REQUIRED — sent for credentials that opted out of automatic rotation, as a reminder to rotate manually.