Public Status Widget
We have added a public endpoint where you can access the status of your status
page. To access it, you only need the unique :slug
you have chosen for your
page.
curl https://api.openstatus.dev/public/status/:slug
The response is a JSON object with the following structure:
{ "status": "operational" }
in which the status can be one of the following values:
enum Status { Operational = "operational", DegradedPerformance = "degraded_performance", PartialOutage = "partial_outage", MajorOutage = "major_outage", UnderMaintenance = "under_maintenance", // currently not in use Unknown = "unknown", Incident = "incident",}
How does it work?
The status is calculated by the ratio uptime / (uptime + downtime)
of the last
5 cron jobs of each monitor which we accumulate together. It is a simple
calculation that gives us a good overview of the status of your services.
function getStatus(ratio: number) { if (isNaN(ratio)) return Status.Unknown; if (ratio >= 0.98) return Status.Operational; if (ratio >= 0.6) return Status.DegradedPerformance; if (ratio >= 0.3) return Status.PartialOutage; if (ratio >= 0) return Status.MajorOutage; return Status.Unknown;}
We are caching the result for 30 seconds
to reduce the load on our database.
The Status.Incident
will always be returned when then status of any incident
on your page is not “monitoring” or “resolved”. You can attach an
incident to a monitor (implicit) or a page (explicit).
If you have a doubt about the above calculation, feel free to contact us via ping@openstatus.dev or discord.
React Widget
We currently do not provide any SDK or package to integrate the status into your stack. But will in the future.
If you are using Nextjs and RSC with the /app
directory, feel free to use that
Component. Small reminder that we are using shadcn ui and tailwindcss. You might
want to update the bg-muted
and text-foreground
classes to your needs.
We are using zod
to validate the response. You can use any other library if
you want or just remove it. But better be safe than sorry.
import * as z from "zod";
const statusEnum = z.enum([ "operational", "degraded_performance", "partial_outage", "major_outage", "under_maintenance", "unknown", "incident",]);
const statusSchema = z.object({ status: statusEnum });
const dictionary = { operational: { label: "Operational", color: "bg-green-500", }, degraded_performance: { label: "Degraded Performance", color: "bg-yellow-500", }, partial_outage: { label: "Partial Outage", color: "bg-yellow-500", }, major_outage: { label: "Major Outage", color: "bg-red-500", }, unknown: { label: "Unknown", color: "bg-gray-500", }, incident: { label: "Incident", color: "bg-yellow-500", }, under_maintenance: { label: "Under Maintenance", color: "bg-blue-500", },} as const;
export async function StatusWidget({ slug }: { slug: string }) { const res = await fetch(`https://api.openstatus.dev/public/status/${slug}`, { next: { revalidate: 60 }, // cache request for 60 seconds }); const data = await res.json(); const parsed = statusSchema.safeParse(data);
if (!parsed.success) { return null; }
const key = parsed.data.status; const { label, color } = dictionary[key];
return ( <a className="border-border text-foreground/70 hover:bg-muted hover:text-foreground inline-flex max-w-fit items-center gap-2 rounded-md border px-3 py-1 text-sm" href={`https://${slug}.openstatus.dev`} target="_blank" rel="noreferrer" > {label} <span className="relative flex h-2 w-2"> {parsed.data.status === "operational" ? ( <span className={`absolute inline-flex h-full w-full animate-ping rounded-full ${color} opacity-75 duration-1000`} /> ) : null} <span className={`relative inline-flex h-2 w-2 rounded-full ${color}`} /> </span> </a> );}
Then, import your StatusWidget.tsx
component to your layout as well as the slug prop to your specific monitor. Ideally, relative imports would be setup in your tsconfig.json
.
import StatusWidget from '../../components/StatusWidget.tsx
...
<StatusWidget slug="monitor-slug-here" />
Astro Widget
---import * as z from "zod"
const statusEnum = z.enum([ "operational", "degraded_performance", "partial_outage", "major_outage", "under_maintenance", "unknown", "incident",])
const statusSchema = z.object({ status: statusEnum })
const dictionary = { operational: { label: "Operational", color: "bg-green-500", }, degraded_performance: { label: "Degraded Performance", color: "bg-yellow-500", }, partial_outage: { label: "Partial Outage", color: "bg-yellow-500", }, major_outage: { label: "Major Outage", color: "bg-red-500", }, unknown: { label: "Unknown", color: "bg-gray-500", }, incident: { label: "Incident", color: "bg-yellow-500", }, under_maintenance: { label: "Under Maintenance", color: "bg-blue-500", },}
const slug = Astro.props.slug
const res = await fetch(`https://api.openstatus.dev/public/status/${slug}`, { // @ts-ignore next: { revalidate: 60 },})const data = await res.json()const parsed = statusSchema.safeParse(data)if (!parsed.success) { return null}const key = parsed.data.statusconst { label, color } = dictionary[key]---
<a class="border-border text-foreground/70 hover:bg-muted hover:text-foreground inline-flex max-w-fit items-center gap-2 rounded-md border px-3 py-1 text-sm" href={`https://${slug}.openstatus.dev`} target="_blank" rel="noreferrer"> {label} <span class="relative flex h-2 w-2"> { parsed.data.status === "operational" ? ( <span class:list={[ "absolute", "inline-flex", "h-full", "w-full", "animate-ping", "rounded-full", color, "opacity-75", "duration-1000", ]} /> ) : null } <span class:list={[ "relative", "inline-flex", "h-2", "w-2", "rounded-full", color, ]}></span> </span></a>
Then, import your StatusWidget.astro
component as well as the slug from your specific monitor to your layout, most likely your footer. It would be better to have relative imports setup in your tsconfig.json.
---import StatusWidget from '../../components/StatusWidget.astro'---
<StatusWidget slug="monitor-slug-here" />