> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/get-started/samples/showcase-demo-cad-viewer.md).

# CAD Viewer Showcase Demo Code Sample

Easily create a visualization tool for AutoCAD's DWG and DXF files. View, edit, and annotate those files as PDF files from the browser.

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="/core/get-started/get-started.md" class="button primary">Server SDK</a><a href="/web/get-started/readme.md" class="button primary">Web SDK</a><a href="https://apryse.com/capabilities#CADConversion" class="button primary">Package: CAD Conversion</a><a href="/core/learn-more/modules.md#cad-module" class="button primary">Module: CAD</a><a href="https://showcase.apryse.com/cad-viewer" class="button primary">Live demo</a>
{% endhint %}

Easily create a visualization tool for AutoCAD's DWG and DXF files. View, edit, and annotate those files as PDF files from the browser.

This demo allows you to:

* Load AutoCAD Files: DWG, DWF, DXF, DGN, and RVT.
* View and edit the files as PDF documents
* Save as PDF or PNG.

Don't need the WebViewer UI component or need a different server language? Check out the [Server SDK CAD Conversion sample code](/core/get-started/samples/cad2pdftest.md).

### **Implementation steps**

To add CAD Viewer capability in Node.js with Server and WebViewer: Step 1: [Get started with Server SDK in Node.js](/core/get-started/frameworks/nodejs.md) Step 2: [Download the CAD Module](https://github.com/iKettles/apryse-gitbook/tree/main/core/guides/info/modules.md#cad-module) Step 3: Get started in your [preferred web stack for WebViewer](/web/get-started/readme.md) Step 4: Add the ES6 JavaScript sample code provided in this guide

Once you generate your license key, it will automatically be included in your sample code below.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="compact" %}

