# Status pages

A status page shows your customers what your monitors are measuring, on an address you control. This
guide takes an organization from nothing to a public page on its own hostname, such as
`status.acme.io`. For each step it gives the dashboard path in bold with the REST route beside it,
who can take the step, what you will see while Bucker waits on DNS or a certificate and for how long,
and what to check when it stalls.

Three steps are required: create the page, add what it watches, and have an owner publish it. A logo,
your own hostname and sharing are optional, and the hostname is the step with the most waiting in it.
Each section below stands on its own, so a person can start anywhere and an agent that fetches this
page with `get_doc` can read it one section at a time.

## What a status page is

A page belongs to an organization, not to a project, so it can show components from several projects.

- **Components** are the things your customers recognise, such as "Checkout API". A component's status
  is measured, not typed in: it comes from the monitors bound to it, and the worst status among them
  wins. A component whose monitors have not reported yet shows **No data**, never Operational.
- **Incidents.** When a component's status turns bad, Bucker drafts an incident for it and tells your
  organization's notification channels, if any. A draft is visible only inside your organization until
  a person publishes it. Auto-publishing is an owner-only setting, off by default, and the page says so
  when no person wrote an incident.
- **Maintenance** windows are scheduled from the dashboard or the API and shown on the page.
- **Visibility** is **Private** (the default: only your organization can see the page),
  **Password protected** (no web page yet: readers exchange the password for an API token) or
  **Public**.

Until a page has its own hostname it is served at `https://status.bucker.io/<orgSlug>/<pageSlug>`.
That site has no database of its own and reads a snapshot that Bucker regenerates whenever something
changes, so it keeps answering when the Bucker API does not; if what a reader sees is out of date, a
banner says how old it is. Incidents, maintenance and subscribers are managed on the same screen,
**Trust → Status pages**. This guide covers setting a page up.

## Set one up

In the dashboard open **Trust → Status pages** and choose **Guided setup**. A new organization's
sidebar has no Trust entry until it has a status page or a verification certificate, so press Ctrl+K
(⌘K on a Mac) and choose **Status pages**, or choose **Show these on a status page** under
**Monitors → Uptime**, which opens the setup with that project already selected.

The setup has six steps. Three are required: a page, something for it to watch, and publishing it.

| Step | In the dashboard | REST route | Who can take it |
| --- | --- | --- | --- |
| 1. Name your page (required) | **Create status page**, or the first screen of **Guided setup** | `POST /orgs/{orgSlug}/status-pages` | Owner or admin. A page starts **Private** unless an owner chooses otherwise |
| 2. Choose what to show (required) | **Choose what to show** → **Add to page** | `POST /orgs/{orgSlug}/status-pages/{pageSlug}/setup/components` | Owner or admin, with write access to the project |
| 3. Make it yours | **Make it yours**, or **Edit page** | `PATCH /orgs/{orgSlug}/status-pages/{pageSlug}` and `PUT /orgs/{orgSlug}/status-pages/{pageSlug}/brand/{asset}` | Owner or admin |
| 4. Publish it (required) | **Publish it** → **Make public** | `PATCH /orgs/{orgSlug}/status-pages/{pageSlug}` with `{"visibility": "PUBLIC"}` | Owner only |
| 5. Custom domain | **Custom domain** | `POST /orgs/{orgSlug}/status-pages/{pageSlug}/domain` and `POST /orgs/{orgSlug}/status-pages/{pageSlug}/domain/check` | Set and remove: owner only. Look and check: owner or admin |
| 6. Share it | **Share it** | The addresses are in `GET /orgs/{orgSlug}/status-pages/{pageSlug}/setup` | Anyone who can see the page |

A page's slug comes from its name unless you send `slug`; it is part of the Bucker address and cannot
be changed.

**The setup reads, it does not remember.** Each time you open it, it works out from the page itself
which steps are done, which is next and what your role may do, so a change made in another tab,
through the API or by an agent shows up the next time you look. The address carries the page and the
step (`?page=` and `?step=`), so a link opens the same step for someone else. The Status pages screen
shows a **Finish setting up** banner that points at the next step; **Not now** hides it in your
browser.

**When your role cannot take a step.** Members, billing contacts and viewers can open the setup and see
where the page stands. A control you cannot use stays visible and disabled, says who can use it, names
the organization's owners, and offers **Copy link to this step** to send them. Publishing, the
hostname and deleting a page are owner-only, because they decide who can read what and which hostname
points at Bucker; an admin can take every other step of the setup.

