> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gcore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create custom response pages

export const MethodSection = ({children}) => children ?? null;

export const MethodSwitch = ({children}) => {
  const tabs = React.Children.toArray(children).map(c => {
    if (!c || !c.props) return null;
    if (c.props.id) return c;
    const inner = c.props.children;
    if (inner && inner.props && inner.props.id) return inner;
    return null;
  }).filter(Boolean);
  const firstId = tabs.length > 0 ? tabs[0].props.id : "";
  const [active, setActive] = React.useState(firstId);
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem("gcore_docs_method");
      if (saved && tabs.find(t => t.props.id === saved)) {
        setActive(saved);
      }
    } catch (_) {}
  }, []);
  React.useEffect(() => {
    try {
      document.querySelectorAll("h2[id], h3[id]").forEach(heading => {
        const visible = heading.offsetParent !== null;
        document.querySelectorAll(`a[href="#${heading.id}"]`).forEach(link => {
          if (link.closest("h1,h2,h3,h4,h5,h6")) return;
          const li = link.closest("li");
          if (li) li.style.display = visible ? "" : "none";
        });
      });
    } catch (_) {}
    window.dispatchEvent(new Event("scroll"));
  }, [active]);
  const handleClick = id => {
    setActive(id);
    try {
      localStorage.setItem("gcore_docs_method", id);
    } catch (_) {}
  };
  return <div>
      <div className="not-prose flex gap-0 border-b border-zinc-200 dark:border-zinc-800 mb-8 mt-2" role="tablist">
        {tabs.map(tab => {
    const isActive = active === tab.props.id;
    return <button key={tab.props.id} role="tab" aria-selected={isActive} onClick={() => handleClick(tab.props.id)} className={["px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors cursor-pointer", isActive ? "border-primary text-primary" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"].join(" ")}>
              {tab.props.label}
            </button>;
  })}
      </div>

      {tabs.map(tab => <div key={tab.props.id} style={{
    display: active === tab.props.id ? "" : "none"
  }}>
          {tab.props.children}
        </div>)}
    </div>;
};

<MethodSwitch>
  <MethodSection id="portal" label="Customer Portal">
    <p>In some cases, you might need to personalize the [response page](/waap/response-pages) displayed to users when a particular custom rule or WAAP policy is triggered. For instance, to add contact information, adjust the message according to your brand's voice and tone, or provide additional validation instructions.</p>

    <p>To do so, you need to create a new custom response page, as described in the following instructions.</p>

    <Tip>
      **Tip**

      For guidelines on how to modify or delete custom response pages, as well as change domains where those pages appear, check out the [Manage custom response pages](/waap/response-pages/manage-custom-response-pages) guide.
    </Tip>

    ## Create a custom response page

    <p>Creating custom response pages allows you to display different messages to your users based on which domains they are navigating to. You can create up to 6 page sets.</p>

    <p>Note that creating custom response pages is only available [at the account level](/waap/response-pages/manage-custom-response-pages#manage-custom-response-pages-at-the-account-level). You can't add and modify pages in domain settings.</p>

    ### Step 1. Add a new page set for your account

    <p>1. In the [Gcore Customer Portal](https://portal.gcore.com/accounts/reports/dashboard), navigate to **WAAP** > **Response Pages**.</p>

    <p>2. In the upper-right corner of the screen, click **Response Page Set**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/response-pages/create-page-set-button.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=5db23d40bcebf9d23c522ab38ffbb6ca" alt="Highlighted button for creating response page in the Customer Portal" width="1824" height="1016" data-path="images/docs/waap/response-pages/create-page-set-button.png" />
    </Frame>

    ### Step 2. Give your page set a name

    <p>1. Enter a unique name for your page set that's easy to distinguish from the default response pages. The name can be up to 50 characters long.</p>

    <p>2. Click **Continue to pages** to proceed with the next steps.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/response-pages/continue-to-pages.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=02e70c2cba7c25afb928182579b9885b" alt="Create response page set dialog" width="1593" height="1093" data-path="images/docs/waap/response-pages/continue-to-pages.png" />
    </Frame>

    ### Step 3. Configure the selected response page

    <p>1. In the **Create pages** section, click **Add page**.</p>

    <p>2. In the sidebar menu that opens, choose the default page type that you want to customize:</p>

    * Block
    * Block CSRF
    * Captcha
    * Enable cookies
    * Browser validation
    * Enable javascript

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/response-pages/choose-default-page-type.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=4206061bc112f432480b008c1ec57201" alt="Crete response page dialog" width="4368" height="1840" data-path="images/docs/waap/response-pages/choose-default-page-type.png" />
    </Frame>

    ### Step 4. Configure the selected response page

    <p>Configure the default response page you've selected in the previous step. Note that page customization options will vary depending on the type of page you want to modify.</p>

    #### Browser title

    <p>Add the text that will appear as the title in your web browser's tab where the custom response page is open. The title can be up to 62 characters.</p>

    <p>You can add browser title to the following response pages: Browser validation, Captcha, Block, Block CSRF.</p>

    #### Page title

    <p>Provide the name of your custom page. You can add a page title to all response pages.</p>

    <p>For example, in following screenshot, the text "Sorry, you've been blocked" is a page title.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/response-pages/block-page.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=3eb680d4bfa6cb0dcc915d1beb1aaff0" alt="Block response page" width="2852" height="1268" data-path="images/docs/waap/response-pages/block-page.png" />
    </Frame>

    #### Page message

    <p>Enter a body text that will be displayed under the page title. This is a good place to explain why a user has been challenged or blocked and what they should do to pass the check or obtain access to a domain.</p>

    <p>You can add a message to all response pages except for Browser validation.</p>

    <p>For example, in the following screenshot, the message informs users that they need to have JavaScript enabled to access the domain.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/response-pages/enable-js.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=6bebc2ea787637a19cad51785b635149" alt="Enable JavaScript response page" width="2852" height="1268" data-path="images/docs/waap/response-pages/enable-js.png" />
    </Frame>

    #### Error message

    <p>Provide a message that will be displayed when a client fails to pass the validation. This is only available for a Captcha page.</p>

    #### Upload image (optional)

    <p>Add an image that'll appear on the page. The image should be in jpeg, png, or jpg format and have the following dimensions: 450px \*130 px, 24KB maximum size. If you don't upload an image, then the default image will be applied.</p>

    <p>You can add images to the following response pages: Browser validation, Captcha, Block, Block CSRF.</p>

    <p>Here's an example of a Block CSRF page with the default Gcore image:</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/response-pages/block-csrf.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=35d1c45bd6a98f133d21a4cf39eecd90" alt="Block CSRF response page" width="2852" height="1268" data-path="images/docs/waap/response-pages/block-csrf.png" />
    </Frame>

    ### Step 5. Configure page status

    <p>Optionally, you can enable the **Page active status** checkbox to make the custom response page publicly available after creation.</p>

    <p>If you don't enable the toggle, the page won't be available for use on any domains, and the default response pages will be used instead. In such a case, you'll need to manually change its status to **Active**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/response-pages/page-status-active-toggle.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=eb561bf8d4f9901c3251c5a16fa39556" alt="Set page to active toggle enabled" width="440" height="61" data-path="images/docs/waap/response-pages/page-status-active-toggle.png" />
    </Frame>

    ### Step 6. Finalize page setup

    <p>1. Click **Preview page** to check what it'll look like.</p>

    <p>2. If you're satisfied with the changes, click **Save** to create the page.</p>

    <p>3. Click **Continue to domains** to proceed with the next steps.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/response-pages/preview-page-button.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=d0bfaa2f831fb22f746fb8662f2b2c19" alt="Preview page button" width="3468" height="1844" data-path="images/docs/waap/response-pages/preview-page-button.png" />
    </Frame>

    ### Step 7. Associate the page with domains

    <p>1. Select the domains where the new custom response page will appear.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bDhdzFkD6lztVu55/images/docs/waap/response-pages/add-domains-to-new-set.png?fit=max&auto=format&n=bDhdzFkD6lztVu55&q=85&s=b52cca3a4802a124f71e422b5f81edb5" alt="List of domains to select" width="3468" height="1464" data-path="images/docs/waap/response-pages/add-domains-to-new-set.png" />
    </Frame>

    <p>2. Click **Save** to add the page to the selected domains.</p>

    <p>The page set will be created with the **Unpublished** state. It'll change to **Published** after a few moments.</p>

    <Info>
      **Info**

      For instructions on how to edit and delete custom response pages, as well as change associated domains, check out the [Manage custom response pages](/waap/response-pages/manage-custom-response-pages) guide.
    </Info>
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>A [Custom Page Sets](/api-reference/waap#custom-page-sets) resource contains one or more customized response pages (Block, Captcha, Browser validation, and others) and can be assigned to multiple domains. All pages not included in the set use the default Gcore response page. Response examples include only the fields used in each step.</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required. To assign the page set to a domain, retrieve the WAAP domain ID via <code>GET /waap/v1/domains</code>.
    </Info>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    export WAAP_DOMAIN_ID="{YOUR_DOMAIN_ID}"
    ```

    ## Quickstart

    <p>Create a page set with a custom Block page and assign it to a domain.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import gcore
        import os

        client = gcore.Gcore(api_key=os.environ["GCORE_API_KEY"])
        domain_id = int(os.environ["WAAP_DOMAIN_ID"])

        page_set = client.waap.custom_page_sets.create(
            name="My custom pages",
            domains=[domain_id],
            block={
                "enabled": True,
                "header": "Access Denied",
                "title": "You have been blocked",
                "text": "Your request has been blocked. Contact support if you believe this is an error.",
            },
        )
        print(f"Created page set id={page_set.id}")
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"
            "strconv"

            gcore "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/option"
            "github.com/G-Core/gcore-go/waap"
        )

        func main() {
            client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))
            domainID, _ := strconv.ParseInt(os.Getenv("WAAP_DOMAIN_ID"), 10, 64)

            pageSet, err := client.Waap.CustomPageSets.New(context.Background(), waap.CustomPageSetNewParams{
                Name:    "My custom pages",
                Domains: []int64{domainID},
                Block: waap.CustomPageSetNewParamsBlock{
                    Enabled: true,
                    Header:  gcore.String("Access Denied"),
                    Title:   gcore.String("You have been blocked"),
                    Text:    gcore.String("Your request has been blocked. Contact support if you believe this is an error."),
                },
            })
            if err != nil {
                panic(err)
            }
            fmt.Printf("Created page set id=%d\n", pageSet.ID)
        }
        ```
      </Tab>
    </Tabs>

    ## Page set parameters

    <p>Only `name` is required. Include only the page types to customize — unspecified types use the default Gcore page. The table below maps Portal page type names to their API field names.</p>

    | Portal name        | API field             | Fields available                                      |
    | ------------------ | --------------------- | ----------------------------------------------------- |
    | Block              | `block`               | `enabled`, `header`, `title`, `text`, `logo`          |
    | Block CSRF         | `block_csrf`          | `enabled`, `header`, `title`, `text`, `logo`          |
    | Captcha            | `captcha`             | `enabled`, `header`, `title`, `text`, `error`, `logo` |
    | Enable cookies     | `cookie_disabled`     | `enabled`, `header`, `text`                           |
    | Browser validation | `handshake`           | `enabled`, `header`, `title`, `logo`                  |
    | Enable javascript  | `javascript_disabled` | `enabled`, `header`, `text`                           |

    <p>Field descriptions: `enabled` (required) — `true` activates the page immediately; `header` — browser tab title, up to 62 characters; `title` — page heading, up to 50 characters; `text` — body message, up to 400 characters; `error` — validation failure message, up to 400 characters (Captcha only); `logo` — base64-encoded image (JPEG/PNG/JPG, 450×130 px, max 24 KB).</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import gcore
        import os

        client = gcore.Gcore(api_key=os.environ["GCORE_API_KEY"])
        domain_id = int(os.environ["WAAP_DOMAIN_ID"])

        page_set = client.waap.custom_page_sets.create(
            name="My custom pages",
            domains=[domain_id],
            block={
                "enabled": True,
                "header": "Access Denied",
                "title": "You have been blocked",
                "text": "Your request has been blocked. Contact support if you believe this is an error.",
            },
            captcha={
                "enabled": True,
                "title": "Security check",
                "text": "Complete the challenge below to continue.",
                "error": "Verification failed. Please try again.",
            },
        )
        print(f"Created page set id={page_set.id}")
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"
            "strconv"

            gcore "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/option"
            "github.com/G-Core/gcore-go/waap"
        )

        func main() {
            client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))
            domainID, _ := strconv.ParseInt(os.Getenv("WAAP_DOMAIN_ID"), 10, 64)

            pageSet, err := client.Waap.CustomPageSets.New(context.Background(), waap.CustomPageSetNewParams{
                Name:    "My custom pages",
                Domains: []int64{domainID},
                Block: waap.CustomPageSetNewParamsBlock{
                    Enabled: true,
                    Header:  gcore.String("Access Denied"),
                    Title:   gcore.String("You have been blocked"),
                    Text:    gcore.String("Your request has been blocked. Contact support if you believe this is an error."),
                },
                Captcha: waap.CustomPageSetNewParamsCaptcha{
                    Enabled: true,
                    Title:   gcore.String("Security check"),
                    Text:    gcore.String("Complete the challenge below to continue."),
                    Error:   gcore.String("Verification failed. Please try again."),
                },
            })
            if err != nil {
                panic(err)
            }
            fmt.Printf("Created page set id=%d\n", pageSet.ID)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X POST "https://api.gcore.com/waap/v1/custom-page-sets" \
          -H "Authorization: APIKey ${GCORE_API_KEY}" \
          -H "Content-Type: application/json" \
          -d '{
            "name": "My custom pages",
            "domains": ['"${WAAP_DOMAIN_ID}"'],
            "block": {
              "enabled": true,
              "header": "Access Denied",
              "title": "You have been blocked",
              "text": "Your request has been blocked. Contact support if you believe this is an error."
            },
            "captcha": {
              "enabled": true,
              "title": "Security check",
              "text": "Complete the challenge below to continue.",
              "error": "Verification failed. Please try again."
            }
          }'
        ```

        Response:

        ```json theme={null}
        {
          "id": 3321,
          "name": "My custom pages",
          "domains": [12345],
          "block": {
            "enabled": true,
            "header": "Access Denied",
            "title": "You have been blocked",
            "text": "Your request has been blocked. Contact support if you believe this is an error."
          },
          "captcha": {
            "enabled": true,
            "title": "Security check",
            "text": "Complete the challenge below to continue.",
            "error": "Verification failed. Please try again."
          }
          // ...
        }
        ```
      </Tab>
    </Tabs>

    <p>The API returns the created page set with its assigned <code>id</code>. Save this value — it is required for updates and deletion.</p>
  </MethodSection>
</MethodSwitch>