{% tabs %}
{% tab title="index.js" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// ES6 Compliant Syntax
// GitHub Copilot v1.0, GPT-4.1, September 29, 2025
// File: index.js

import WebViewer from '@pdftron/webviewer';

const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_WEBVIEWER_LICENSE_KEY"</code>';

// CAD Viewer Demo
// 
// This code demonstrates how to view CAD files directly in the browser with the Apryse JavaScript document SDK.
// It shows how to create a display and visualization tool for AutoCAD floor plans and drawings.
// Allowing users to view, edit, and annotate AutoCAD DWG CAD files from the browser using the WebViewer Server.
//
// **Important** 
// 1. You must get a license key from Apryse for the server to run. 
// A trial key can be obtained from:
// https://docs.apryse.com/core/guides/get-started/trial-key 
// 
// 2. You need to also run the `npm install` command at /cad-viewer/server/ location to install the `@pdftron/pdfnet-node` and `@pdftron/cad` packages.

// Initialize WebViewer with the specified settings
function initializeWebViewer() {

    // This code initializes the WebViewer with the basic settings
    WebViewer({
        path: '/lib',
        licenseKey: licenseKey,
        enableFilePicker: false,
    }, element).then((instance) => {
        // Enable the measurement toolbar so it appears with all the other tools, and disable Cloudy rectangular tool
        const cloudyTools = [
            instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT,
            instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT2,
            instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT3,
            instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT4,
        ];
        instance.UI.enableFeatures([instance.UI.Feature.Measurement, instance.UI.Feature.Initials]);
        instance.UI.disableTools(cloudyTools);

        // Set default toolbar group to Annotate
        instance.UI.setToolbarGroup('toolbarGroup-Annotate');

        // Set default tool on mobile devices to Pan.
        if (UIElements.isMobileDevice()) {
            instance.UI.setToolMode(instance.Core.Tools.ToolNames.PAN);
        }

        instance.Core.documentViewer.addEventListener('documentUnloaded', () => {
            if (searchParams.has('file')) {
                searchParams.delete('file');
                history.replaceState(null, '', '?' + searchParams.toString());
            }
        });

        instance.Core.annotationManager.enableAnnotationNumbering();
        instance.UI.NotesPanel.enableAttachmentPreview();

        // Add the demo-specific functionality
        customizeUI(instance).then(() => {
            // Create UI controls after demo is initialized
            UIElements.createUIControls(instance);
        });
    });
}

// CAD files, Key is the file name, value is the URL
const CAD_FILES = {
    'DWG': 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/construction%20drawings%20color.dwg',
    'DXF': 'PROVIDE_URL_HERE/*.dxf',
};

const defaultDoc = CAD_FILES['DWG']; // Default CAD file to load

// Default styles for measurement tools
const DEFAULT_FONT_SIZE = 16;
window.DEFAULT_FONT_SIZE = DEFAULT_FONT_SIZE;
const DEFAULT_STROKE_THICKNESS = 2;
window.DEFAULT_STROKE_THICKNESS = DEFAULT_STROKE_THICKNESS;
const MEASUREMENT_TOOLS = [
    'AnnotationCreateDistanceMeasurement',
    'AnnotationCreatePerimeterMeasurement',
    'AnnotationCreateAreaMeasurement',
];

window.MEASUREMENT_TOOLS = MEASUREMENT_TOOLS;

const customizeUI = async (instance) => {
    const { Feature } = instance.UI;
    const { Annotations, documentViewer } = instance.Core;

    instance.UI.enableFeatures([Feature.Measurement]);
    instance.UI.enableTools(MEASUREMENT_TOOLS);
    instance.UI.openElements(['tabPanel']);
    instance.UI.setActiveLeftPanel('layersPanel');

    // Update default tool styles
    MEASUREMENT_TOOLS.forEach((tool, index) => {
        documentViewer.getTool(tool).setStyles({
            StrokeThickness: DEFAULT_STROKE_THICKNESS / documentViewer.getZoomLevel(),
            StrokeColor: new Annotations.Color(
                255 * Number(index === 0),
                255 * Number(index === 1),
                255 * Number(index === 2),
            ),
        });
    });

    // Update font size to be larger
    Annotations.LineAnnotation.prototype['constant']['FONT_SIZE'] = DEFAULT_FONT_SIZE / documentViewer.getZoomLevel() + 'px';
    Annotations.LineAnnotation.prototype['constant']['TEXT_COLOR'] = '#FF0000';
    documentViewer.addEventListener('zoomUpdated', (zoom) => UIElements.zoomUpdated(instance, zoom));

    // Using the Apryse CAD Module, Convert and Load default document
    const cadUrl = defaultDoc;
    const cadFilename = cadUrl.split('/').pop();
    const response = await fetch(defaultDoc);
    if (!response.ok) {
        throw new Error(`Failed to fetch CAD: ${response.status}`);
    }
    const cadBuffer = await response.arrayBuffer();
    const pdfBuffer = await convertCadtoPdf(cadBuffer, cadFilename);
    instance.UI.loadDocument(pdfBuffer, {
        extension: 'pdf',
    });
};

const convertCadtoPdf = async (cadBuffer, cadFilename) => {
    // Send the CAD to the server to be converted to PDF
    console.log('Sending CAD to server for conversion...');
    const cadBlob = new Blob([cadBuffer]);
    const formData = new FormData();
    formData.append('cadfile', cadBlob, cadFilename);

    const postResponse = await fetch('http://localhost:5050/server/handler.js', {
        method: 'POST',
        body: formData,
    });

    if (postResponse.status !== 200) {
        throw new Error(`Server error during CAD upload: ${postResponse.status}`);
    }
    const buffer = await postResponse.arrayBuffer();
    return buffer;
};

// Make convertCadtoPdf globally available
window.convertCadtoPdf = convertCadtoPdf;
const searchParams = new URLSearchParams(window.location.search);
const history = window.history || window.parent.history || window.top.history;
const element = document.getElementById('viewer');

// Cleanup function for when the demo is closed or page is unloaded
const cleanup = (instance) => {
    const { Feature } = instance.UI;

    if (typeof instance !== 'undefined' &#x26;&#x26; instance.UI) {
        instance.UI.disableTools(MEASUREMENT_TOOLS);
        instance.UI.disableFeatures([Feature.Measurement]);
        instance.UI.closeElements(['leftPanel']);
        console.log('Cleaning up cad-viewer demo');
    }
};

// Register cleanup for page unload
window.addEventListener('beforeunload', () => cleanup(instance));
window.addEventListener('unload', () => cleanup(instance));

//helper function to load the ui-elements.js script
function loadUIElementsScript() {
    return new Promise((resolve, reject) => {
        if (window.UIElements) {
            console.log('UIElements already loaded');
            resolve();
            return;
        }

        const script = document.createElement('script');
        script.src = '/showcase-demos/cad-viewer/client/ui-elements.js';
        script.onload = function () {
            console.log('✅ UIElements script loaded successfully');
            resolve();
        };
        script.onerror = function () {
            console.error('Failed to load UIElements script');
            reject(new Error('Failed to load ui-elements.js'));
        };
        document.head.appendChild(script);
    });
}

// Load UIElements script first, then initialize WebViewer
loadUIElementsScript().then(() => {
    initializeWebViewer();
}).catch((error) => {
    console.error('Failed to load UIElements:', error);
});

</code></pre>

{% endtab %}

{% tab title="ui-elements.js" %}
{% code title="ui-elements.js" lineNumbers="true" %}

```js
// UI Elements class to create and manage custom UI controls
//
// Helper code to add controls to the viewer holding the buttons
// This code creates a container for the buttons, styles them, and adds them to the viewer
//
class UIElements {

    // Function to check if the user is on a mobile device
    static isMobileDevice = () => {
        return (
            /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
                window.navigator.userAgent
            ) ||
            /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(
                window.navigator.userAgent.substring(0, 4)
            )
        );
    }

    // Choose File button
    static filePicker = (instance) => {
        const button = document.createElement('button');
        button.className = 'btn-filepicker';
        button.textContent = 'Choose File';
        button.onclick = () => {
            const input = document.createElement('input');
            input.type = 'file';
            input.accept = '.dwg,.dxf,.dwf,.dgn,.rvt'; // Supported CAD file formats
            input.onchange = async (event) => {
                try {
                    const file = event.target.files[0];
                    if (file) {
                        const arrayBuffer = await file.arrayBuffer();
                        const pdfBuffer = await window.convertCadtoPdf(arrayBuffer, file.name);
                        instance.UI.loadDocument(pdfBuffer, {
                            extension: 'pdf',
                        });
                    }
                } catch (e) {
                    console.error(e);
                }
            };
            input.click();
        };

        return button;
    }

    static createUIControls = (instance) => {
        // Create a container for all controls
        const controlsContainer = document.createElement('div');
        controlsContainer.className = 'button-container';

        // Add the file picker and Import/Export buttons to the controls container
        controlsContainer.appendChild(this.filePicker(instance));

        // Add the controls container to the viewer element
        const element = document.getElementById('viewer');
        element.insertBefore(controlsContainer, element.firstChild);
    };

    static zoomUpdated = (instance, zoom) => {
        const { Annotations, documentViewer } = instance.Core;
        Annotations.LineAnnotation.prototype['constant']['FONT_SIZE'] = window.DEFAULT_FONT_SIZE / zoom + 'px';

        window.MEASUREMENT_TOOLS.forEach((tool) => {
            documentViewer.getTool(tool).setStyles({
                StrokeThickness: window.DEFAULT_STROKE_THICKNESS / zoom,
            });
        });
    };
}
```

{% endcode %}
{% endtab %}

{% tab title="index.css" %}
{% code title="index.css" lineNumbers="true" %}

```css
/* CSS standard Compliant Syntax */
/* GitHub Copilot v1.0, GPT-4.1, September 29, 2025 */
/* File: index.css */

.btn-filepicker {
    background-color: #007bff;
    margin: 10px;
    padding: 5px 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    font-weight: bold;
    transition: all 0.2s ease;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    color: white;
}

.btn-filepicker:hover {
    background-color: #0056b3;
    transform: translateY(-1px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}

.btn-filepicker:active {
    transform: translateY(1px);
    box-shadow: 0 1px 2px rgba(0,0,0,0.2);
}

/* Button Container */
.button-container {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 15px;
    margin: 5px 0;
    padding: 16px;
    padding-bottom: 5px;
    border-bottom: 1px solid #DFE1E6;
    background-color: rgba(112, 198, 255, 0.2);
}
```

{% endcode %}
{% endtab %}

{% tab title="handler.js" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// ES6 Compliant Syntax
// GitHub Copilot v1.0, GPT-4.1, September 29, 2025
// File: handler.js
// This file will handle CAD file conversion requests.

const fs = require('fs');
const { PDFNet } = require('@pdftron/pdfnet-node');

// **Important** 
// 1. You must get a license key from Apryse for the server to run. 
// A trial key can be obtained from:
// https://docs.apryse.com/core/guides/get-started/trial-key 
// 
// 2. You need to also run the `npm install` command at /cad-viewer/server/ location to install the `@pdftron/pdfnet-node` and `@pdftron/cad` packages.
const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_SERVER_LICENSE_KEY"</code>';
const multer = require('multer');
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'sentCads/')
  },
  filename: function (req, file, cb) {
    // Save with original filename and extension
    cb(null, file.originalname)
  }
});
const upload = multer({ storage: storage });
const { response } = require('express');
const serverFolder = 'server';
const sentCads = 'sentCads';
const serverHandler = `/${serverFolder}/handler.js`;