**REST.** The examples use `https://api.bucker.io` and a personal access token from
**Settings → Tokens** in `$TOKEN`. Steps 1, 2, 3 and the DNS check need `org:write`, and step 2 also
needs `project:write`. A personal access token can never carry owner authority, so the owner-only calls
(publishing, setting or removing a hostname, deleting a page) need an owner's signed-in session; in
practice an owner makes them in the dashboard.

```bash
curl -X POST https://api.bucker.io/orgs/acme/status-pages \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "Acme status"}'
```

The answer is the page, with its `slug` (`acme-status` here) and `publicUrl`. Step 2's request is
under [Components and monitors](#components-and-monitors).

## Where a page stands

`GET /orgs/{orgSlug}/status-pages/{pageSlug}/setup` is the one read that the dashboard, scripts and
agents all use to ask "is this page set up, what is next, and may I do it". Anyone who can see the page
can call it. It is worked out on every call and stores nothing.

```text
{
  "page": { "slug": "acme-status", "name": "Acme status", "visibility": "PRIVATE",
            "publicUrl": "https://status.bucker.io/acme/acme-status" },
  "permissions": { "write": true, "admin": false },
  "capabilities": { "customDomains": "automatic", "emailDelivery": false },
  "ready": false,
  "nextStep": "publish",
  "steps": [
    { "id": "page", "state": "done", "required": true, "requires": "write", "blockedBy": null },
    { "id": "components", "state": "done", "required": true, "requires": "write", "blockedBy": null },
    { "id": "appearance", "state": "optional", "required": false, "requires": "write", "blockedBy": null },
    { "id": "publish", "state": "todo", "required": true, "requires": "admin", "blockedBy": null },
    { "id": "domain", "state": "optional", "required": false, "requires": "admin", "blockedBy": null },
    { "id": "share", "state": "blocked", "required": false, "requires": "write", "blockedBy": "publish" }
  ],
  "facts": { "components": { "total": 2, "bound": 2, "live": 2 }, "domain": null }
}
```

- `ready` is true once the page is public and at least one component watches something. `nextStep` is
  the first required step that is not done, then a hostname still being set up, then `null`.
- A step's `state` is `done`, `todo`, `in_progress` (waiting on something that is not you, such as
  DNS), `blocked` (until the step in `blockedBy` is done) or `optional`. `requires` is `write` for an
  owner or admin and `admin` for an owner alone; `permissions` says which of the two you hold.
