---
title: Create a status page
description: Creates a status page.
url: https://docs.honeybadger.io/api/v3/status-pages/create-status-page/
---

API v3 is in preview

Endpoints and response shapes [can change before general availability](https://docs.honeybadger.io/api/v3/#preview). API v2 is unchanged.

POST `https://app.honeybadger.io/v3/status_pages`

[Required scope](https://docs.honeybadger.io/api/v3/authentication/#scopes) `status_pages:write`

Creates a status page.

Example request

```sh
curl -X POST "https://app.honeybadger.io/v3/status_pages" \
  -H "Authorization: Bearer $HONEYBADGER_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Acme Status"
}'
```

## Request body

| Field | Type | Description |
| - | - | - |
| `name` required | string | Max length 100. |
| `domain` | string \| null | Custom domain the page is served on. Unique across Honeybadger. An account can set at most 30 domains a week. Null clears it. |
| `incidents_enabled` | boolean \| null | Whether the page shows incidents. Changing it requires status page incidents on the plan. On create, defaults to whether the plan has them. Null resets it to that same create default. |
| `search_engine_indexing_disabled` | boolean \| null | Asks search engines not to index the page. Null resets it to false. Default: `false`. |
| `message_enabled` | boolean \| null | Shows the message banner. Null resets it to false. Default: `false`. |
| `message_title` | string \| null | The message banner's title. Null clears it. |
| `message_body` | string \| null | The message banner's text. Null clears it. |
| `google_analytics_id` | string \| null | A Google Analytics measurement ID for the page. Null clears it. |
| `hide_branding` | boolean \| null | Changing it requires advanced status pages on the plan; the default (false) or the stored value is accepted on any plan. Null resets it to false. Default: `false`. |
| `password_protected` | boolean \| null | Changing it requires advanced status pages and password protection on the plan; the default (false) or the stored value is accepted on any plan. Turning it on needs a username and password, sent or already stored. Null resets it to false. Default: `false`. |
| `username` | string \| null | Basic-auth user when password protected. Requires advanced status pages and password protection on the plan. |
| `password` | string \| null | Basic-auth password when password protected. Never returned. Requires advanced status pages and password protection on the plan. |
| `features` | object \| null | Presentation copy and styling. Replaces the whole object; null clears it. Changing it requires advanced status pages on the plan; the default ({}) or the stored value is accepted on any plan. Default: `{}`. |
| `features.home_link` | string \| null | URL the page header links to, usually your own site. |
| `features.up_caption` | string \| null | Text shown when every check is up. Unset, the page says "All systems are operational". |
| `features.down_caption` | string \| null | Text shown when every check is down. Unset, the page says "All systems are experiencing issues". |
| `features.mixed_caption` | string \| null | Text shown when only some checks are down. Unset, the page says "Some systems are experiencing issues". |
| `features.custom_css` | string \| null | CSS added to the page after its own styles. |
| `sites` | array \| null | Sites listed on the page, in page order, replacing the current list. Null or empty removes every site. Default: `[]`. |
| `sites[].site_id` required | uuid | Identifier of a site on this account. |
| `sites[].display_name` | string \| null | null uses the monitor's own name; "" is refused. Min length 1. |
| `sites[].description` | string \| null | null shows no description; "" is refused. Min length 1, max length 80. |
| `check_ins` | array \| null | Check-ins listed on the page, in page order, replacing the current list. Null or empty removes every check-in. Default: `[]`. |
| `check_ins[].check_in_id` required | string | ID of a check-in on this account. |
| `check_ins[].display_name` | string \| null | null uses the monitor's own name; "" is refused. Min length 1. |
| `check_ins[].description` | string \| null | null shows no description; "" is refused. Min length 1, max length 80. |

## Responses

### 201 Status page created

Example payload

```json
{
  "data": {
    "id": "Sp7bMq",
    "account_id": "Ab3kL9",
    "name": "Acme Status",
    "domain": "status.acme.com",
    "features": {
      "home_link": "https://www.example.com",
      "up_caption": "Everything is running smoothly",
      "down_caption": "We're working on an outage",
      "mixed_caption": "Some services are degraded",
      "custom_css": ".header { background: #1d4ed8; }"
    },
    "hide_branding": true,
    "password_protected": true,
    "username": null,
    "incidents_enabled": true,
    "search_engine_indexing_disabled": true,
    "message_enabled": true,
    "message_title": "Scheduled maintenance",
    "message_body": "We're upgrading our database on Saturday at 02:00 UTC.",
    "google_analytics_id": null,
    "url": "https://acme.statuspage.honeybadger.io",
    "created_at": "2026-01-01T00:00:00Z",
    "domain_verified_at": "2026-01-01T00:00:00Z",
    "sites": [
      {
        "site_id": "9f8b6d2e-4c1a-4b7f-9e35-2a6c8d0f1b47",
        "display_name": "Website",
        "description": null
      }
    ],
    "check_ins": [
      {
        "check_in_id": "Ci5xWq",
        "display_name": "Nightly backup",
        "description": null
      }
    ],
    "links": {
      "web": "https://app.honeybadger.io/projects/Xk9mZp"
    }
  },
  "meta": {
    "request_id": "6f1c2b0e-8d4a-4f6e-9b2a-3c5d7e9f1a2b"
  }
}
```

Response schema

| Field | Type | Description |
| - | - | - |
| `data` required | object | A public status page. Readable and deletable after the plan loses status pages; creating and editing need the feature. |
| `data.id` required | string | |
| `data.account_id` required | string | ID of the account this status page belongs to. |
| `data.name` required | string | Status page name. |
| `data.domain` required | string \| null | Custom domain for the status page. |
| `data.features` required | object | Presentation copy and styling. Replaced whole by an update. A key that was never set is absent from a read; one sent as null reads back as null. |
| `data.features.home_link` | string \| null | URL the page header links to, usually your own site. |
| `data.features.up_caption` | string \| null | Text shown when every check is up. Unset, the page says "All systems are operational". |
| `data.features.down_caption` | string \| null | Text shown when every check is down. Unset, the page says "All systems are experiencing issues". |
| `data.features.mixed_caption` | string \| null | Text shown when only some checks are down. Unset, the page says "Some systems are experiencing issues". |
| `data.features.custom_css` | string \| null | CSS added to the page after its own styles. |
| `data.hide_branding` required | boolean | |
| `data.password_protected` required | boolean | |
| `data.username` required | string \| null | Basic-auth user when password protected. The password is never returned. |
| `data.incidents_enabled` required | boolean | Whether the page shows incidents. |
| `data.search_engine_indexing_disabled` required | boolean | Whether search engines are asked not to index the page. |
| `data.message_enabled` required | boolean | Whether the message banner shows. |
| `data.message_title` required | string \| null | The message banner's title. |
| `data.message_body` required | string \| null | The message banner's text. |
| `data.google_analytics_id` required | string \| null | The page's Google Analytics measurement ID. |
| `data.url` required | string | URL of the status page. |
| `data.created_at` required | date-time | When the status page was created. |
| `data.domain_verified_at` required | date-time \| null | When the custom domain was verified. |
| `data.sites` required | array\<object> | Uptime sites displayed on the status page. An ordered list; the order is the order on the page. |
| `data.sites[].site_id` required | uuid | Identifier of the site. |
| `data.sites[].display_name` required | string \| null | Display name on the status page. |
| `data.sites[].description` required | string \| null | Description shown on the status page. |
| `data.check_ins` required | array\<object> | Check-ins displayed on the status page. An ordered list; the order is the order on the page. |
| `data.check_ins[].check_in_id` required | string | ID of the check-in. |
| `data.check_ins[].display_name` required | string \| null | Display name on the status page. |
| `data.check_ins[].description` required | string \| null | Description shown on the status page. |
| `data.links` required | object | Links into the Honeybadger web UI. |
| `data.links.web` required | uri | The resource's page in the Honeybadger web UI, as an absolute URL. Resources without a page, such as outages and deploys, don't have `links`. For a project provisioned through Heroku, error, notice and comment links go through Heroku's single sign-on. |
| `meta` | object | |
| `meta.request_id` | string | Unique identifier for the request, for correlating with support. |

## Errors

### 401 Unauthorized

The credential is missing, invalid, expired or revoked (`unauthorized`), or was sent in a way v3 doesn’t accept (see the examples).

Example payload: unauthorized (Missing, malformed, expired or revoked credential)

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Access denied"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: unsupported\_auth\_scheme (Sent as HTTP basic, which v3 does not accept)

```json
{
  "error": {
    "code": "unsupported_auth_scheme",
    "message": "Send the credential as `Authorization: Bearer <token>`; v3 does not accept HTTP basic"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: credential\_in\_query (Sent as the \`auth\_token\` query parameter)

```json
{
  "error": {
    "code": "credential_in_query",
    "message": "Credentials must be sent in the Authorization header"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Response schema

| Field | Type | Description |
| - | - | - |
| `error` required | object | |
| `error.code` required | string | Machine-readable code. Branch on this, not the message. Allowed values: `unauthorized`, `unsupported_auth_scheme`, `credential_in_query`, `access_denied`, `insufficient_scope`, `requires_user_token`, `project_restricted`, `account_inactive`, `account_parked`, `feature_unavailable`, `not_found`, `validation_error`, `delete_failed`, `limit_reached`, `rate_limit_exceeded`, `concurrent_request`, `streams_provisioning`, `ingestion_key_not_accepted`, `maintenance_mode`, `invalid_parameter`, `forbidden_attributes`, `service_unavailable`, `ambiguous_account`, `fault_merged`. |
| `error.message` required | string | Human-readable explanation. |
| `error.details` | array\<object> \| object | A list of `{field, message}` on `validation_error` and `invalid_parameter`, and on `feature_unavailable` or `limit_reached` when one field caused the refusal; `{required_scope, token_scopes}` on `insufficient_scope`; `{merged_into, location}` on `fault_merged`. Absent when there's nothing to add. |
| `meta` | object | |
| `meta.request_id` | string | Unique identifier for the request, for correlating with support. |

### 403 Forbidden

The credential isn’t allowed to do this. `error.code` says why. An account or project the credential doesn’t cover is 404 `not_found`, not 403.

Example payload: access\_denied (The credential isn't allowed to do this to this record)

```json
{
  "error": {
    "code": "access_denied",
    "message": "Access denied"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: insufficient\_scope (The credential lacks the permission this endpoint requires)

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Insufficient scope",
    "details": {
      "required_scope": "faults:write",
      "token_scopes": [
        "faults:read",
        "insights:read"
      ]
    }
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: requires\_user\_token (The endpoint records who acted, and an account token has nobody)

```json
{
  "error": {
    "code": "requires_user_token",
    "message": "This endpoint records the person who acted, so it requires a user credential rather than an account token"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: project\_restricted (A project-restricted credential cannot create a project)

```json
{
  "error": {
    "code": "project_restricted",
    "message": "Project-restricted tokens cannot create projects"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Response schema

| Field | Type | Description |
| - | - | - |
| `error` required | object | |
| `error.code` required | string | Machine-readable code. Branch on this, not the message. Allowed values: `unauthorized`, `unsupported_auth_scheme`, `credential_in_query`, `access_denied`, `insufficient_scope`, `requires_user_token`, `project_restricted`, `account_inactive`, `account_parked`, `feature_unavailable`, `not_found`, `validation_error`, `delete_failed`, `limit_reached`, `rate_limit_exceeded`, `concurrent_request`, `streams_provisioning`, `ingestion_key_not_accepted`, `maintenance_mode`, `invalid_parameter`, `forbidden_attributes`, `service_unavailable`, `ambiguous_account`, `fault_merged`. |
| `error.message` required | string | Human-readable explanation. |
| `error.details` | array\<object> \| object | A list of `{field, message}` on `validation_error` and `invalid_parameter`, and on `feature_unavailable` or `limit_reached` when one field caused the refusal; `{required_scope, token_scopes}` on `insufficient_scope`; `{merged_into, location}` on `fault_merged`. Absent when there's nothing to add. |
| `meta` | object | |
| `meta.request_id` | string | Unique identifier for the request, for correlating with support. |

### 422 Unprocessable Content

Validation error

Example payload

```json
{
  "error": {
    "code": "validation_error",
    "message": "Name can't be blank",
    "details": [
      {
        "field": "name",
        "message": "can't be blank"
      }
    ]
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Response schema

| Field | Type | Description |
| - | - | - |
| `error` required | object | |
| `error.code` required | string | Machine-readable code. Branch on this, not the message. Allowed values: `unauthorized`, `unsupported_auth_scheme`, `credential_in_query`, `access_denied`, `insufficient_scope`, `requires_user_token`, `project_restricted`, `account_inactive`, `account_parked`, `feature_unavailable`, `not_found`, `validation_error`, `delete_failed`, `limit_reached`, `rate_limit_exceeded`, `concurrent_request`, `streams_provisioning`, `ingestion_key_not_accepted`, `maintenance_mode`, `invalid_parameter`, `forbidden_attributes`, `service_unavailable`, `ambiguous_account`, `fault_merged`. |
| `error.message` required | string | Human-readable explanation. |
| `error.details` | array\<object> \| object | A list of `{field, message}` on `validation_error` and `invalid_parameter`, and on `feature_unavailable` or `limit_reached` when one field caused the refusal; `{required_scope, token_scopes}` on `insufficient_scope`; `{merged_into, location}` on `fault_merged`. Absent when there's nothing to add. |
| `meta` | object | |
| `meta.request_id` | string | Unique identifier for the request, for correlating with support. |

---

## Try Honeybadger for FREE

Intelligent logging, error tracking, and Just Enough APM™ in one dev-friendly platform. Find and fix problems before users notice.

[Start free trial](https://app.honeybadger.io/users/sign_up)

[See plans and pricing](https://www.honeybadger.io/plans/)
