{"id":51317000,"url":"https://github.com/tiennm99/claude-code-routine-trigger-worker","last_synced_at":"2026-07-01T08:30:58.089Z","repository":{"id":356663469,"uuid":"1233543005","full_name":"tiennm99/claude-code-routine-trigger-worker","owner":"tiennm99","description":"Cloudflare Worker firing Claude Code routines on cron via /fire API. Free tier, no servers. See -trigger \u0026 -cron.","archived":false,"fork":false,"pushed_at":"2026-05-23T11:42:50.000Z","size":130,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-23T13:17:14.954Z","etag":null,"topics":["anthropic","claude-code","claude-code-routines","cloudflare-workers","cron","free-tier","javascript","routine-trigger","scheduler","serverless"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tiennm99.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-09T04:30:43.000Z","updated_at":"2026-05-23T11:42:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tiennm99/claude-code-routine-trigger-worker","commit_stats":null,"previous_names":["tiennm99/claude-code-routine-trigger-worker"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tiennm99/claude-code-routine-trigger-worker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiennm99%2Fclaude-code-routine-trigger-worker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiennm99%2Fclaude-code-routine-trigger-worker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiennm99%2Fclaude-code-routine-trigger-worker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiennm99%2Fclaude-code-routine-trigger-worker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tiennm99","download_url":"https://codeload.github.com/tiennm99/claude-code-routine-trigger-worker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiennm99%2Fclaude-code-routine-trigger-worker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34999790,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anthropic","claude-code","claude-code-routines","cloudflare-workers","cron","free-tier","javascript","routine-trigger","scheduler","serverless"],"created_at":"2026-07-01T08:30:56.051Z","updated_at":"2026-07-01T08:30:58.079Z","avatar_url":"https://github.com/tiennm99.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# claude-code-routine-trigger-worker\n\nCloudflare Worker that fires a [Claude Code routine](https://code.claude.com/docs/en/routines) on a precise cron schedule via the [`/fire` API](https://platform.claude.com/docs/en/api/claude-code/routines-fire). Runs on Cloudflare's free tier — no servers, no GitHub minutes, no Docker host.\n\n\u003e [!TIP]\n\u003e **Anthropic's routine editor now ships a built-in cron trigger** — it runs on Anthropic's infra, no setup. Use it first.\n\u003e\n\u003e If you need an external scheduler (Anthropic cron disabled, custom payloads, audit trail outside Anthropic), I recommend **[cron-job.org](https://cron-job.org)** — free, no infra, ~2-min setup. I tried it on this exact routine and am satisfied, so I've parked this worker (cron triggers are commented out in `wrangler.toml`) to keep my CF cron-trigger quota free for other projects. See [Recommended: cron-job.org](#recommended-cron-joborg).\n\u003e\n\u003e This worker is still a valid path if you specifically want secrets in CF / logs in CF / schedule in code review. See [Using Cloudflare Workers](#using-cloudflare-workers).\n\n## Recommended: cron-job.org\n\n[cron-job.org](https://cron-job.org) is a free hosted cron service with a clean dashboard, per-fire history, and 1-min granularity on the free tier. Setup:\n\n1. Sign up at [console.cron-job.org/signup](https://console.cron-job.org/signup).\n2. Click **CREATE CRONJOB**.\n3. **Common** tab:\n   - **Title**: anything (e.g. `claude-code-routine`)\n   - **URL**: paste the `/fire` URL from the Anthropic routine editor → *API trigger* → *URL* (looks like `https://api.anthropic.com/v1/claude_code/routines/trig_.../fire`)\n   - **Schedule**: pick your timezone and the times to fire — cron-job.org accepts both UI selectors and raw cron syntax\n4. **Advanced** tab → set **Request method** to `POST`.\n5. **Headers** tab → add four headers:\n\n   | Key                 | Value                                          |\n   | ------------------- | ---------------------------------------------- |\n   | `Authorization`     | `Bearer sk-ant-oat01-...` (your routine token) |\n   | `anthropic-version` | `2023-06-01`                                   |\n   | `anthropic-beta`    | `experimental-cc-routine-2026-04-01`           |\n   | `Content-Type`      | `application/json`                             |\n\n6. **Body** tab → select `Raw` and paste:\n   ```json\n   {\"text\": \"Scheduled trigger\"}\n   ```\n7. **Notifications** tab (optional) → enable email on failure so you know if the token expires.\n8. **Save**.\n\nEach fire shows up in *History* with status code and response body — open `claude_code_session_url` from the response JSON to watch the run.\n\n**Operational notes:**\n- **Token rotation**: edit the cronjob → swap the `Authorization` header value. No redeploy.\n- **Beta header**: when Anthropic ships a new dated `anthropic-beta` value, update the header. Older dated values keep working for a transition window per Anthropic's beta policy.\n- **No retry on failure**: each `/fire` POST creates a new Claude Code session, so retrying would multiply sessions and burn quota. cron-job.org's default is one attempt per fire, which is what you want.\n- **Limits**: cron-job.org's free tier allows up to 50 cronjobs and unlimited executions at 1-min granularity — way more than enough for routine triggering.\n\n## Why this vs the siblings\n\n|                  | [claude-code-routine-trigger](https://github.com/tiennm99/claude-code-routine-trigger) | [claude-code-routine-cron](https://github.com/tiennm99/claude-code-routine-cron) | **claude-code-routine-trigger-worker** (this) |\n| ---------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------- |\n| Runs on          | GitHub Actions runners                                                                 | your infra (Docker host, k8s, NAS, RPi)                                          | Cloudflare edge                               |\n| Cost             | free (within GitHub minutes)                                                           | minimal (your infra)                                                             | free (CF free tier)                           |\n| Cron precision   | ±30 min – 2 h, can drop runs                                                           | sub-second                                                                       | within ~15 sec                                |\n| Setup            | fork + 2 repo secrets                                                                  | env vars + Docker                                                                | `wrangler deploy` + 2 secrets                 |\n| Audit trail      | GitHub Actions runs page                                                               | container stdout                                                                 | CF dashboard / `wrangler tail`                |\n\n## Using Cloudflare Workers\n\n\u003e The default `wrangler.toml` ships with the `[triggers]` block **commented out** — see the note in the file. To activate this worker, uncomment the block (and edit the schedule) before deploying.\n\n### Quickstart\n\n```bash\ngit clone https://github.com/tiennm99/claude-code-routine-trigger-worker\ncd claude-code-routine-trigger-worker\npnpm install\n\n# Upload secrets (from Anthropic routine editor → API trigger)\necho -n 'https://api.anthropic.com/v1/claude_code/routines/trig_.../fire' \\\n  | npx wrangler secret put ROUTINE_FIRE_URL\necho -n 'sk-ant-oat01-...' \\\n  | npx wrangler secret put ROUTINE_FIRE_TOKEN\n\n# Uncomment [triggers].crons in wrangler.toml, edit the schedule + timezone, then:\nnpx wrangler deploy\n```\n\nTail logs:\n\n```bash\nnpx wrangler tail\n```\n\nA successful fire logs a JSON line with `session_url`. Open it to watch the run.\n\n### Environment variables\n\nConfigured in `wrangler.toml` (`[vars]` for plain values) or via `wrangler secret put` (for secrets).\n\n| Name                 | Type   | Required | Default                                  | Notes |\n| -------------------- | ------ | :------: | ---------------------------------------- | ----- |\n| `ROUTINE_FIRE_URL`   | secret | yes      | —                                        | Anthropic `/fire` endpoint. From routine editor → API trigger. |\n| `ROUTINE_FIRE_TOKEN` | secret | yes      | —                                        | `sk-ant-oat01-...` per-routine token. Shown once in the editor. |\n| `TEXT_TEMPLATE`      | var    | no       | `Scheduled trigger at {LocalTime}`       | Token-substitution template. See *Templates*. |\n| `TZ`                 | var    | no       | `UTC`                                    | IANA tz name (`Asia/Ho_Chi_Minh`, `America/New_York`, …). Used for `{LocalTime}` formatting. |\n\n### Customize the schedule\n\nUncomment and edit `wrangler.toml` `[triggers].crons` — Cloudflare requires literal cron expressions (same constraint as GitHub Actions). To change when the routine fires:\n\n```toml\n[triggers]\ncrons = [\n  \"0 0-17,22-23 * * *\", # UTC+7: 00:00 + 05:00..23:00 hourly\n]\n```\n\nThen redeploy: `npx wrangler deploy`.\n\nTips:\n- Cron runs in **UTC**. Convert your local time: `UTC = local − offset` (e.g. 09:00 UTC+7 → 02:00 UTC → `0 2 * * *`).\n- Validate expressions at \u003chttps://crontab.guru/\u003e.\n- **Free tier limit:** 5 cron expressions per worker. Default config uses 1.\n- Standard 5-field syntax — supports `*`, `,`, `-`, `/`. Comma lists (`22,23,0,1,2`) and ranges (`3-7`) let you cram many fires into one expression.\n\n### Templates\n\n`TEXT_TEMPLATE` supports these `{Token}` substitutions, rendered per fire. Unknown tokens are left intact in the output.\n\n| Token         | Example                                |\n| ------------- | -------------------------------------- |\n| `{ISO}`       | `2026-05-09T03:19:00.000Z`             |\n| `{LocalTime}` | `2026-05-09 10:19 GMT+7`               |\n| `{Cron}`      | `19 3 * * *` — the expression that fired |\n\nExample:\n\n```toml\n[vars]\nTEXT_TEMPLATE = \"Daily digest at {LocalTime} (cron {Cron})\"\nTZ = \"America/New_York\"\n```\n\n### Local development\n\nCopy `.dev.vars.example` to `.dev.vars` and fill in your routine credentials:\n\n```bash\ncp .dev.vars.example .dev.vars\n# edit .dev.vars\n\nnpx wrangler dev --test-scheduled\n```\n\nThen trigger a scheduled run:\n\n```bash\ncurl \"http://localhost:8787/__scheduled?cron=*+*+*+*+*\"\n```\n\n`.dev.vars` is gitignored — never commit it.\n\n### Tests\n\n```bash\npnpm test\n```\n\nVitest runs in the Workers runtime via `@cloudflare/vitest-pool-workers`. Tests mock `fetch`, so they consume no Anthropic quota.\n\n### Secret rotation\n\n`wrangler secret put` overwrites silently. Rotate by re-running it with the new value:\n\n```bash\necho -n 'sk-ant-oat01-newvalue' | npx wrangler secret put ROUTINE_FIRE_TOKEN\n```\n\nThe change takes effect on the next deploy or within a few seconds via the live config.\n\n### Beta header\n\nThe request pins `anthropic-beta: experimental-cc-routine-2026-04-01` (constant in `worker.js`). When Anthropic ships a new dated beta, bump it via a release. Older dated values keep working for a transition window per Anthropic's beta policy.\n\n### Operational notes\n\n- **Time accuracy**: cron precision on CF Workers is within ~15 seconds — adequate for routine triggering.\n- **No retry**: each `/fire` POST creates a new Claude Code session — retrying multiplies sessions and burns quota. The worker logs failures and moves on.\n- **Logs / traces**: `[observability]` is enabled in `wrangler.toml` with `head_sampling_rate = 1` and `invocation_logs = true` — every invocation produces a structured log + trace, retained per the [Workers Logs retention policy](https://developers.cloudflare.com/workers/observability/logs/workers-logs/) (3 days on the free plan). View live with `npx wrangler tail`, or browse history in the CF dashboard under **Workers \u0026 Pages → your worker → Logs**.\n- **Cost**: scheduled handlers count against the Workers Free plan's 100k requests/day budget. 5 daily fires × 30 days = 150 requests/month — negligible.\n\n### Security\n\n- The token is **per-routine**: a leak only fires that one routine.\n- Secrets live in CF's encrypted secret store, never in the bundle, never in logs (verified by tests).\n- TLS to `api.anthropic.com` uses CF's standard cert verification.\n- `worker.js` has no `fetch` HTTP handler — the worker is unreachable from the public internet, only fires on cron tick.\n\n## License\n\n[Apache-2.0](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiennm99%2Fclaude-code-routine-trigger-worker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftiennm99%2Fclaude-code-routine-trigger-worker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiennm99%2Fclaude-code-routine-trigger-worker/lists"}