- `capabilities.customDomains` is `automatic` (Bucker connects a verified hostname on its own),
  `operator` (Bucker arranges each one) or `not_offered`, which is the dashboard's "custom domains are
  not switched on yet" (see [Your own domain](#your-own-domain)). `emailDelivery` is false while email
  subscriptions cannot complete (see [Share](#share)).
- `facts` carries the counts behind each step, the projects that monitors can go in with their
  remaining room, and the addresses to share. For a hostname it describes progress only: it never
  contains the DNS records or the verification value, which is why everyone who can read the page can
  read it.

## Components and monitors

Who: an owner or admin, with write access to the project that will hold the monitors. A component is
what your customers see; a monitor is what measures it. This step creates both and binds them in one
request.

In the dashboard open **Trust → Status pages → Guided setup → Choose what to show**. Monitors live in
a project, so an organization with none creates one first under **Settings → Projects**. Pick the
project (it shows how many of its 100 monitors are used), type or paste web addresses (a missing
`https://` is filled in), optionally give each the name customers should see, and choose
**Add to page**. You can add up to 25 at a time. Instead of an address you can choose
**A monitor I already have** (uptime or cron). That is also how to put a monitor that needs request
headers or a body on the page: those are set under **Monitors → Uptime**, never in this step.

```bash
curl -X POST https://api.bucker.io/orgs/acme/status-pages/acme-status/setup/components \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"projectSlug": "web", "groupName": "Public site", "entries": [
        {"name": "Website", "url": "https://acme.io"},
        {"name": "Billing", "monitor": {"kind": "uptime", "slug": "billing-api"}}]}'
```

```text
{ "items": [ { "index": 0, "name": "Website", "outcome": "created",
               "component": { "slug": "website", "outcome": "created" },
               "monitor": { "kind": "uptime", "slug": "website", "outcome": "created" },
               "binding": { "id": "…", "outcome": "created" }, "error": null }, … ],
  "created": { "monitors": 1, "components": 2, "bindings": 2 }, "failed": 0 }
```

An entry is either an address (`url`, with optional `name`, `componentName`, `description`, `method`,
`expectedStatus`, `intervalSeconds`, `timeoutMs`, `failureThreshold`, `recoveryThreshold` and
`degradedAboveLatencyMs`) or an existing monitor (`name` and `monitor: { kind, slug }`). `groupName`
puts every component the call creates under a group of that name.

**What each address becomes.** An uptime monitor in the project you name: a `GET` every 60 seconds
that expects a 200, goes down after 3 failures in a row and recovers after 2. When it goes down it
opens an issue in that project and that project's alert rules run, so check that those rules notify
someone you want notified ([alert-rules.md](alert-rules.md), [uptime.md](uptime.md)).

**Rules.** Only public addresses can be watched: `localhost`, internal-only names and private network
addresses are refused, and an address that does not resolve to a public address fails its own entry. A
project holds at most 100 uptime monitors, disabled ones included; a batch that would not fit is
refused before anything is created, naming both numbers. Repeating a request creates nothing twice:
each part (monitor, component, binding) is found again and reported as `created` or `existing`, and an
entry that fails leaves the others in place, so sending the list again finishes it.

**What you will see while you wait.** Each new monitor makes its first check within a few minutes.
Until then its component reads **No data**, never Operational. Afterwards a monitor that is down makes
its component a **Major outage**; with `degradedAboveLatencyMs` set, a monitor that is up but slower
than that on average over its last five checks makes it **Degraded performance**. A monitor that is
disabled, deleted or has never run contributes nothing, so its component stays **No data**.

## Appearance

Optional, and none of it changes who can read the page. Who: an owner or admin. In the dashboard use
**Make it yours** in the guided setup, or **Edit page** on the Status pages screen. The fields are on
`PATCH /orgs/{orgSlug}/status-pages/{pageSlug}`.

| Setting | What it does | Field |
| --- | --- | --- |
| Headline | Shown at the top of the page. Up to 2,000 characters | `headline` |
| Support link | A public `http://` or `https://` link shown on the page | `supportUrl` |
| Brand colour | A six-digit hex colour such as `#1a2b3c` for the page's frame. It never changes a status colour, so an outage cannot be made to look calmer | `brandColor` |
| Time zone | Every time on the page is shown in this IANA zone, such as `Europe/Madrid`. Default `UTC` | `timezone` |
| Language of dates | Dates and times on the page are written in this language, such as `en`, `es` or `pt-BR`. The rest of the page stays in English. Default `en` | `locale` |
| Uptime window | How many days the uptime bars cover, 7 to 365. Default 90 | `uptimeWindowDays` |

Send `null` for `headline`, `supportUrl` or `brandColor` to clear it. A change reaches the public page
within a minute. Dates in incident and maintenance notes, and the month names over the uptime
calendar, follow the language setting; the rest of the page stays in English. **Edit page** also
holds the behaviour settings: whether scheduled maintenance counts against uptime by default, a
reminder when a drafted incident has waited too long (`autoDeclareAfterMinutes`, 1 to 1,440
minutes; it never publishes anything), and auto-publishing, which is owner-only.

**Logo and favicon.** A logo can be a PNG, JPEG, GIF, WebP, ICO or SVG image and a favicon a PNG, ICO
or SVG image, each up to 1 MB. An SVG that contains scripts or external references is refused. The
dashboard uploads a file as soon as you choose **Upload**; it is not part of **Save**, and **Save**
refuses, naming the file, while a logo or favicon you chose has not been uploaded yet, in **Edit
page** as in the guided setup. Over REST the file goes in as base64, and `{asset}` is `logo` or
`favicon`. Send the body from standard input, as below: a shell cannot pass one argument larger than
about 128 KB, so a logo over roughly 96 KB put inline in `-d` fails with `Argument list too long`
before anything is sent.

```bash
{ printf '{"contentBase64": "'; base64 < logo.png | tr -d '\n'; printf '"}'; } |
  curl -X PUT https://api.bucker.io/orgs/acme/status-pages/acme-status/brand/logo \
    -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
    --data-binary @-
```

`DELETE /orgs/{orgSlug}/status-pages/{pageSlug}/brand/{asset}` takes the image off the page. Both
calls answer 200 with the updated page, and uploads are limited to 30 a minute. Deleting the page, or
the organization, deletes the uploaded images. Until you upload a favicon, the browser tab shows the
Bucker mark.

**What the page calls itself.** The browser tab and link previews carry `<Name> Status` (a name that
already ends in "Status" is not doubled), and the site name in a preview is the page's name. The page's
canonical address is your hostname once it is live, and its Bucker address until then.

## Publishing

Who: an owner. A new page is Private by default, and nothing on it is visible outside your
organization until an owner makes it public.

In the dashboard open **Trust → Status pages → Guided setup → Publish it** and choose **Make public**.
The confirmation names the exact address that becomes readable, and the guided step stays blocked
until at least one component watches something. That rule belongs to the guided step alone:
**Create status page**, **Edit page** and the API do not check it, so an owner who sets the
visibility to Public in any of them publishes a page that may show nothing yet. Over REST an owner
sends `PATCH /orgs/{orgSlug}/status-pages/{pageSlug}` with `{"visibility": "PUBLIC"}`.

- **What visitors see.** Your components and their current status, any incident you publish and
  scheduled maintenance. Drafts stay private. The page, its feeds, the JSON and the subscribe form
  start answering within a minute.
- **Which calls are owner-only.** A `PATCH` that names `visibility` or `autoPublishDefault` is an owner
  call even when the value does not change, and so is creating a page that is public or has
  `autoPublishDefault` on. An admin can create a Private page and edit everything else.
- **Taking it offline.** An owner sets the visibility back to Private in **Edit page**. A hostname set
  on the page then waits for the page to be public again.
- **Password protected** pages have no web page yet, so publishing is also how a password page becomes
  a normal one.
- **Deleting a page** is owner-only too: open the page's actions menu on the Status pages screen,
  choose **Delete page** and type the slug, or call `DELETE /orgs/{orgSlug}/status-pages/{pageSlug}`.
  It removes the page with its components, incidents, maintenance and subscribers, takes the public
  page down and deletes its uploaded images.

## Your own domain

Serve a page on a hostname you own, such as `status.acme.io`, instead of
`status.bucker.io/acme/acme-status`. Who: an owner sets and removes the hostname; owners and admins see
its records and check it. It takes two DNS records that you add, a check that Bucker repeats until it
finds them, and a certificate; [After you set a hostname](#after-you-set-a-hostname) explains the
waiting.

If the dashboard says custom domains are not switched on yet, your records are kept and used as soon as they are.

**Choose a hostname.** A subdomain such as `status.acme.io`. The root of a domain (`acme.io`) cannot
hold the CNAME record Bucker needs, so it is refused, and so is a name inside `bucker.io`. Use
lowercase letters, digits, hyphens and dots (for an internationalised name, its `xn--` form). A
hostname belongs to one page at a time.

**Set it.** Open **Trust → Status pages → Guided setup → Custom domain**, type the hostname and choose
**Use this domain**. An admin can type a hostname too and use **Copy link to this step**: the link
opens for an owner with the hostname filled in. Over REST an owner sends
`POST /orgs/{orgSlug}/status-pages/{pageSlug}/domain`, and owners and admins read the records with
`GET /orgs/{orgSlug}/status-pages/{pageSlug}/domain`:

```bash
# An owner's signed-in session; a personal access token cannot make this call
curl -X POST https://api.bucker.io/orgs/acme/status-pages/acme-status/domain \
  -H "Authorization: Bearer $OWNER_SESSION_TOKEN" -H "Content-Type: application/json" \
  -d '{"domain": "status.acme.io"}'
```

**Add two records at your DNS provider.** Bucker shows them, each with a copy button, as soon as the
hostname is saved:

| Type | Name | Value | What it does |
| --- | --- | --- | --- |
| `CNAME` | `status.acme.io` | `cname.bucker.io` | Points the hostname at Bucker |
| `TXT` | `_bucker-status.status.acme.io` | `bucker-status-verify=` and a value Bucker generates for this page | Proves you control the zone today |

Anyone can point a CNAME at Bucker, including at a name they have just taken over from someone who
forgot to delete theirs, so the TXT record is what proves control. Bucker never creates or changes
records in your DNS. **A hostname serves nothing until both records resolve.** The TXT name is
`_bucker-status.` followed by the full hostname, not by the root domain, and its value must match
exactly, with no extra spaces or quotation marks of your own.

**Where to add them.**

- **Cloudflare.** Set the CNAME to **DNS only** (the grey cloud). A proxied record (the orange cloud)
  hides the CNAME, and Bucker cannot verify it.
- **Amazon Route 53.** Create a plain record of type CNAME, not an Alias record.
- **A registrar or another DNS host.** Some providers want only the part before your domain in the
  Host or Name field. Try the full name first. If the result looks doubled (`status.acme.io.acme.io`),
  enter `status` for the CNAME and `_bucker-status.status` for the TXT record instead.
- **TTL.** Leave it on automatic, or set 300 seconds or less.

Check from your own terminal; a resolver that cached an older answer can lag behind Bucker's view:

```bash
dig +short CNAME status.acme.io                # cname.bucker.io.
dig +short TXT _bucker-status.status.acme.io   # "bucker-status-verify=…"
```

A name that answers with an address instead of the CNAME (an A record, a proxied Cloudflare record, an
ALIAS or a flattened CNAME) cannot be verified. A three-label root such as `acme.co.uk` looks like
`status.acme.io` to Bucker; if it answers with an address, the check says so.

## After you set a hostname

Who: owners and admins watch this; an owner changes the hostname. **Bucker checks DNS on its own**, so
nobody has to press anything. The first check comes within about a minute of saving the hostname.
After each check that does not find both records, Bucker waits 1, 2, 5 and then 15 minutes before the
next, and an hour after that, until the hostname's 48-hour hold ends. Checking runs whether or not
custom domains are switched on; only connecting the hostname waits on that. While the setup is open it
refreshes itself every 15 seconds, and Bucker keeps checking when it is closed.

- **The hold.** An unverified hostname is held for your page for 48 hours, so you have time to add the
  records. After that Bucker stops checking and another page, in any organization, may take the name.
  The dashboard says **The hold on this domain has ended** and offers **Hold it again** (an owner).
- **Setting the same hostname again.** While the hold is live it changes nothing, and the page keeps
  its TXT value. After the hold lapses, setting the same hostname renews it and keeps the value, so a
  TXT record you already published still verifies. Removing the hostname and setting it again gives
  a new value, and you must publish the new TXT record.
- **Ten at a time.** An organization can have at most 10 hostnames waiting for DNS verification. An
  eleventh is refused until one is verified or removed.
- **Check now.** The button, or `POST /orgs/{orgSlug}/status-pages/{pageSlug}/domain/check` for owners
  and admins, looks up both records immediately and starts the 1-minute cadence again. It is limited
  to six a minute per page; past that the API answers 429 with the seconds to wait, and nothing is
  lost by waiting.

The check's answer has `action` (`verified`, `checked`, `provisioning_retried` or `none`), `cnameOk`,
`txtOk`, `problems` and the new `state`. Each problem has a `record`, a `code` (`missing`,
`wrong_target`, `address_record` or `token_mismatch`) and what was `found`. On a verified hostname that
is not live yet, Check now looks up no DNS and asks Bucker's hosting to try again at its next pass; when
there is nothing to check, `action` is `none`. The older
`GET /orgs/{orgSlug}/status-pages/{pageSlug}/domain/verify` still works and is limited the same way,
but is deprecated: it also re-checks a verified hostname and stops verifying it on a miss.

**Verified hostnames are re-checked.** About once an hour Bucker looks again at the records of every
verified hostname. If a record is missing on two lookups in a row, the hostname stops being verified,
the page falls back to its Bucker address and you are told which record went missing. Bucker looks
again within a minute, keeps checking for 48 hours, and verifies it again as soon as the record is
back.

## Domain states and notices

Who: owners and admins. The dashboard shows where the hostname stands:

| State | What it means | What moves it on |
| --- | --- | --- |
| **Waiting for DNS** | One or both records are not found yet. Each shows Found, Not found yet or Not checked yet | You add the records. DNS changes usually show within minutes and can take a few hours |
| **Waiting for the page to be public** | Both records are verified, but the page is private or password protected | An owner makes the page public. Where Bucker connects hostnames on its own, it starts within about a minute; where Bucker arranges each one, it does that next; while custom domains are not switched on yet, publishing does not connect it, and your records are kept |
| **Being set up** | Verified and public. Bucker adds the hostname to its hosting within about a minute of that | Nothing from you |
| **Waiting for the certificate** | The hostname is added and HTTPS has not answered yet | Usually a few minutes. After 48 hours the dashboard says it is taking longer than usual |
| **Live** | The hostname answers over HTTPS and is the page's address | Nothing |
| **Verified** | Verified and public, but not connected: custom domains are not switched on yet, or Bucker is arranging the hostname. The page keeps its Bucker address | Bucker connecting the hostname. Your records are kept |

**Live is the only state in which Bucker's links use your hostname.** Verifying DNS does not make a
hostname answer, and a link to a name with no certificate is worse than the address it replaces. Once
a hostname is live, the Bucker address forwards to it, so links people already have keep working.

**What visitors see before Live.** Until Bucker has connected the hostname, a browser can show a
certificate warning or an error from the hosting platform, because nothing is answering for the name.
Once it is connected, a hostname with no verified, public page behind it shows a short page, "No status
page is published at this address", saying the page must be public and both DNS records verified. It is
the same page for every hostname, so it tells a stranger nothing about who owns a name.

**Notices.** Bucker tells your organization's notification channels, if any (**Settings → Channels**),
when its own checks verify a hostname, when a hostname goes live, when it stops verifying (naming the
missing record, never the TXT value), and once when it is still not live 48 hours after Bucker started
connecting it. An organization with no channels gets no notices; the Custom domain step shows the same
facts. A hostname you verify yourself with **Check now** shows its answer on screen and
sends no notice. Each change is also a row in **Settings → Audit log** (owners), and the ones Bucker
makes on its own are attributed to Bucker's status service.

**Changing or removing it.** **Remove domain** (an owner), or
`DELETE /orgs/{orgSlug}/status-pages/{pageSlug}/domain`, puts the page back on its Bucker address and
takes the hostname off Bucker's hosting at its next pass, about a minute later. Your DNS records are
not touched, so delete them where you added them. Links to the hostname stop working, and a browser
that followed the redirect from the Bucker address may remember it for a while. Setting a different
hostname replaces the old one the same way.

## When the domain does not work

Start with **Check now**: it names what is wrong with each record. The messages are the dashboard's and
the API's own.

| What you see | What it means | What to do |
| --- | --- | --- |
| No CNAME record found at *name* | Nothing answers at that name | Add the CNAME on exactly that name |
| The CNAME at *name* points to *x* | It has another target | Point it at `cname.bucker.io` |
| *name* answers with an IP address, not a CNAME | An A record, a proxied (orange-cloud) Cloudflare record, an ALIAS or a flattened CNAME hides the CNAME | Use a plain CNAME; in Cloudflare set it to DNS only, in Route 53 use a CNAME, not an Alias |
| No TXT record found at *name* | Missing, or at the wrong name | Add it at `_bucker-status.` plus the full hostname |
| A TXT record exists at *name*, but its value doesn't match | An old value, a copy error, or the hostname was removed and set again (a new value) | Copy the value from the dashboard again |
| A doubled name such as `status.acme.io.acme.io` | The provider's Host field wanted the name relative to your domain | Enter `status` and `_bucker-status.status` |
| The hold on this domain has ended | 48 hours passed without both records | If the records are in place press **Check now**; otherwise an owner presses **Hold it again** |
| 409 "That domain is already attached to another status page" | Another page holds the hostname | Use another hostname. A hostname that was never verified frees up 48 hours after it was claimed; a verified one stays with its page until its owner removes it or its records stay missing for 48 hours |
| 409 "This organization already has 10 hostnames waiting for DNS verification; verify or remove one first" | The limit of 10 | Verify or remove one |
| 400 "Use a subdomain such as status.acme.io: the root of a domain cannot hold the CNAME record Bucker needs." | You entered a root domain | Enter a subdomain |
| 400 "A custom domain cannot be inside bucker.io" | The name is Bucker's | Use your own domain |
| 429 "You can check again in *n* seconds; Bucker is also checking automatically" | Six checks a minute per page | Wait |
| **Waiting for the page to be public** | Verified, but the page is not public | An owner publishes the page |
| **Verified**, with "Custom domains are not switched on yet" | The hosting is not switched on | Nothing: your records are kept |
| **Waiting for the certificate** for a long time | HTTPS has not answered | Check that both records are still in place and that your DNS provider is not proxying the name. Bucker keeps retrying and tells you once at 48 hours |
| A notice that the hostname "stopped verifying" | A record was missing on two lookups in a row | Restore it; Bucker verifies again as soon as it sees it |
| A certificate warning in the browser | The hostname is not connected yet | Wait for **Live** |

A note under a hostname that Bucker could not finish connecting is one of three sentences. "The hosting
platform refused this hostname. If it is attached to another hosting project you control, remove it
there first; Bucker keeps retrying" means you hold the name elsewhere, and removing it there is the fix.
"Bucker could not finish connecting this hostname; the problem is on Bucker's side, not in your DNS, and
it is retried automatically" and "Bucker's hosting platform could not be reached just now; this is
retried automatically" need nothing from you.

## Share

Who: anyone who can see the page. Once the page is public, **Share it** lists every address with a copy
button, and the same addresses are in `facts.share` of
`GET /orgs/{orgSlug}/status-pages/{pageSlug}/setup`. They use your hostname once it is live and the
Bucker address before that:

| What | Address | For |
| --- | --- | --- |
| Page | `https://status.acme.io` | The link to send customers |
| Subscribe | `https://status.acme.io/subscribe` | Where customers sign up for updates |
| Badge | `https://status.acme.io/badge.svg` | A small image that links to the page |
| Embed | `https://status.acme.io/embed` | A compact status strip for an `<iframe>` |
| RSS | `https://status.acme.io/history.rss` | Incident history for feed readers |
| Atom | `https://status.acme.io/history.atom` | The same history as Atom |
| JSON summary | `https://status.acme.io/api/v2/summary.json` | A Statuspage-compatible summary for tools |

```text
<a href="https://status.acme.io"><img src="https://status.acme.io/badge.svg" alt="Acme status" height="20"></a>
<iframe src="https://status.acme.io/embed" title="Acme status" width="100%" height="320" style="border:0" loading="lazy"></iframe>
```

**What moves to your hostname.** Once the hostname is live, the page links in notifications (the
`page.url` and `url` fields of a webhook delivery, and the links in Slack and email messages), the
feeds and the Statuspage-compatible JSON use it. Confirm and unsubscribe links always stay on the
Bucker address, so a message a subscriber kept for a year still works, and they keep working if you
remove the hostname. If you receive webhook deliveries, treat `page.url` as where to send a person and
not as an identifier: it changes when the page gains or loses a hostname. `page.slug` does not change.

**Subscribers.** Readers subscribe at `/subscribe` by email, webhook or Slack, and can choose which
components to hear about. Every subscription is confirmed first and every webhook delivery is signed.
Email confirmations are not sent from Bucker yet, so email subscriptions cannot complete; webhook and Slack subscriptions work.

## Availability targets

Give a component a **target** (for example 99.9%) and a **window** (7 to 365 days, default 30) in
its settings, and it gets a measurement card in the dashboard and on the public page:

> **Target met.** 99.95% over the last 30 days against a 99.9% target.
> 50% of the error budget left (22 of 43 min).
> Spending the budget at 0.5× the rate the target allows; 0.3× over the last 7 days.

- **Attainment** is the share of the time that was *observed* that the component was not down,
  over the component's own window. Degraded time counts as available (the service answered) and is
  reported separately; maintenance is shown, never silently removed.
- **The error budget** is the downtime the target permits over the time observed. Remaining budget
  is what is left of it; once the target is missed the card says the budget is used up.
- **Burn** is downtime so far divided by the downtime the target permits. 1× spends the budget
  exactly as fast as the target allows; above 1× the target is being missed. It is shown for the
  whole window and for the last seven days, so a fresh incident shows before the window does, and
  the card says in words when the recent rate is faster than the target allows even if the window
  as a whole is still met.
- **Unobserved time is not up.** If only part of the window was measured, the card says how much
  ("Measured over 40% of the window"). An error budget computed over six days of a thirty-day
  window is not a thirty-day budget, and nothing here pretends it is.
- A component nothing has measured yet has neither met nor missed its target, so it shows no card.

**This is measurement, not a promise.** The target is yours, set against your own measurement.
Bucker does not compute credits, refunds or breach remedies, and the card on the public page says
"the operator's own target, not a guarantee". There is no SLA product.

The public page draws the card from the pre-generated snapshot only: it never queries a database,
so it is available whenever the page is. The dashboard reads the same figures live through the same
function (`GET /orgs/{orgSlug}/status-pages/{pageSlug}/uptime`, field `slo`), so the two cannot disagree
about one component. A snapshot generated before burn was added simply omits the burn lines until
its next regeneration.

## With an agent

An agent can read this guide and set most of a page up. It cannot publish anything.

**Reading the guide.** `search_docs` finds this page, and `get_doc` with the slug `status-pages`
returns it one part at a time (`part` counts from 0 and the answer says how many there are).
[mcp-setup.md](mcp-setup.md) connects an agent and lists every tool with its token budget.

**Six tools** cover the setup, two reads and four writes:

| Tool | Covers |
| --- | --- |
| `status_setup_get` | Where a page stands, which step is next, who takes it (the agent, with which tool, or an owner) and the dashboard link to hand an owner. Pass `domain` and the link arrives with that hostname filled in. Never returns the DNS records or the verification value |
| `status_domain_records_get` | The two records a page's hostname needs and what the last check found |
| `status_page_create` | Step 1, always a Private page with auto-publishing off |
| `status_page_update` | Step 3's settings: name, headline, support link, brand colour, time zone, language, uptime window |
| `status_components_add` | Step 2, up to 10 addresses or existing monitors per call. Query values are masked in the plan the approver reads |
| `status_domain_check` | Step 5's check: looks up a pending hostname's DNS now and says what is wrong with each record. It never stops a verified hostname verifying |

**Scope.** Every tool except `status_setup_get` needs `status:write`. Owners and admins hold it, no
custom role can grant it, and it is never a default scope, so name it when you connect:
`"scope": "project:read issue:read alerts:write status:write"`. `status_components_add` also needs
`alerts:write` on the project that will hold the monitors.

**Approval.** Creating a page, changing its settings and adding components are proposals: the first
call writes nothing and opens an approval request that holds the plan, a person decides it, and the
agent's retry applies it. The DNS check applies at once unless your organization has set a policy to
ask. The person who decides must hold `status:write` in your organization (an owner or admin) and
must not be in the chain that made the request: not the agent, not the person it acts for, and not
the person whose own MCP token proposed it. They decide in the approvals inbox (**Agents →
Approvals**), which shows the whole plan, or from Slack where it is connected. An `approvalToken`
sent back from the client cannot decide these proposals.

**An organization with one person.** If the only owner or admin is the person running the agent, nobody
can approve its proposals and they wait. There are two ways through. An owner sets Allow policies in
**Settings → Approval policies** for `status_page.create`, `status_page.update` and
`status_component.setup`, after which those calls apply without a human decision; or the person takes
those steps in the dashboard. `status_setup_get` says so in `approvalNote` whenever it is true.

**What has no tool.** Making a page public or password protected, turning on auto-publishing,
setting or removing the hostname and deleting a page are owner acts in the dashboard, and the
approvals engine marks them human-only, so a tool added later could not perform them either.
Uploading a logo or favicon has no tool either; an owner or admin does it in the dashboard or over
REST, and it is not on the human-only list.

Each applied write returns a `plan` that records what was approved. It is not configuration to commit:
`bucker.yml` has no status page section, and a file that names one is refused.

## Limits and what is not done

- **Email subscriptions.** Email confirmations are not sent from Bucker yet, so email subscriptions cannot complete; webhook and Slack subscriptions work.
- **Hosting a hostname is switched on separately from verifying it.** If the dashboard says custom domains are not switched on yet, your records are kept and used as soon as they are.
- **A subdomain, not the root.** `acme.io` is refused, and there is no support for an A record or an
  ALIAS at the root. One hostname per page, 10 waiting hostnames per organization, none inside
  `bucker.io`.
- **Public pages only on a hostname.** A password-protected page has no web page, so it has no
  hostname either; a verified hostname on a private page waits for the page to be public.
- **No preview of a private page.** The dashboard shows a private page's components and statuses, but
  the page itself is rendered only once it is public. Publish, look, and take it back to Private if you
  need to.
- **A small set of looks.** A colour, a logo, a favicon, a headline and a support link. There is no
  custom CSS or footer, no separate logo for dark mode, and no image of its own in link previews. Only
  dates follow the language setting; the rest of the page is English.
- **No `robots.txt` of its own.** Your hostname answers 404 for `/robots.txt`, which crawlers read as
  everything allowed. The favicon reaches browsers through the page's own icon link.
- **If the Bucker API cannot be reached,** the status site cannot look up which page belongs to a
  hostname that the server answering has not looked up before, and answers 503 ("could not be looked up
  just now"), never 404. A hostname it has already looked up keeps being served.
- **No `bucker.yml` section and no export or import** for status pages.
- **Agents cannot publish.** [With an agent](#with-an-agent) lists what they can and cannot do.
- **An approver may see only the start of a plan.** An agent's proposal is stored with its plan, the
  agent's reason and the change it would make, and the approvals inbox shows all of it. A Slack
  message carries a summary cut at 300 characters and a link to the request, and the approval card in
  an agent's client, where there is one, the first part. Open the request in the inbox before you
  approve a change you have not read in full.
