Custom domains

Add your own domains, check their DNS and set where the home page goes.

Custom domains need the Business plan. A domain serves links once its TXT record proves ownership and its CNAME points here: status is then Active and canServeLinks is true.

Every path below starts with https://bgum.me/api/v1/workspaces/{workspaceId}. Send your API key in the Authorization header as described in the API overview.

OperationMethod and path
List domainsGET /domains
Add a domainPOST /domains
Get a domainGET /domains/{domainId}
Check a domainPOST /domains/{domainId}/check
Set the home page and not-found redirectsPUT /domains/{domainId}/redirects
Remove a domainDELETE /domains/{domainId}

List domains

GET /domains

Lists the workspace's custom domains with their status and the DNS records each needs.

Full path
/api/v1/workspaces/{workspaceId}/domains
API key scope
domains:read
Role needed
Viewer or above

Path parameters

NameTypeRequiredDescription
workspaceId string Yes Your workspace ID, from Settings → Workspace.

Example request

curl

curl "https://bgum.me/api/v1/workspaces/$WORKSPACE_ID/domains" \
  -H "Authorization: Bearer $BGL_API_KEY"

JavaScript

const res = await fetch(`https://bgum.me/api/v1/workspaces/${process.env.WORKSPACE_ID}/domains`, {
  headers: {
    Authorization: `Bearer ${process.env.BGL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const data = await res.json();
console.log(data);

Python

import os
import requests

res = requests.get(
    f"https://bgum.me/api/v1/workspaces/{os.environ['WORKSPACE_ID']}/domains",
    headers={
        "Authorization": f"Bearer {os.environ['BGL_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())

Response

200 OK · application/json

JSON

{
  "items": [
    {
      "id": "WAgJQCtT4fGU",
      "hostname": "go.example.com",
      "status": "PendingVerification",
      "certificateStatus": "NotRequested",
      "canServeLinks": false,
      "verifiedAt": null,
      "lastCheckedAt": null,
      "lastCheckMessage": null,
      "verificationFailingSince": null,
      "rootRedirectUrl": null,
      "notFoundRedirectUrl": null,
      "linkCount": 0,
      "expiredRedirectUrl": null,
      "records": [
        {
          "type": "TXT",
          "name": "_blackgum-verify.go.example.com",
          "value": "blackgum-verify=LkI0anhlUEo1ZVQC9xjjSg",
          "purpose": "Proves you control the domain. Leave it in place."
        },
        {
          "type": "CNAME",
          "name": "go.example.com",
          "value": "bgum.me",
          "purpose": "Sends visitors to this service."
        }
      ],
      "createdAt": "2026-10-14T22:49:38.7348494+00:00"
    }
  ]
}

Errors

Any request can also return the general errors: 401 for a missing or wrong key, 403 for a missing scope or plan feature, 404 for an unknown workspace and 429 when you're sending too fast.

Add a domain

POST /domains

Adds a domain and returns the DNS records to publish. Use a subdomain you don't use for anything else, such as go.example.com.

Full path
/api/v1/workspaces/{workspaceId}/domains
API key scope
domains:write
Role needed
Administrator or above
Plan
Needs custom domains in your plan (compare plans)

Path parameters

NameTypeRequiredDescription
workspaceId string Yes Your workspace ID, from Settings → Workspace.

Request body

JSON (Content-Type: application/json). Leave out fields you don't need.

NameTypeRequiredDescription
hostname string Yes The domain, e.g. go.example.com. A pasted https:// address is reduced to its hostname.

Example request

curl

curl -X POST "https://bgum.me/api/v1/workspaces/$WORKSPACE_ID/domains" \
  -H "Authorization: Bearer $BGL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "hostname": "go.example.com"
}'

JavaScript

const res = await fetch(`https://bgum.me/api/v1/workspaces/${process.env.WORKSPACE_ID}/domains`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BGL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "hostname": "go.example.com"
  }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const data = await res.json();
console.log(data);

Python

import os
import requests

res = requests.post(
    f"https://bgum.me/api/v1/workspaces/{os.environ['WORKSPACE_ID']}/domains",
    headers={
        "Authorization": f"Bearer {os.environ['BGL_API_KEY']}",
    },
    json={
        "hostname": "go.example.com",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())

Response

201 Created · application/json

JSON

{
  "id": "WAgJQCtT4fGU",
  "hostname": "go.example.com",
  "status": "PendingVerification",
  "certificateStatus": "NotRequested",
  "canServeLinks": false,
  "verifiedAt": null,
  "lastCheckedAt": null,
  "lastCheckMessage": null,
  "verificationFailingSince": null,
  "rootRedirectUrl": null,
  "notFoundRedirectUrl": null,
  "linkCount": 0,
  "expiredRedirectUrl": null,
  "records": [
    {
      "type": "TXT",
      "name": "_blackgum-verify.go.example.com",
      "value": "blackgum-verify=LkI0anhlUEo1ZVQC9xjjSg",
      "purpose": "Proves you control the domain. Leave it in place."
    },
    {
      "type": "CNAME",
      "name": "go.example.com",
      "value": "bgum.me",
      "purpose": "Sends visitors to this service."
    }
  ],
  "createdAt": "2026-10-14T22:49:38.7348494+00:00"
}

Good to know

  • Publish every record in records at your DNS provider, then call Check a domain (or wait: we check every few minutes).

Errors

StatusCodeMeaning
422domain.invalidThe hostname isn't a valid domain or subdomain.
409domain.reservedThe hostname belongs to this service.
409domain.takenAnother workspace has already added this hostname.
429domain.limit_reachedThe organisation has as many custom domains as its plan allows.

Any request can also return the general errors: 401 for a missing or wrong key, 403 for a missing scope or plan feature, 404 for an unknown workspace and 429 when you're sending too fast.

Get a domain

GET /domains/{domainId}

Returns one domain with its status, last check result and DNS records.

Full path
/api/v1/workspaces/{workspaceId}/domains/{domainId}
API key scope
domains:read
Role needed
Viewer or above

Path parameters

NameTypeRequiredDescription
workspaceId string Yes Your workspace ID, from Settings → Workspace.
domainId string Yes The domain's ID, as returned when it was created or listed.

Example request

curl

curl "https://bgum.me/api/v1/workspaces/$WORKSPACE_ID/domains/WAgJQCtT4fGU" \
  -H "Authorization: Bearer $BGL_API_KEY"

JavaScript

const res = await fetch(`https://bgum.me/api/v1/workspaces/${process.env.WORKSPACE_ID}/domains/WAgJQCtT4fGU`, {
  headers: {
    Authorization: `Bearer ${process.env.BGL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const data = await res.json();
console.log(data);

Python

import os
import requests

res = requests.get(
    f"https://bgum.me/api/v1/workspaces/{os.environ['WORKSPACE_ID']}/domains/WAgJQCtT4fGU",
    headers={
        "Authorization": f"Bearer {os.environ['BGL_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())

Response

200 OK · application/json

JSON

{
  "id": "WAgJQCtT4fGU",
  "hostname": "go.example.com",
  "status": "PendingVerification",
  "certificateStatus": "NotRequested",
  "canServeLinks": false,
  "verifiedAt": null,
  "lastCheckedAt": null,
  "lastCheckMessage": null,
  "verificationFailingSince": null,
  "rootRedirectUrl": null,
  "notFoundRedirectUrl": null,
  "linkCount": 0,
  "expiredRedirectUrl": null,
  "records": [
    {
      "type": "TXT",
      "name": "_blackgum-verify.go.example.com",
      "value": "blackgum-verify=LkI0anhlUEo1ZVQC9xjjSg",
      "purpose": "Proves you control the domain. Leave it in place."
    },
    {
      "type": "CNAME",
      "name": "go.example.com",
      "value": "bgum.me",
      "purpose": "Sends visitors to this service."
    }
  ],
  "createdAt": "2026-10-14T22:49:38.7348494+00:00"
}

Good to know

  • status is PendingVerification (TXT record not found yet), Verified (ownership proved; waiting for the CNAME or for HTTPS), Active (serving links), DnsMisconfigured (was working, but the records have gone) or Suspended. lastCheckMessage says what's missing.

Errors

StatusCodeMeaning
404domain.not_foundNo domain with that ID in this workspace, or it isn't available to it.

Any request can also return the general errors: 401 for a missing or wrong key, 403 for a missing scope or plan feature, 404 for an unknown workspace and 429 when you're sending too fast.

Check a domain

POST /domains/{domainId}/check

Checks the DNS records now and returns the updated domain.

Full path
/api/v1/workspaces/{workspaceId}/domains/{domainId}/check
API key scope
domains:write
Role needed
Administrator or above

Path parameters

NameTypeRequiredDescription
workspaceId string Yes Your workspace ID, from Settings → Workspace.
domainId string Yes The domain's ID, as returned when it was created or listed.

Example request

curl

curl -X POST "https://bgum.me/api/v1/workspaces/$WORKSPACE_ID/domains/WAgJQCtT4fGU/check" \
  -H "Authorization: Bearer $BGL_API_KEY"

JavaScript

const res = await fetch(`https://bgum.me/api/v1/workspaces/${process.env.WORKSPACE_ID}/domains/WAgJQCtT4fGU/check`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BGL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const data = await res.json();
console.log(data);

Python

import os
import requests

res = requests.post(
    f"https://bgum.me/api/v1/workspaces/{os.environ['WORKSPACE_ID']}/domains/WAgJQCtT4fGU/check",
    headers={
        "Authorization": f"Bearer {os.environ['BGL_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())

Response

200 OK · application/json

JSON

{
  "id": "WAgJQCtT4fGU",
  "hostname": "go.example.com",
  "status": "PendingVerification",
  "certificateStatus": "NotRequested",
  "canServeLinks": false,
  "verifiedAt": null,
  "lastCheckedAt": "2026-10-14T23:02:10+00:00",
  "lastCheckMessage": "TXT record _blackgum-verify.go.example.com with the verification value wasn't found yet. DNS changes can take up to an hour.",
  "verificationFailingSince": null,
  "rootRedirectUrl": null,
  "notFoundRedirectUrl": null,
  "linkCount": 0,
  "expiredRedirectUrl": null,
  "records": [
    {
      "type": "TXT",
      "name": "_blackgum-verify.go.example.com",
      "value": "blackgum-verify=LkI0anhlUEo1ZVQC9xjjSg",
      "purpose": "Proves you control the domain. Leave it in place."
    },
    {
      "type": "CNAME",
      "name": "go.example.com",
      "value": "bgum.me",
      "purpose": "Sends visitors to this service."
    }
  ],
  "createdAt": "2026-10-14T22:49:38.7348494+00:00"
}

Errors

StatusCodeMeaning
404domain.not_foundNo domain with that ID in this workspace, or it isn't available to it.

Any request can also return the general errors: 401 for a missing or wrong key, 403 for a missing scope or plan feature, 404 for an unknown workspace and 429 when you're sending too fast.

Set the home page and not-found redirects

PUT /domains/{domainId}/redirects

Sets where the bare domain (go.example.com/) and unknown back-halves send people. Send null to use the defaults.

Full path
/api/v1/workspaces/{workspaceId}/domains/{domainId}/redirects
API key scope
domains:write
Role needed
Administrator or above

Path parameters

NameTypeRequiredDescription
workspaceId string Yes Your workspace ID, from Settings → Workspace.
domainId string Yes The domain's ID, as returned when it was created or listed.

Request body

JSON (Content-Type: application/json). Leave out fields you don't need.

NameTypeRequiredDescription
rootRedirectUrl string Yes Where go.example.com/ goes, or null for this service's home page.
notFoundRedirectUrl string Yes Where unknown back-halves go, or null for a "link not found" page.
expiredRedirectUrl string No Where expired links on this domain go when the link doesn't set its own, or null (or left out) for a "link expired" page.

Example request

curl

curl -X PUT "https://bgum.me/api/v1/workspaces/$WORKSPACE_ID/domains/WAgJQCtT4fGU/redirects" \
  -H "Authorization: Bearer $BGL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "rootRedirectUrl": "https://example.com/",
  "notFoundRedirectUrl": null
}'

JavaScript

const res = await fetch(`https://bgum.me/api/v1/workspaces/${process.env.WORKSPACE_ID}/domains/WAgJQCtT4fGU/redirects`, {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.BGL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "rootRedirectUrl": "https://example.com/",
    "notFoundRedirectUrl": null
  }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const data = await res.json();
console.log(data);

Python

import os
import requests

res = requests.put(
    f"https://bgum.me/api/v1/workspaces/{os.environ['WORKSPACE_ID']}/domains/WAgJQCtT4fGU/redirects",
    headers={
        "Authorization": f"Bearer {os.environ['BGL_API_KEY']}",
    },
    json={
        "rootRedirectUrl": "https://example.com/",
        "notFoundRedirectUrl": None,
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())

Response

200 OK · application/json

JSON

{
  "id": "WAgJQCtT4fGU",
  "hostname": "go.example.com",
  "status": "PendingVerification",
  "certificateStatus": "NotRequested",
  "canServeLinks": false,
  "verifiedAt": null,
  "lastCheckedAt": null,
  "lastCheckMessage": null,
  "verificationFailingSince": null,
  "rootRedirectUrl": "https://example.com/",
  "notFoundRedirectUrl": null,
  "linkCount": 0,
  "expiredRedirectUrl": null,
  "records": [
    {
      "type": "TXT",
      "name": "_blackgum-verify.go.example.com",
      "value": "blackgum-verify=LkI0anhlUEo1ZVQC9xjjSg",
      "purpose": "Proves you control the domain. Leave it in place."
    },
    {
      "type": "CNAME",
      "name": "go.example.com",
      "value": "bgum.me",
      "purpose": "Sends visitors to this service."
    }
  ],
  "createdAt": "2026-10-14T22:49:38.7348494+00:00"
}

Errors

StatusCodeMeaning
404domain.not_foundNo domain with that ID in this workspace, or it isn't available to it.
422destination.invalidThe destination isn't a valid web address.
422destination.schemeOnly http and https addresses can be shortened.
422destination.privateThe address points to a private network, localhost or an internal name.
422destination.loopThe address is itself a short link on this service, which would loop.

Any request can also return the general errors: 401 for a missing or wrong key, 403 for a missing scope or plan feature, 404 for an unknown workspace and 429 when you're sending too fast.

Remove a domain

DELETE /domains/{domainId}

Removes the domain from the workspace. Refused while links still use it, so nothing breaks silently.

Full path
/api/v1/workspaces/{workspaceId}/domains/{domainId}
API key scope
domains:write
Role needed
Administrator or above

Path parameters

NameTypeRequiredDescription
workspaceId string Yes Your workspace ID, from Settings → Workspace.
domainId string Yes The domain's ID, as returned when it was created or listed.

Example request

curl

curl -X DELETE "https://bgum.me/api/v1/workspaces/$WORKSPACE_ID/domains/WAgJQCtT4fGU" \
  -H "Authorization: Bearer $BGL_API_KEY"

JavaScript

const res = await fetch(`https://bgum.me/api/v1/workspaces/${process.env.WORKSPACE_ID}/domains/WAgJQCtT4fGU`, {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.BGL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
// 204 No Content: nothing to read.

Python

import os
import requests

res = requests.delete(
    f"https://bgum.me/api/v1/workspaces/{os.environ['WORKSPACE_ID']}/domains/WAgJQCtT4fGU",
    headers={
        "Authorization": f"Bearer {os.environ['BGL_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
# 204 No Content: nothing to read.

Response

204 No Content

Errors

StatusCodeMeaning
404domain.not_foundNo domain with that ID in this workspace, or it isn't available to it.
409domain.in_useLinks still use the domain, so it can't be removed.

Any request can also return the general errors: 401 for a missing or wrong key, 403 for a missing scope or plan feature, 404 for an unknown workspace and 429 when you're sending too fast.

Something missing or unclear? Tell us and we'll improve this page.