module.exports = async (app) => {

  async function initializePDFNet() {
    // Create folder sentCads that will hold the sent CAD format files, if it doesn't exist
    if (!fs.existsSync(sentCads))
      fs.mkdirSync(sentCads);

    // Initialize PDFNet
    await PDFNet.initialize(licenseKey);

    // Specify the CAD library path
    await PDFNet.addResourceSearchPath('./node_modules/@pdftron/cad/lib/');

    // Check if the Apryse SDK CAD module is available.
    if (await PDFNet.CADModule.isModuleAvailable())
      console.log('Apryse SDK CAD module is available.');
    else
      console.log('Unable to run: Apryse SDK CAD module not available.');
  }

  // Handle POST request sent to '/server/handler.js'
  // This endpoint receives the CAD file URL to be loaded in the Apryse webviewer, then saves it to the server
  app.post(serverHandler, upload.single('cadfile'), async (request, response) => {
    try {
      const cadFilename = request.file.originalname;
      const fullFilename =  request.file.path;

      // Convert the CAD file to PDF and get the buffer
      const buffer = await convertCadToPdfBuffer(fullFilename);

      console.log(`Conversion complete, sending PDF back to client...`);

      // Set headers to indicate a PDF file attachment and send the buffer
      await response.setHeader('Content-Type', 'application/pdf');
      await response.setHeader('Content-Disposition', `attachment; filename="${cadFilename.replace(/\.[^/.]+$/, ".pdf")}"`);
      response.status(200).send(buffer);
    } catch (e) {
      response.status(500).send(`Error processing CAD file: ${e.message}`);
    } finally {
      // Cleanup: remove the sent CAD file
      const cadPath = request.file.path;
      fs.unlink(cadPath, (err) => {
        if (err) {
          console.error(`Error removing CAD file ${cadPath}: ${err.message}`);
        }
      });
    }
  });

  const convertCadToPdfBuffer = async (fullFilename) => {
    try {
      // Create a new PDF document and convert the CAD file to PDF
      const doc = await PDFNet.PDFDoc.create();
      console.log('Converting CAD to PDF. Filename and Extension:', fullFilename);

      const options = new PDFNet.Convert.CADConvertOptions();
      options.setPageWidth(800);
      options.setPageHeight(600);
      options.setRasterDPI(150);

      await PDFNet.Convert.fromCAD(doc, fullFilename, options);


      // Initialize security handler and lock the document
      doc.initSecurityHandler();
      doc.lock();

      // Save the PDF document to a memory buffer
      console.log('After Conversion and Stored in PDFDoc Full filename:', doc.fullFilename);
      const uint8Array = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_linearized);
      const buffer = Buffer.from(uint8Array);

      // Unlock the document
      doc.unlock();

      // Return the PDF buffer
      return buffer;
    }
    catch (err) {
      console.log(err);
      throw new Error(err);
    }
  };

  // Initialize PDFNet
  PDFNet.runWithoutCleanup(initializePDFNet, licenseKey).then(
    function onFulfilled() {
      response.status(200);
    },
    function onRejected(error) {
      // log error and close response
      console.error('Error initializing PDFNet', error);
      response.status(503).send();
    }
  );
};

