Plainwork

Plainwork / Status Endpoint Atlas / Block a deploy when a vendor is down

Block a deploy when a vendor status page says they are down

Deploying while GitHub, your cloud host or your payment provider is mid-outage is how a normal release turns into a long night. Most vendor status pages run on Atlassian Statuspage or a compatible service, and they all serve the same machine-readable summary at /api/v2/summary.json. This page gives you a 25-line Node script that reads a few of them and exits with code 1 when any is in a major outage, so a CI job or a shell script can stop before it ships. It needs Node 18 or newer and no packages.

The script

Save it as vendor-gate.mjs. It was run on 2026-10-14 against 60 hosts from the Status Endpoint Atlas; it printed BLOCK for two that were in a major state at that moment (Aiven and Cloudflare) and ok for the rest.

// vendor-gate.mjs: exit 1 if any vendor status page is not healthy. Node 18+, no dependencies.
// Usage: node vendor-gate.mjs www.githubstatus.com status.npmjs.org
const hosts = process.argv.slice(2);
if (!hosts.length) { console.error("usage: node vendor-gate.mjs <status-host> [...]"); process.exit(2); }
const RANK = { none: 0, maintenance: 0, minor: 1, major: 2, critical: 3 };
let bad = 0;
await Promise.all(hosts.map(async (h) => {
  try {
    const r = await fetch(`https://${h}/api/v2/summary.json`, { signal: AbortSignal.timeout(10000) });
    if (!r.ok) throw new Error(`HTTP ${r.status}`);
    const j = await r.json();
    const ind = j?.status?.indicator;
    if (!(ind in RANK)) throw new Error(`unknown indicator ${ind}`);
    const open = (j.incidents || []).map((i) => i.name);
    const down = (j.components || []).filter((c) => c.status === "major_outage").map((c) => c.name);
    const fail = RANK[ind] >= 2 || down.length > 0;
    console.log(`${fail ? "BLOCK" : "ok   "} ${h} indicator=${ind} open_incidents=${open.length} major_outage_components=${down.length}`);
    if (fail) bad++;
  } catch (e) {
    console.log(`UNKNOWN ${h}: ${e.message} (treated as not blocking; change if you prefer)`);
  }
}));
process.exit(bad ? 1 : 0);

How it decides

  • Blocks when the page indicator is major or critical, or when any single component is in major_outage. A minor indicator, degraded_performance or partial_outage does not block, because on large vendors something is nearly always slightly degraded. Change RANK[ind] >= 2 to >= 1 if you want a stricter gate.
  • Maintenance is not an outage. The indicator maintenance and the component state under_maintenance do not block. See the status values reference for every string and what it means.
  • Unknown is not green. A 404, a timeout or an unexpected indicator prints UNKNOWN and does not block here, because a broken status page should not freeze every release. If a vendor is critical to the deploy, change the catch branch to count it in bad.
  • Whole-vendor outages are rare, component outages are common. On a large page, one region or one API can be in major_outage while the product you use is fine (Cloudflare lists hundreds of components, one per data centre). For big vendors, filter the components to the ones you depend on, for example c.name.includes("Workers").

Use it

Locally or in any shell, before a release step:

node vendor-gate.mjs www.githubstatus.com status.npmjs.org www.vercel-status.com && ./deploy.sh

In GitHub Actions, as the first step of a deploy job (save the script in your repo, for example under scripts/):

- uses: actions/setup-node@v4
  with: { node-version: 20 }
- run: node scripts/vendor-gate.mjs www.githubstatus.com status.npmjs.org

A non-zero exit fails the job, and the log shows which host and how many open incidents. Remember that a status page is often updated some minutes after an outage starts, so this gate catches declared incidents, not all of them.

Finding the right host for each vendor

The host is not always status.vendor.com: GitHub is www.githubstatus.com, Vercel is www.vercel-status.com, Netlify is www.netlifystatus.com. The Status Endpoint Atlas lists 246 verified hosts with their summary.json URL, component counts, CORS header and feed availability, and the API guide explains the format and its quirks. A free browser checker tests a single host.

Poll politely: a deploy gate makes one request per host per run, which is fine; a dashboard should cache and poll at 60 seconds or slower.

More from the catalogue

Other small tools

See everything →