Routines fire API: newly generated routine tokens rejected (401) / expire within minutes; tokens issued before ~2026-07-16 unaffected

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 17, 2026

Summary

Routine API tokens generated via the claude.ai/code/routines web UI on 2026-07-17 behave completely differently from tokens generated on 2026-07-14, with no announced change:

  • Old tokens (minted 2026-07-14) kept authenticating successfully on POST /v1/claude_code/routines/{id}/fire until at least 2026-07-17 06:03 UTC (a fire succeeded).
  • Fresh tokens (minted 2026-07-17) are rejected with 401 Authentication failed on that same endpoint from the moment they are minted, and appear to expire entirely within ~10 minutes (evidence below).

The current platform docs ("Trigger a routine through the API") still describe these as long-lived per-routine bearer tokens revoked only by regeneration. Observed behavior contradicts this.

Environment

  • External relay: Google Apps Script (UrlFetchApp) → POST https://api.anthropic.com/v1/claude_code/routines/{routine_id}/fire
  • Headers: Authorization: Bearer <routine token>, anthropic-beta: experimental-cc-routine-2026-04-01, anthropic-version: 2023-06-01, Content-Type: application/json
  • Body: {"text": "<short message>"}
  • This exact setup worked end-to-end on 2026-07-14 (multiple successful fires, status 200) and intermittently until 2026-07-17 06:03 UTC with the 2026-07-14 tokens.

Empirical timeline (all times UTC, 2026-07-17)

| Time | Token generation | Endpoint | Result |
|---|---|---|---|
| 06:03 | minted 2026-07-14 | old /fire | ✅ fired successfully (last success of an old-generation token) |
| 06:48 | minted 06:42 (fresh) | old /fire | 401 Authentication failed (request_id req_011Cd6swEQBfehNxcUynRgvW earlier same-day example; later: req_011Cd7HB4STxDoPzNirmKsWb) |
| 07:43 | minted 06:38 (fresh, 65 min old) | old /fire | 401 Authentication failed (request_id req_011Cd7Fku1QWnGCJu3PAqAxd) |
| 07:56 | minted 06:38 | new /v1/code/triggers/{id}/run + same beta header | 401 "OAuth access token has been revoked." (so this surface recognizes the token type and reports it revoked) |
| 07:59:11 | re-minted (fresh) | — | — |
| 08:02 (T+3min) | minted 07:59 | new /run + beta | 404 structured not_found — i.e. the token PASSED authentication |
| 08:08 (T+9min) | minted 07:59 (same token, same request) | new /run + beta | 401 Authentication failed — the token died within ~9 minutes with no revocation action by anyone |

Additional probes at 08:08 with the (by-then-dead) fresh token:

  • POST /v1/code/triggers/{id}/fire → plain-text 404 page not found (route does not exist)
  • GET /v1/code/triggers/{id} (Bearer only) → 401
  • old /fire without beta headers → 401

What this looks like

Newly-issued routine tokens appear to be short-lived OAuth access tokens (lifetime measured in minutes), while pre-change tokens were long-lived. The legacy /v1/claude_code/routines/{id}/fire endpoint rejects the new-style tokens entirely (generic Authentication failed), while /v1/code/triggers/{id}/run authenticates them during their brief lifetime. No refresh endpoint is documented for external callers, so an external relay (GAS, webhook bridges, etc.) currently has no way to keep firing routines.

Questions

  1. Is this an intentional token-model migration or a regression?
  2. If intentional: what is the current recommended pattern for an external system (e.g. a webhook relay) to fire a routine with arbitrary text? Is there a long-lived credential or a documented refresh flow?
  3. If a regression: is there a tracking incident? Happy to provide the request_ids above for correlation.

Docs referenced: platform docs "Trigger a routine through the API" (states tokens are shown once, revoked only by regeneration — no expiry mentioned).

View original on GitHub ↗