</code></pre>

{% endtab %}

{% tab title="server.js" %}
{% code title="server.js" lineNumbers="true" %}

```js
// ES6 Compliant Syntax
// GitHub Copilot v1.0, GPT-4.1, September 29, 2025
// File: server.js
// This file is to run a server in localhost.

const express = require('express');
const fs = require('fs');
const bodyParser = require('body-parser');
const handler = require('./handler.js');
const port = process.env.PORT || 5050;
const app = express();
const sentPdfs = 'sentPdfs';

// CORS middleware to allow cross-origin requests from the playground
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
  
  // Handle preflight OPTIONS requests
  if (req.method === 'OPTIONS') {
    res.sendStatus(200);
  } else {
    next();
  }
});

app.use(bodyParser.text());
app.use('/client', express.static('../client')); // For statically serving 'client' folder at '/'

handler(app);

// Run server
const server = app.listen(port, 'localhost', (err) => {
  if (err) {
    console.error(err);
  } else {
    console.info(`Server is listening at http://localhost:${port}`);
    
  }
});

// Server shutdown and cleanup
function shutdown() {
  console.log('Cleanup started...');

  // Example: Close server
  server.close(() => {
    console.log('Server closed.');

    // Removes sent PDFs folder
    if (fs.existsSync(sentPdfs))
      fs.rmdirSync(sentPdfs, { recursive: true });

    // If no async cleanup, exit directly
    process.exit(0);
  });
}

// Handle shutdown signals
process.on('SIGINT', shutdown);   // Ctrl+C
process.on('SIGTERM', shutdown);  // kill command or Docker stop
process.on('uncaughtException', (err) => {
  console.error('Uncaught Exception:', err);
  shutdown();
});
```

{% endcode %}
{% endtab %}

{% tab title="package.json" %}
{% code title="package.json" lineNumbers="true" %}

```json
{
  "name": "cad-viewer-server",
  "version": "1.0.0",
  "description": "CAD Viewer Demo Server Component",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "node server.js"
  },
  "dependencies": {
    "@pdftron/cad": "^11.7.0",
    "@pdftron/pdfnet-node": "^11.7.0",
    "body-parser": "^1.20.2",
    "express": "^4.18.2",
    "multer": "^1.4.4",
    "open": "^9.1.0"
  },
  "keywords": [
    "cad-viewer",
    "pdf",
    "server",
    "pdftron",
    "webviewer"
  ],
  "author": "Apryse",
  "license": "MIT"
}

```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/get-started/samples/showcase-demo-cad-viewer.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
