Every subcommand of case-calendar. All commands accept -c / --config to
point at a non-default config file:
uv run case-calendar -c production.yaml sync
sync — pull updates from CourtListener
Backfills new docket entries for every case in config.yaml, extracts
hearings and deadlines via the LLM, and re-emits affected calendars.
uv run case-calendar sync
| Flag | Purpose |
|---|---|
--case <case_id> |
Sync only this one case. |
--only-new |
Sync only cases whose dockets aren’t yet in the store — useful after adding new cases to config.yaml without remembering their ids. |
--no-emit |
Skip the auto-emit at the end of the sync. Rare; mostly for tests. |
--force-summaries |
Regenerate every AI case summary in the same sync. Use after a model or prompt change so the CourtListener session is reused instead of running summarize --force separately. |
A three-tier short-circuit keeps quiet days cheap: the docket-level
date_modified watermark, the per-entry modified_after filter, and the
content fingerprint dedup mean an unchanged docket costs roughly one cheap
CourtListener request and zero LLM calls.
Run on a cron once you’re past the initial backfill — every five minutes is fine; once an hour is plenty for most cases. Or skip cron entirely and use real-time webhooks.
serve — real-time webhook receiver
Runs an HTTPS-ready webhook receiver. CourtListener pushes DOCKET_ALERT
events to it, and each delivery processes a single entry and re-renders
just the affected calendar in seconds. Bypasses the daily polling quota
entirely.
uv run case-calendar serve --host 127.0.0.1 --port 8000
| Flag | Purpose |
|---|---|
--host <addr> |
Bind address. Default 127.0.0.1. |
--port <n> |
Bind port. Default 8000. |
The receiver is HTTP, not HTTPS — put it behind Caddy / nginx / Cloudflare Tunnel for the public-facing TLS. See real-time webhooks for the full registration walk-through.
emit — force-render calendars
Re-renders every ICS file (and pushes to gcal / M365 if configured) without
pulling new data. Useful after editing config.yaml or hand-fixing a row in
the store.
uv run case-calendar emit
No flags. Always operates on all calendars.
setup gcal / setup m365 — one-time OAuth
Runs the interactive OAuth flow for a push backend and caches the refresh token to disk.
uv run case-calendar setup gcal
uv run case-calendar setup m365
Run once per machine. Both flows open a browser, ask you to grant permission,
and write the token cache to the path configured by google_token_path /
m365_token_path. Subsequent runs of sync / serve / emit auto-push to
that backend with no flag.
See calendar backends for the Cloud Console / Entra app registration steps that precede these.
summarize — AI case summaries (opt-in)
Generates per-docket AI prose summaries for the index page. Gated on
case_summaries.enabled: true in config.yaml. Most users won’t need to
run this manually — summaries auto-refresh during sync and serve when
a new primary document or disposition lands.
uv run case-calendar summarize
| Flag | Purpose |
|---|---|
--case <case_id> |
Summarize only this one case. |
--force |
Regenerate even when a summary row already exists. Use after a model or prompt change. |
--no-emit |
Skip the index.html re-emit after writing. |
See case summaries.
show — dump current state
Prints every hearing and deadline currently in the store, grouped by case. Read-only — useful for sanity checks.
uv run case-calendar show
uv run case-calendar show --case us-v-wang
| Flag | Purpose |
|---|---|
--case <case_id> |
Limit to one case. |
prune — clean up orphaned data
After you remove a case (or one of its dockets) from config.yaml, the
hearings, deadlines, entry-fingerprint cache, and AI summaries tied to the
deleted docket would otherwise live forever in the store. prune deletes
them. Dry-run by default — pass --apply to actually delete.
uv run case-calendar prune # dry-run: print what would be deleted
uv run case-calendar prune --apply # actually delete
Back up data/case-calendar.sqlite (and its -wal / -shm sidecar files,
or run PRAGMA wal_checkpoint(TRUNCATE) first) before applying.
webhook-url — print the receiver URL
Composes the URL to paste into the CourtListener webhook dashboard. Uses
CASE_CALENDAR_WEBHOOK_SECRET from .env.
uv run case-calendar webhook-url --host webhook.example.com
# https://webhook.example.com/webhooks/case-calendar/<your-secret>
| Flag | Purpose |
|---|---|
--host <host> |
Public host where the receiver is reachable. https:// is assumed unless you pass an explicit http:// URL. |
--check |
After printing the URL, probe the receiver’s secret-gated health endpoint to verify the host is reachable, case-calendar is the service answering, and the secret in .env matches the one the running receiver expects. |
--check is the single command that catches the common deployment
failure modes: a Cloudflare access policy intercepting the path, a stale
Caddy config pointing at the wrong port, or a secret mismatch between the
config file and the running daemon.
Global flag
| Flag | Purpose |
|---|---|
-c <path>, --config <path> |
Use a different config file. Default config.yaml. |
Environment variables
| Variable | Purpose |
|---|---|
COURTLISTENER_TOKEN |
Required. Your CourtListener API token. |
ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEY |
Set at least one for the extractor pipeline. The tool auto-detects which provider to use. |
LLM_PROVIDER |
Optional. Force a specific extractor provider (anthropic / openai / gemini). |
LLM_MODEL |
Optional. Override the extractor model. |
LLM_SUMMARY_PROVIDER |
Optional. Force a specific summary-pipeline provider. |
LLM_SUMMARY_MODEL |
Optional. Override the summary model. |
CASE_CALENDAR_WEBHOOK_SECRET |
Required for serve and webhook-url. A long random string included in the webhook URL — CourtListener has no signing mechanism, so the URL secret is the auth model. |
M365_CLIENT_ID |
Alternative to m365_client_id in config.yaml. |
LOG_LEVEL |
Optional. DEBUG for verbose output; default INFO. |
Environment variables are read from .env in the project root via
python-dotenv before any module touches the environment.