# Custom Domains API Guide

This guide walks through configuring a custom domain for a StatusPal status page entirely via the REST API. By the end, your status page will be reachable at a domain you own (e.g. `status.example.com`).

## Prerequisites

| Requirement             | Details                                                                                                                                                                     |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| StatusPal API key       | Either an **Organization API key** (prefixed `ok_`) or a **User API key** (prefixed `uk_`). Found under **Organization Settings → API Keys** or **User Profile → API Key**. |
| Organization ID         | The numeric ID of your StatusPal organization.                                                                                                                              |
| An existing status page | You need the **subdomain** (slug) of the status page you want to attach the custom domain to.                                                                               |
| DNS provider access     | You must be able to create CNAME and TXT records on the domain you want to use.                                                                                             |

All API requests use the base URL:

```bash
https://statuspal.eu/api/v2
```

{% hint style="info" %}
US-region accounts use `https://statuspal.us/api/v2` instead.
{% endhint %}

Authentication is via the `Authorization` header:

```bash
Authorization: <your-api-key>
```

## Overview

StatusPal supports two CDN providers for custom domains: **Cloudflare** and **Bunny**. The provider handles SSL certificate issuance and edge routing. The general flow is:

{% stepper %}
{% step %}

### Enable the custom domain

Configure the custom domain on the status page via the API.
{% endstep %}

{% step %}

### Retrieve DNS records

Get the DNS records StatusPal needs you to create.
{% endstep %}

{% step %}

### Create DNS records

Create those DNS records at your DNS provider.
{% endstep %}

{% step %}

### Wait for activation

Poll the status page until the domain becomes `active`.
{% endstep %}
{% endstepper %}

The Cloudflare provider requires up to three DNS records (CNAME + two TXT records for hostname and certificate verification). The Bunny provider requires only a CNAME record.

## Step 1 — Enable the custom domain

Update the status page with a `domain_config` object:

```bash
curl -X PUT "https://statuspal.eu/api/v2/orgs/{organization_id}/status_pages/{subdomain}" \
  -H "Authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "status_page": {
      "domain_config": {
        "provider": "cloudflare",
        "domain": "status.example.com"
      }
    }
  }'
```

### Parameters

| Field                    | Type   | Required | Description                                                       |
| ------------------------ | ------ | -------- | ----------------------------------------------------------------- |
| `domain_config.provider` | string | Yes      | `"cloudflare"` or `"bunny"`                                       |
| `domain_config.domain`   | string | Yes      | The custom domain (e.g. `status.example.com`). Must be lowercase. |

### Response

The response includes the full status page object. The `domain_config` field will show the initial state:

```json
{
  "status_page": {
    "subdomain": "my-page",
    "domain": "status.example.com",
    "custom_domain_enabled": true,
    "domain_config": {
      "provider": "cloudflare",
      "domain": "status.example.com",
      "status": "configuring",
      "main_hostname": null,
      "validation_records": null,
      "external_id": null,
      "error": null,
      "pullzone_id": null
    }
  }
}
```

StatusPal begins provisioning the domain asynchronously. The `status` field starts as `"configuring"`.

## Step 2 — Retrieve DNS records

Poll the status page until `validation_records` are populated:

```bash
curl "https://statuspal.eu/api/v2/orgs/{organization_id}/status_pages/{subdomain}" \
  -H "Authorization: <your-api-key>"
```

The response `domain_config` will eventually include the DNS records you need:

### Cloudflare provider

```json
{
  "domain_config": {
    "provider": "cloudflare",
    "domain": "status.example.com",
    "status": "configuring",
    "validation_records": {
      "hostname_cname_name": "status.example.com",
      "hostname_cname_value": "sp-abc123.statuspal.io",
      "hostname_txt_name": "status.example.com",
      "hostname_txt_value": "abc123-verification-string",
      "certificate_txt_name": "_acme-challenge.status.example.com",
      "certificate_txt_value": "xyz789-challenge-string"
    },
    "main_hostname": "sp-abc123.statuspal.io",
    "external_id": "cf-hostname-id"
  }
}
```

### Bunny provider

```json
{
  "domain_config": {
    "provider": "bunny",
    "domain": "status.example.com",
    "status": "configuring",
    "validation_records": {
      "hostname_cname_name": "status.example.com",
      "hostname_cname_value": "sp-abc123.b-cdn.net"
    },
    "main_hostname": "sp-abc123.b-cdn.net",
    "external_id": "status.example.com"
  }
}
```

{% hint style="info" %}
The `certificate_txt_*` fields for Cloudflare may not appear immediately. Poll every 10–15 seconds until they are present before proceeding to Step 3.
{% endhint %}

## Step 3 — Create DNS records

