Circle V2 API Docs
    Preparing search index...

    On-Call

    The rotation is 24/7. This page is what you need when something is wrong in production: how bad it is, where to look, which levers exist, and who to pull in. Symptom-specific playbooks live in on-call/.

    Role Who How to reach
    Primary on-call see rotation schedule
    Secondary / backup
    Engineering lead
    Customer contact (facility comms)
    Privacy / compliance (PHI exposure)

    Hand off with a short written note in the on-call channel: open incidents, anything you changed (flags, concurrency, deleted deployments), and what to watch.

    Severity decides whether you wake someone up. When unsure, pick the higher one; downgrading later is free.

    Sev Examples Response
    SEV1 App down or login broken for everyone. PHI shown to the wrong org or user. Chart reviews failing across all customers. A migration left prod half-applied. Now, any hour. Page the secondary. Post in the on-call channel.
    SEV2 One integration's sync failing. One queue topic 504ing or backed up. Uploads broken for one facility. Within the hour. Overnight, stabilize (flag off, lower concurrency) and hand the root cause to the morning.
    SEV3 A cosmetic bug, a slow page, one stuck run. Next business day. File a Linear issue.

    Any suspected PHI exposure is SEV1 regardless of scale. Stop the exposure first, then contact privacy/compliance. Do not paste patient names, DOBs, MRNs, or chart text into Slack, Linear, or PRs — refer to records by ID.

    1. Acknowledge in the on-call channel so nobody duplicates you.
    2. Check whether it's us. Vercel status (alerts already post to Slack), Supabase status, and the EHR vendor if the symptom is sync-only.
    3. Check whether it started with a release. Compare the start time against the latest GitHub Release and the Vercel production deployment. A release-aligned start goes to bad release or migration.
    4. Find the error. Datadog logs (service/env tags), then Datadog APM traces for the failing route or jobbable. Traces carry organization.id, patient.id, auditRun.id, and friends as span attributes, so filter by those.
    5. Stabilize before you fix. A flag off, a lowered concurrency, or a rollback beats a clever hotfix at 3am.
    Question Source Caveat
    What errored? Datadog logs Logs go stdout → Vercel drain → Datadog with no flush in the queue handler. Under load they are sampled and dropped.
    How many jobs ran / failed? Datadog APM traces Traces force-flush, so trust them over log counts.
    Did a chart review actually happen? audit_runs table The source of truth. audit_runs has no entity_id; join cases → patient.
    Did a job fail? job_executions.status = 'failed' A job killed by a 504 never reaches its catch, so it stays running. Rows past expires_at are pruned hourly; a missing row is not a signal.
    What did the LLM do? LangSmith Prompt tag follows VERCEL_ENV.
    Is the queue backed up? Vercel dashboard → Queues Messages are pinned to the deployment that enqueued them — see queue storm.
    What is the browser seeing? Datadog RUM Off when NEXT_PUBLIC_DISABLE_DATADOG=true.

    There is no health-check endpoint. The hourly /api/cron/test cron enqueues a test job; if its trace stops appearing, the queue pipeline is down.

    There is no global "stop syncing" or "stop reviews" switch. These are what exist, roughly fastest first.

    Lever Where Takes effect
    LaunchDarkly flags LaunchDarkly UI; registry in apps/web/feature-flags.d.ts Immediately. Operational ones: chartReviewBatchingEnabled, chartReviewBatchSize, triggeredChartReviewResumeEnabled, and per-entity canSyncInsurances. None of them stops sync or reviews outright.
    Vercel instant rollback Vercel → Deployments → promote previous Immediately for requests. Does not move queued messages and does not undo migrations.
    Delete a deployment Vercel → Deployments Immediately. Kills every queued message pinned to it. The emergency stop for a queue storm.
    Queue maxConcurrency / retryAfterSeconds apps/web/vercel.json functions triggers Next deploy, and only for messages that deploy enqueues.
    Stale-run windows AUDIT_RUNS_STALE_MINUTES, SESSION_NOTES_STALE_MINUTES env Next deploy.

    If LaunchDarkly is unreachable, every flag silently takes its code default (packages/feature-flags/src/server.ts). "Flags look wrong everywhere at once" can mean LaunchDarkly is down, not that someone flipped them.

    • Superadmin → Audit runs: "Re-run all questions" requeues a run with forceRun, past the dedup window. Only pending questions re-run, so it is safe to repeat.
    • Superadmin → Automation runs: retry failed RPA (InSync/CloudCruise) and Extend runs.
    • Cleanup crons (every 5 min): fail stale audit runs, session notes, and form runs (60 minutes by default for the first two). A stuck run that turns failed by itself is this, not a new error.
    • @repo/backfills CLIs: pnpm --filter @repo/backfills <script>. Dry-run first where the script supports it. Never re-run a full backfill to "fix" one record — it clobbers.
    • Production deploys from a published GitHub Release (.github/workflows/release.yaml): prod migrations run first, then the Vercel production build.
    • Migrations are forward-only. A bad migration is fixed by a new idempotent migration or a hand-applied fix, never a rollback.
    • Rolling the app back past a migration is only safe if the old code tolerates the new schema. Additive migrations are fine; renames and drops are not.
    • Index builds on large tables (patient_charts, job_executions) are operations, not migrations. Run them CONCURRENTLY, off-peak, by hand.

    Don't improvise here at night. Stabilize and escalate.

    • EHR clients in packages/legacy/src/integrations/ — vendor rate limits. Never "resync everything".
    • Trigger keys for triggered chart reviews — they are an idempotency contract with v1; changing one re-runs every review v1 already did.
    • RLS, auth middleware, session/cookie logic.

    Within two business days, write a short blameless note: timeline, impact (orgs and run counts, no PHI), root cause, what stopped it, and follow-up Linear issues. If the playbook was wrong or missing, fixing it is part of closing the incident.

    None of these exist yet; each is a gap an on-call engineer currently covers by noticing.

    • 5xx rate on /api/queue/* per topic, alerting on a sustained rise.
    • 504s served by a deployment that is not current production.
    • audit_runs stuck in running for longer than the stale window.
    • Absence of the hourly test job trace.
    • Supabase CPU and disk I/O.
    • LaunchDarkly init failures at boot.