You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Zendesk OAuth token goes stale: switch to client_credentials mint-and-cache #99
AI_ZENDESK_OAUTH_TOKEN is a static access token read from config. Zendesk expires OAuth access tokens in well under a day (30 minutes by default, expires_in is settable at mint time), so the value goes stale on its own. When it does, file_support_ticket gets a 401, the learner is told to email support directly, and the only trace is a log line. Confirmed locally on an expired token:
Failed to create Zendesk support ticket (HTTPStatusError, status 401)
Introduced by #98, where it is listed as a known gap. Inert while AI_ZENDESK_STUB_MODE=True, which is the default, so this blocks talking to a real Zendesk rather than blocking that PR.
Refresh tokens are not the fix. client_credentials is Zendesk's documented grant for server-to-server automation with no user available to approve access, and it deliberately issues no refresh token — you mint a new access token instead. API tokens aren't an alternative either: Zendesk is removing them in phases through 2027-04-30, and accounts created on or after 2026-07-28 can't create them at all.
Note the attribution caveat from Zendesk's docs: actions performed with a client credentials token are attributed to the Zendesk user who created the OAuth client. If attribution matters, the OAuth client should be owned by a dedicated service account.
Plan/Design
Replace AI_ZENDESK_OAUTH_TOKEN with AI_ZENDESK_OAUTH_CLIENT_ID / AI_ZENDESK_OAUTH_CLIENT_SECRET.
Mint on demand against https://{subdomain}.zendesk.com/oauth/tokens with grant_type=client_credentials and scope=tickets:write users:write, cache the token until shortly before expires_in elapses, and re-mint on a 401.
Give the failure an operator-visible signal rather than a log line only — the current behavior is a silent per-request failure that looks identical to the bot deciding not to file.
Activity
zamanafzal commentedon Oct 7, 2026
Gap is already written up in the Additional Context section of #98, which is still a draft. No value in tracking them separately.