# Recover when the Zendesk OAuth refresh token is revoked

## TL;DR
The stored refresh token was revoked - by the user, an admin, a password change, or the app being uninstalled - so every refresh attempt now fails with invalid_grant. Revocation is terminal: there is no way to un-revoke it. Send the user through the OAuth authorization flow again for a fresh token pair, and make the integration flip to a "needs re-auth" state on invalid_grant instead of retrying forever.

## The error
```text
POST https://YOUR-subdomain.zendesk.com/oauth/tokens -> 400
{"error":"invalid_grant","error_description":"The refresh token is revoked or invalid"}
```

## Use this skill
Agent? Search Vectle for this error:
```bash
curl -s "https://vectle.com/api/v1/search?q=zendesk%20oauth%20refresh%20token%20revoked%20error"
```

## Fix it

### Step 1: Confirm it is revocation, not a transient error

```bash
Attempt one refresh and log the exact error body. Retry once after a short wait to rule out a blip.
```

Expected: invalid_grant both times. A transient error would differ or clear; revocation is deterministic.

### Step 2: Mark the stored token pair dead

```bash
In the integration's credential store, flag the Zendesk token pair as revoked so no worker keeps attempting refresh.
```

Expected: refresh attempts stop immediately. Every doomed refresh is wasted API traffic and log noise.

### Step 3: Re-authorize through the OAuth flow

```bash
Direct the Zendesk admin to the app's authorize URL to grant access again.
```

Expected: a fresh authorization code. Only the account holder or admin can re-grant; the integration cannot do it alone.

### Step 4: Exchange and store the new pair atomically

```bash
Exchange the code for the new access and refresh tokens and write both to the store in one transaction.
```

Expected: API calls succeed with the new access token. Atomic storage avoids half-written pairs.

### Step 5: Handle invalid_grant as re-auth, not retry

```bash
In the token client, catch invalid_grant on refresh: set the integration state to needs-reauth, notify the admin, and stop retrying.
```

Expected: the next revocation pages a human with a clear action instead of burning retries silently.

## When this applies

- Zendesk refresh returns invalid_grant consistently
- The integration worked before and suddenly lost auth
- A user or admin changed passwords or uninstalled the app

## When it doesn't

- The access token expired but refresh works (normal rotation)
- The error is intermittent (network or Zendesk-side issue)
- The authorize step itself fails (app config problem)

## Compatibility

Zendesk OAuth 2.0 authorization code flow, API v2. Applies to subdomain-scoped tokens.

## Variant phrasings

### zendesk invalid_grant refresh token

Confirm the grant type is refresh_token and the client credentials are correct before concluding revocation.

### zendesk oauth token revoked reauthorize

Re-authorization is the only recovery. There is no refresh-of-a-revoked-token.

## Why it happens

Refresh tokens are long-lived secrets that die on user or admin action: revoking the app's access, uninstalling, or changing the account password. The token endpoint cannot distinguish "revoked" from "never existed" and answers invalid_grant either way. Because the integration holds no other credential for that user, only a fresh interactive grant can restore access.

## Edge cases

- Zendesk revokes tokens on password change; schedule re-auth comms around password policies
- Some configurations allow only one active refresh token per user and client; concurrent refreshes can kill each other
- The offline-access style scope is required to receive a refresh token at all; without it there is nothing to refresh

## If it still fails

- Capture the exact timestamp, the username, and the full error from the system log before changing anything else.
- Reproduce with a single test user so you are not debugging a crowd.
- If it worked before, diff the config against the last known good, then open a vendor ticket with the timestamp and request id. Never send secrets or private keys.

## Prevention

- Track credential and certificate expiry with alerts, not memory.
- Run a synthetic check per app daily so breakage pages you, not a user.
- Document mappings, URLs, and runbook steps where the next admin will find them.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_pYkatfmrnBIPeFlwKgH8wg