Create the DNS records at your DNS provider. The exact records depend on the CDN provider.

### Cloudflare provider — 3 records

| Type    | Name                            | Value                            | Purpose                                   |
| ------- | ------------------------------- | -------------------------------- | ----------------------------------------- |
| `CNAME` | `status.example.com`            | value of `hostname_cname_value`  | Routes traffic to StatusPal               |
| `TXT`   | value of `hostname_txt_name`    | value of `hostname_txt_value`    | Hostname ownership verification           |
| `TXT`   | value of `certificate_txt_name` | value of `certificate_txt_value` | SSL certificate issuance (ACME challenge) |

{% hint style="warning" %}
If your DNS provider is Cloudflare (for DNS management, not the StatusPal CDN provider), make sure the CNAME record has **proxy disabled** (DNS-only / grey cloud) to avoid routing conflicts.
{% endhint %}

### Bunny provider — 1 record

| Type    | Name                 | Value                           | Purpose                     |
| ------- | -------------------- | ------------------------------- | --------------------------- |
| `CNAME` | `status.example.com` | value of `hostname_cname_value` | Routes traffic to StatusPal |

#### Example using the Cloudflare DNS API

```bash
# CNAME record
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" \
  -H "Authorization: Bearer <cloudflare-api-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "CNAME",
    "name": "status.example.com",
    "content": "sp-abc123.statuspal.io",
    "proxied": false
  }'

# Hostname TXT record
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" \
  -H "Authorization: Bearer <cloudflare-api-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "TXT",
    "name": "status.example.com",
    "content": "abc123-verification-string"
  }'

# Certificate TXT record
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" \
  -H "Authorization: Bearer <cloudflare-api-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "TXT",
    "name": "_acme-challenge.status.example.com",
    "content": "xyz789-challenge-string"
  }'
```

## Step 4 — Wait for activation

After DNS records are created, poll the status page until `domain_config.status` becomes `"active"`:

```bash
curl "https://statuspal.eu/api/v2/orgs/{organization_id}/status_pages/{subdomain}" \
  -H "Authorization: <your-api-key>"
```

### Domain status values

| Status                | Meaning                                                               |
| --------------------- | --------------------------------------------------------------------- |
| `configuring`         | StatusPal is provisioning the domain or waiting for DNS verification. |
| `active`              | The domain is fully configured and serving your status page with SSL. |
| `failed_to_configure` | Something went wrong. Check the `error` field for details.            |
| `disabled`            | No custom domain is configured.                                       |

The verification process typically takes 1–10 minutes depending on DNS propagation. Poll every 10–30 seconds.

Once the status is `"active"`, your status page is live at `https://status.example.com`.

## Removing a custom domain

To remove the custom domain, update `domain_config` with `provider` set to `null`:

```bash
curl -X PUT "https://statuspal.eu/api/v2/orgs/{organization_id}/status_pages/{subdomain}" \
  -H "Authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "status_page": {
      "domain_config": {
        "provider": null,
        "domain": null
      }
    }
  }'
```

This revokes the SSL certificate and removes the domain from the CDN. Remember to clean up the DNS records at your DNS provider.

## Retrying a failed configuration

If `domain_config.status` is `"failed_to_configure"`, you can retry by re-submitting the same `domain_config`:

```bash
curl -X PUT "https://statuspal.eu/api/v2/orgs/{organization_id}/status_pages/{subdomain}" \
  -H "Authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "status_page": {
      "domain_config": {
        "provider": "cloudflare",
        "domain": "status.example.com"
      }
    }
  }'
```

This resets the status to `"configuring"` and restarts the provisioning process.

## Changing the custom domain

To switch to a different domain, update `domain_config` with the new domain:

```bash
curl -X PUT "https://statuspal.eu/api/v2/orgs/{organization_id}/status_pages/{subdomain}" \
  -H "Authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "status_page": {
      "domain_config": {
        "provider": "cloudflare",
        "domain": "new-status.example.com"
      }
    }
  }'
```

StatusPal will automatically revoke the old domain's certificate and begin provisioning the new one. Follow Steps 2–4 again for the new domain.

## Setting up a custom domain on a new status page

You can configure the custom domain at creation time:

```bash
curl -X POST "https://statuspal.eu/api/v2/orgs/{organization_id}/status_pages" \
  -H "Authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "status_page": {
      "name": "My Status Page",
      "url": "example.com",
      "time_zone": "UTC",
      "domain_config": {
        "provider": "cloudflare",
        "domain": "status.example.com"
      }
    }
  }'
```

Then follow Steps 2–4 to complete the DNS setup.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.statuspal.io/guides/platform/user-guides/custom-domains-api-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
