# Web Hosting

*Web Hosting* is a resource that puts a built static website into a *Space* and serves it at a `{subdomain}.weegloo.app` address. Take a clothing store as an example: serving the built store site at `dailywear-shop.weegloo.app` is one *Web Hosting*.

The order of putting it up is as follows. First, bundle the build output as a ZIP or tar.gz and upload it through the [Upload API](/api/reference/upload-api.md) to get one *Upload*. Then create a *Web Hosting* with `POST /web-hostings`, referencing that *Upload*. Once the system processes the uploaded files and `sys.state` becomes `COMPLETED`, you can reach the site at `url`. In CMA, a *Web Hosting* is a sub-resource of *Space*, and its path is based on `/spaces/{spaceId}/web-hostings`.

## Resource structure {#resource-structure}

The following is the single-read response for the *Web Hosting* "DailyWear store site" after processing has finished. Along with `sys` (system properties), it has the body properties (`name`, `description`, `isSpa`, `subdomain`, `url`, `pageMetas`).

```json
{
  "sys": {
    "id": "3trmXRM3RqbgSnifyg7PWeb01Examp",
    "type": "WebHosting",
    "space": { "sys": { "id": "tcq4V2Xb", "type": "Refer", "targetType": "Space" } },
    "createdBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
    "updatedBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
    "createdAt": "2026-06-18T11:40:00.000Z",
    "updatedAt": "2026-06-18T11:40:05.000Z",
    "state": "COMPLETED",
    "totalFileSize": 245786,
    "originMetas": [
      {
        "file": "index.html",
        "meta": {
          "title": "Daily Wear Store",
          "description": "Everyday clothing store"
        }
      }
    ],
    "version": 3
  },
  "name": "DailyWear store site",
  "description": "Static site for the clothing and accessories store",
  "isSpa": true,
  "subdomain": "dailywear-shop",
  "url": "https://dailywear-shop.weegloo.app",
  "pageMetas": [
    {
      "file": "index.html",
      "meta": {
        "title": "DailyWear - clothes for every day",
        "description": "Everyday pieces you can wear to work and on weekends.",
        "image": "https://dailywear-shop.weegloo.app/og-cover.png"
      }
    }
  ]
}
```

Key properties:

- `subdomain`: The subdomain the site will be served on. In the example above it is `dailywear-shop`, so the final address becomes `dailywear-shop.weegloo.app`.
- `url`: The site address you can reach once processing has finished.
- `isSpa`: Whether it is a single-page app (SPA). If `true`, every path request is sent to `index.html`.
- `state`: The deployment-processing state of the uploaded files. Explained in [System properties (sys)](#system-properties-sys) below.
- `pageMetas`: The meta tag values overridden per document. `sys.originMetas` holds the values as they were before the override. Both are explained in [Per-document metadata](#page-metadata) below.

## System properties (sys) {#system-properties-sys}

Every *Web Hosting* carries common system properties in the `sys` object. `space`, `createdBy`, and `updatedBy` are in the `Refer` shape (`{ "sys": { "id", "type": "Refer", "targetType" } }`).

| Property | Type | Description |
|---|---|---|
| `id` | string | Unique resource identifier. It goes into `{webHostingId}` in the single-read, update, and delete paths. |
| `type` | string | Resource kind. For a *Web Hosting* this is always `"WebHosting"`. |
| `space` | Refer&lt;Space&gt; | The *Space* this *Web Hosting* belongs to. |
| `createdBy` | Refer&lt;User&gt; | The user who created it. |
| `createdAt` | string (date-time) | Creation time. |
| `updatedBy` | Refer&lt;User&gt; | The user who last updated it. |
| `updatedAt` | string (date-time) | Last update time. |
| `state` | string (enum) | Deployment-processing state. One of the four below. |
| `error` | string | The reason when processing fails. Empty when there is no failure. |
| `totalFileSize` | integer | The total size of the uploaded files (in bytes). |
| `originMetas` | PageMeta[] | The meta tag values the documents originally carried. Filled in only for a *Web Hosting* created by installing a *MarketApp*. Explained in [Per-document metadata](#page-metadata) below. |
| `version` | integer (≥1) | Resource version. It increases by 1 with every create and update. This is the value you must send in `x-weegloo-version` on update and partial-update requests. |

`state` represents the processing stage of deploying the uploaded files. It is not a *Content* publish status, and a *Web Hosting* has no concept of publishing or archiving. Once the files are processed and it becomes `COMPLETED`, the site is reachable at `url`.

| `state` | Meaning |
|---|---|
| `PENDING` | Waiting to be processed. |
| `PROCESSING` | Being processed. |
| `COMPLETED` | Processing complete. Reachable at `url`. |
| `FAILED` | Processing failed. The reason is held in `sys.error`. |

## Body properties {#body-properties}

The body properties of a *Web Hosting* are as follows.

| Property | Type | Description |
|---|---|---|
| `name` | string (1-64) | The *Web Hosting* name. Required on create. |
| `description` | string (≤128) | Description. Optional. |
| `isSpa` | boolean | Whether it is a single-page app. If `true`, every path request is sent to `index.html` (for SPA routing). Required on create. |
| `subdomain` | string (3-32) | The serving subdomain. Pattern `^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$` (lowercase letters, digits, hyphens; the first and last characters cannot be a hyphen). Required on create. |
| `upload` | Refer&lt;Upload&gt; | A reference to the files to put up. It is a ZIP or tar.gz, must have `index.html` at the root, and assets must be referenced with relative paths. |
| `url` | string | The access URL after processing finishes. Filled in by the system. |
| `pageMetas` | PageMeta[] | The meta tag values to override per document. Optional. Explained in [Per-document metadata](#page-metadata) below. |
| `customDomain` | string | The connected custom domain. Optional. Explained in [Custom domain](#custom-domain) below. |

## Checking a subdomain {#checking-a-subdomain}

Before creating a *Web Hosting*, you can check whether the subdomain you want to use is free. Pass the subdomain to check as the `subdomain` query on `GET /web-hostings/availability?subdomain=...`.

The response has the following shape, and if `available` is `true`, you can use that subdomain.

```json
{ "subdomain": "dailywear-shop", "available": true }
```

## Custom domain {#custom-domain}

Instead of the default address `{subdomain}.weegloo.app`, you can connect a domain you own to a *Web Hosting*. The state of the connected domain is represented by the `customDomain` object, in the `{ id, domain, dns, cert }` shape. `dns` and `cert` are, respectively, the domain-ownership verification (DNS) and certificate-issuance (cert) states, and both are in the `{ status, txtName, txtContent }` shape. `txtName` and `txtContent` are the name and value of the DNS TXT record you must register on the domain side.

```json
{
  "id": 1024,
  "domain": "shop.dailywear.example",
  "dns": {
    "status": "pending",
    "txtName": "_weegloo.shop.dailywear.example",
    "txtContent": "weegloo-verify=3trmXRM3RqbgSnifyg7PWebVerifyEx"
  },
  "cert": {
    "status": "pending",
    "txtName": "_acme-challenge.shop.dailywear.example",
    "txtContent": "acme-verify=3trmXRM3RqbgSnifyg7PWebCertEx"
  }
}
```

After registering the TXT records on the domain side, trigger verification with `PUT /web-hostings/{webHostingId}/custom-domain/status/verify`, and read the current state with `GET /web-hostings/{webHostingId}/custom-domain/status`. When verification finishes, `dns.status` becomes `active` and `cert.status` becomes `ok`. Calling the status read on a *Web Hosting* that has no connected custom domain responds with an error (see [Errors](#errors)).

## Per-document metadata {#page-metadata}

You can override the meta tags in each document's `<head>` on a site you have put up. The title, description, and image that appear in search results and in messenger link previews change to these values. The point is to change them with one request instead of rebuilding and uploading the files again.

Editing works only on a *Web Hosting* created by installing a *MarketApp*. Once you replace the files through `upload`, editing is no longer possible and `sys.originMetas` is cleared along with it. A request that breaks the condition is rejected (see [Errors](#errors)).

Values go in the `pageMetas` array, and one entry targets one document.

| Property | Type | Description |
|---|---|---|
| `file` | string (1-1024) | The relative path of the target document. For example `index.html`, `about/index.html`. |
| `meta` | WebHostingMeta | The slot values to apply to that document. |

`sys.originMetas` has the same shape and holds the values the documents originally carried before the override. To revert, send those values again.

### Slots {#meta-slots}

The keys of `meta` are called slots. One slot changes several tags together.

| Slot | Max length | Tags changed |
|---|---|---|
| `title` | 200 | `<title>`, `og:title`, `twitter:title` |
| `description` | 500 | `description`, `og:description`, `twitter:description` |
| `canonical` | 2048 | `link[rel=canonical]`, `og:url` |
| `image` | 2048 | `og:image`, `twitter:image` |
| `siteName` | 200 | `og:site_name` |
| `favicon` | 2048 | `link[rel=icon]`, `link[rel="shortcut icon"]`, `link[rel=apple-touch-icon]` |
| `themeColor` | 32 | `theme-color` |

If a tag is missing from the document, it is created and inserted. Five are the exception: `twitter:title`, `twitter:description`, `twitter:image`, `link[rel="shortcut icon"]`, and `link[rel=apple-touch-icon]` change only when the document already has them, and are not created when it does not.

### What each value does {#meta-value-semantics}

The outcome depends on the shape of the value you send.

| What you put in the request | Result |
|---|---|
| The slot key is absent | The current setting stays as it is. |
| The slot key is `null` | The current setting stays as it is. |
| The slot key is an empty string | That tag is removed from the document. |
| The slot key has a value | It is overwritten with that value. |
| A document entry is left out of `pageMetas` | That document's setting stays as it is. |
| `pageMetas` is absent | Every document's setting stays as it is. |

Pay particular attention to the last two rows. The server **merges** what you send per document and per slot. So **leaving an entry out of the array does not clear a setting**; to clear one you must state an empty string on that slot.

An empty string **removes the tag rather than restoring the original**. A tag the document originally carried disappears with it. To restore the original value, send the value still held in `sys.originMetas`.

The following request body changes only the title of `index.html` and removes its description tag.

```json
{
  "pageMetas": [
    {
      "file": "index.html",
      "meta": {
        "title": "DailyWear - clothes for every day",
        "description": ""
      }
    }
  ]
}
```

Changing `pageMetas` starts the processing that applies those values to the actual documents. `sys.state` returns to `PENDING` and then comes back to `COMPLETED`, and updates and deletes are rejected in between.

## Errors {#errors}

These are the codes you meet when working with a *Web Hosting*. For codes that are common to every resource, see [common errors](/api/reference/common/errors.md).

| Code | Condition |
|---|---|
| `WGL422031` | The caller tried to update or delete a *Web Hosting* while its deployment files were still being processed. Try again once the processing has finished. |
| `WGL422113` | `pageMetas` was sent to a *Web Hosting* whose per-document metadata cannot be edited. Either it was not created by installing a *MarketApp*, or its files have since been replaced through an upload. |
| `WGL500039` | The information for the connected custom domain could not be read from the delivery network. |
| `WGL429001` | The caller tried to attach a custom domain to a *Web Hosting* that had none, while the *Organization* had already reached its plan limit on the number of custom domains. Changing a domain that already exists does not raise the count, so that change does not run this check. |

## API {#api}

The base URL for all endpoints below is `https://cma.weegloo.com/v1`, and a Bearer token that authenticates against CMA is required in the `Authorization` header. Update and partial update must also send the `X-Weegloo-Version` header (the current resource's `sys.version`) for optimistic concurrency control. Create and delete requests do not carry this header.

```api-endpoint
{
  "title": "List Web Hosting",
  "method": "GET",
  "path": "/spaces/{spaceId}/web-hostings",
  "description": "Reads the list of Web Hosting in a Space, page by page.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true }
  },
  "queryParameterSchema": {
    "limit": { "type": "integer", "description": "Number of items to return per page (1-100)", "default": 15 },
    "skip": { "type": "integer", "description": "Number of items to skip", "default": 0 },
    "next": { "type": "string", "description": "Next page cursor (the value obtained from links.next in the previous response)" },
    "prev": { "type": "string", "description": "Previous page cursor (the value obtained from links.prev in the previous response)" },
    "order": { "type": "string", "description": "Sort criteria. Comma-separated for multi-level sorting (e.g. sys.createdAt,sys.id)", "default": "sys.createdAt,sys.id" },
    "select": { "type": "string", "description": "Fields to include (sys.id,sys.createdAt) or exclude (-sys.id). Do not mix include and exclude." },
    "include": { "type": "integer", "description": "Related-resource inclusion level. 0=default, 1=related resources, 2=nested relations, 3=full", "default": 0 }
  },
  "additionalQueryParams": true,
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "sys": { "type": "TotalPageResponse" },
    "limit": 15,
    "totalCount": 1,
    "items": [
      {
        "sys": {
          "id": "3trmXRM3RqbgSnifyg7PWeb01Examp",
          "type": "WebHosting",
          "space": { "sys": { "id": "tcq4V2Xb", "type": "Refer", "targetType": "Space" } },
          "createdBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
          "updatedBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
          "createdAt": "2026-06-18T11:40:00.000Z",
          "updatedAt": "2026-06-18T11:40:05.000Z",
          "state": "COMPLETED",
          "totalFileSize": 245786,
          "version": 3
        },
        "name": "DailyWear store site",
        "description": "Static site for the clothing and accessories store",
        "isSpa": true,
        "subdomain": "dailywear-shop",
        "url": "https://dailywear-shop.weegloo.app"
      }
    ],
    "links": { "self": "/v1/spaces/tcq4V2Xb/web-hostings" }
  }
}
```

```api-endpoint
{
  "title": "Check subdomain availability",
  "method": "GET",
  "path": "/spaces/{spaceId}/web-hostings/availability",
  "description": "Checks whether a subdomain is free before creating a Web Hosting. If available is true, you can use that subdomain.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true }
  },
  "queryParameterSchema": {
    "subdomain": { "type": "string", "description": "The subdomain to check", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "subdomain": "dailywear-shop",
    "available": true
  }
}
```

```api-endpoint
{
  "title": "Read a single Web Hosting",
  "method": "GET",
  "path": "/spaces/{spaceId}/web-hostings/{webHostingId}",
  "description": "Reads one Web Hosting by sys.id.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "webHostingId": { "type": "string", "description": "The sys.id of the Web Hosting", "required": true }
  },
  "queryParameterSchema": {
    "include": { "type": "integer", "description": "Related-resource inclusion level. 0=default, 1=related resources, 2=nested relations, 3=full", "default": 0 }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "sys": {
      "id": "3trmXRM3RqbgSnifyg7PWeb01Examp",
      "type": "WebHosting",
      "space": { "sys": { "id": "tcq4V2Xb", "type": "Refer", "targetType": "Space" } },
      "createdBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
      "updatedBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-06-18T11:40:00.000Z",
      "updatedAt": "2026-06-18T11:40:05.000Z",
      "state": "COMPLETED",
      "totalFileSize": 245786,
      "version": 3
    },
    "name": "DailyWear store site",
    "description": "Static site for the clothing and accessories store",
    "isSpa": true,
    "subdomain": "dailywear-shop",
    "url": "https://dailywear-shop.weegloo.app"
  }
}
```

```api-endpoint
{
  "title": "Create Web Hosting",
  "method": "POST",
  "path": "/spaces/{spaceId}/web-hostings",
  "description": "Creates a new Web Hosting by referencing an uploaded Upload. The system processes the uploaded files, and once state becomes COMPLETED, the site is reachable at url.",
  "responseStatus": 201,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "requestBodySchema": {
    "type": "object",
    "required": ["name", "subdomain", "isSpa", "upload"],
    "properties": {
      "name": { "type": "string", "minLength": 1, "maxLength": 64, "description": "Web Hosting name" },
      "description": { "type": "string", "maxLength": 128, "description": "Description (optional)" },
      "isSpa": { "type": "boolean", "description": "Whether it is a single-page app. If true, every path is sent to index.html." },
      "subdomain": { "type": "string", "minLength": 3, "maxLength": 32, "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$", "description": "Serving subdomain (lowercase letters, digits, hyphens)" },
      "upload": { "type": "object", "description": "A Refer<Upload> pointing to the files to put up. It is a ZIP or tar.gz and must have index.html at the root." }
    },
    "example": {
      "name": "DailyWear store site",
      "description": "Static site for the clothing and accessories store",
      "isSpa": true,
      "subdomain": "dailywear-shop",
      "upload": { "sys": { "id": "4bgMfu7cFGYDRQn4jdqFPwebUp01", "type": "Refer", "targetType": "Upload" } }
    }
  },
  "responseExample": {
    "sys": {
      "id": "3trmXRM3RqbgSnifyg7PWeb01Examp",
      "type": "WebHosting",
      "space": { "sys": { "id": "tcq4V2Xb", "type": "Refer", "targetType": "Space" } },
      "createdBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
      "updatedBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-06-18T11:40:00.000Z",
      "updatedAt": "2026-06-18T11:40:00.000Z",
      "state": "PROCESSING",
      "totalFileSize": 245786,
      "version": 1
    },
    "name": "DailyWear store site",
    "description": "Static site for the clothing and accessories store",
    "isSpa": true,
    "subdomain": "dailywear-shop",
    "url": "https://dailywear-shop.weegloo.app"
  }
}
```

```api-endpoint
{
  "title": "Update Web Hosting (redeploy)",
  "method": "PUT",
  "path": "/spaces/{spaceId}/web-hostings/{webHostingId}",
  "description": "Updates a Web Hosting. If you put a new Upload in upload, it is redeployed with those files.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "webHostingId": { "type": "string", "description": "The sys.id of the Web Hosting", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" },
    "X-Weegloo-Version": { "type": "integer", "description": "The current resource's sys.version (optimistic concurrency control)" }
  },
  "requestBodySchema": {
    "type": "object",
    "required": ["name", "isSpa"],
    "properties": {
      "name": { "type": "string", "minLength": 1, "maxLength": 64, "description": "Web Hosting name" },
      "description": { "type": "string", "maxLength": 128, "description": "Description (optional)" },
      "isSpa": { "type": "boolean", "description": "Whether it is a single-page app" },
      "subdomain": { "type": "string", "minLength": 3, "maxLength": 32, "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$", "description": "Serving subdomain (optional)" },
      "upload": { "type": "object", "description": "A Refer<Upload> to redeploy with new files (optional)" },
      "customDomain": { "type": "string", "description": "The custom domain to connect (optional). Example: www.example.com" },
      "pageMetas": { "type": "array", "description": "The meta tag values to override per document (optional). Each entry has the shape { file, meta }, and what you send is merged into the existing settings per document and per slot. Replacing the files through upload clears the existing settings." }
    },
    "example": {
      "name": "DailyWear store site",
      "description": "Static site for the clothing, accessories, and footwear store",
      "isSpa": true,
      "upload": { "sys": { "id": "4bgMfu7cFGYDRQn4jdqFPwebUp02", "type": "Refer", "targetType": "Upload" } }
    }
  },
  "responseExample": {
    "sys": {
      "id": "3trmXRM3RqbgSnifyg7PWeb01Examp",
      "type": "WebHosting",
      "space": { "sys": { "id": "tcq4V2Xb", "type": "Refer", "targetType": "Space" } },
      "createdBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
      "updatedBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-06-18T11:40:00.000Z",
      "updatedAt": "2026-06-18T11:41:00.000Z",
      "state": "PROCESSING",
      "totalFileSize": 245786,
      "version": 4
    },
    "name": "DailyWear store site",
    "description": "Static site for the clothing, accessories, and footwear store",
    "isSpa": true,
    "subdomain": "dailywear-shop",
    "url": "https://dailywear-shop.weegloo.app"
  }
}
```

```api-endpoint
{
  "title": "Partially update Web Hosting (JSON Patch)",
  "method": "PATCH",
  "path": "/spaces/{spaceId}/web-hostings/{webHostingId}",
  "description": "Updates only part of a Web Hosting using RFC 6902 JSON Patch. The request Content-Type header must be application/json-patch+json. When you first put a value into a property that does not have one yet, such as pageMetas, use add rather than replace. replace requires the target path to already exist.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "requestContentType": "application/json-patch+json",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "webHostingId": { "type": "string", "description": "The sys.id of the Web Hosting", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" },
    "Content-Type": { "type": "string", "description": "application/json-patch+json (RFC 6902 JSON Patch)" },
    "X-Weegloo-Version": { "type": "integer", "description": "The current resource's sys.version (optimistic concurrency control)" }
  },
  "requestBodySchema": {
    "type": "array",
    "description": "An array of RFC 6902 JSON Patch operations (op / path / value / from). path is an RFC 6901 JSON Pointer.",
    "items": {
      "type": "object",
      "required": ["op", "path"],
      "properties": {
        "op": { "type": "string", "enum": ["add", "remove", "replace", "move", "copy", "test"], "description": "Operation kind" },
        "path": { "type": "string", "description": "Target location (JSON Pointer). Example: /description" },
        "value": { "description": "The value used by add/replace/test" },
        "from": { "type": "string", "description": "The source location for move/copy" }
      }
    },
    "example": [
      { "op": "replace", "path": "/description", "value": "Static site for the clothing and accessories store" }
    ]
  },
  "responseExample": {
    "sys": {
      "id": "3trmXRM3RqbgSnifyg7PWeb01Examp",
      "type": "WebHosting",
      "space": { "sys": { "id": "tcq4V2Xb", "type": "Refer", "targetType": "Space" } },
      "createdBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
      "updatedBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
      "createdAt": "2026-06-18T11:40:00.000Z",
      "updatedAt": "2026-06-18T11:42:00.000Z",
      "state": "COMPLETED",
      "totalFileSize": 245786,
      "version": 5
    },
    "name": "DailyWear store site",
    "description": "Static site for the clothing and accessories store",
    "isSpa": true,
    "subdomain": "dailywear-shop",
    "url": "https://dailywear-shop.weegloo.app",
    "pageMetas": [
      {
        "file": "index.html",
        "meta": {
          "title": "DailyWear - clothes for every day",
          "description": "Everyday pieces you can wear to work and on weekends.",
          "image": "https://dailywear-shop.weegloo.app/og-cover.png"
        }
      }
    ]
  }
}
```

```api-endpoint
{
  "title": "Delete Web Hosting",
  "method": "DELETE",
  "path": "/spaces/{spaceId}/web-hostings/{webHostingId}",
  "description": "Deletes a Web Hosting. On success it responds with 204 No Content and no response body.",
  "responseStatus": 204,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "webHostingId": { "type": "string", "description": "The sys.id of the Web Hosting", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  }
}
```

```api-endpoint
{
  "title": "Read custom domain status",
  "method": "GET",
  "path": "/spaces/{spaceId}/web-hostings/{webHostingId}/custom-domain/status",
  "description": "Reads the DNS and certificate verification status of the connected custom domain. Calling it on a Web Hosting that has no connected custom domain responds with an error.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "webHostingId": { "type": "string", "description": "The sys.id of the Web Hosting", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "id": 1024,
    "domain": "shop.dailywear.example",
    "dns": {
      "status": "pending",
      "txtName": "_weegloo.shop.dailywear.example",
      "txtContent": "weegloo-verify=3trmXRM3RqbgSnifyg7PWebVerifyEx"
    },
    "cert": {
      "status": "pending",
      "txtName": "_acme-challenge.shop.dailywear.example",
      "txtContent": "acme-verify=3trmXRM3RqbgSnifyg7PWebCertEx"
    }
  }
}
```

```api-endpoint
{
  "title": "Trigger custom domain verification",
  "method": "PUT",
  "path": "/spaces/{spaceId}/web-hostings/{webHostingId}/custom-domain/status/verify",
  "description": "Triggers verification after you register the DNS TXT records on the domain side. There is no request body. When verification finishes, dns.status becomes active and cert.status becomes ok.",
  "responseStatus": 200,
  "baseUrl": "https://cma.weegloo.com/v1",
  "pathParameterSchema": {
    "spaceId": { "type": "string", "description": "The sys.id of the Space", "required": true },
    "webHostingId": { "type": "string", "description": "The sys.id of the Web Hosting", "required": true }
  },
  "requestHeaderSchema": {
    "Authorization": { "type": "string", "description": "Bearer token (CMA authentication)" }
  },
  "responseExample": {
    "id": 1024,
    "domain": "shop.dailywear.example",
    "dns": {
      "status": "active",
      "txtName": "_weegloo.shop.dailywear.example",
      "txtContent": "weegloo-verify=3trmXRM3RqbgSnifyg7PWebVerifyEx"
    },
    "cert": {
      "status": "ok",
      "txtName": "_acme-challenge.shop.dailywear.example",
      "txtContent": "acme-verify=3trmXRM3RqbgSnifyg7PWebCertEx"
    }
  }
}
```

## Related documents {#related-documents}

- [Upload API](/api/reference/upload-api.md): The request that uploads a static-file ZIP to get an *Upload* to use when creating a *Web Hosting*.
- [Space](/api/reference/cma/space.md): The *Space* the *Web Hosting* belongs to.
