Policy Documents¶
Waulter can render versioned legal documents — Cookie Policy, Privacy Policy, Terms of Service — directly on your website using the appendDocument SDK method. Document content is maintained in the Waulter dashboard, so updates to legal text require no code deploy.
What appendDocument does¶
The appendDocument method fetches a managed document from Waulter's servers and injects its HTML content into a DOM element on your page.
- Versioned — each document has a unique ID and version history
- Centrally managed — edit text in the dashboard, changes appear on your site immediately
- Styled — content inherits your site's CSS and is wrapped in a
.waulter-documentcontainer class
The element ID is your choice
The first argument to appendDocument is the id of any HTML element on your page where the document will be rendered. You choose this ID — it can be anything valid (cookie-policy, privacy-doc, terms-container, etc.). Just make sure the <div> with that ID exists in the DOM when the function fires.
Document IDs and versioning¶
Each managed document has a unique ID (e.g. YOUR_DOC_ID). Find it in the Waulter dashboard under your configuration's Documents section.
Where to find your Document ID
In the Waulter dashboard, navigate to your website configuration > Documents. Each document (Cookie Policy, Privacy Policy) shows its unique ID.
Latest version vs specific version¶
By default, appendDocument loads the latest published version of the document — whenever you update the text in the dashboard, visitors automatically see the new version without any code change.
If you need to pin a specific version (e.g. for legal audit trail), pass the version identifier as a third argument:
// Always load the latest version (default)
window.WaulterSDK.appendDocument('your-element-id', 'YOUR_DOC_ID');
// Pin to a specific version
window.WaulterSDK.appendDocument('your-element-id', 'YOUR_DOC_ID', 'v2');
When to pin a version
Most sites should use the default (latest) behaviour. Pin a version only when you need to reference a specific document revision — for example, when a consent receipt must link to the exact policy text the user accepted.
Implementation patterns¶
Pattern 1: Static HTML page¶
The simplest approach — suitable when the SDK is loaded in <head> and you are confident it is ready:
<h1>Cookie Policy</h1>
<div id="your-element-id">
<p>Loading cookie policy...</p>
</div>
<script>
if (window.WaulterSDK && window.WaulterSDK.appendDocument) {
window.WaulterSDK.appendDocument('your-element-id', 'YOUR_DOC_ID');
}
</script>
SDK timing
This simple check only works if the SDK has already finished loading. For production use, always use the async-safe pattern below.
Pattern 2: Async-safe with polling (recommended)¶
The SDK loads asynchronously, so you cannot guarantee it is ready when your script runs. The polling pattern handles this reliably:
<h1>Cookie Policy</h1>
<div id="your-element-id">
<p>Loading cookie policy...</p>
</div>
<script>
(function() {
var callWhenReady = function() {
if (window.WaulterSDK && typeof window.WaulterSDK.appendDocument === 'function') {
window.WaulterSDK.appendDocument('your-element-id', 'YOUR_DOC_ID');
return;
}
var deadline = Date.now() + 8000;
var interval = setInterval(function() {
if (window.WaulterSDK && typeof window.WaulterSDK.appendDocument === 'function') {
clearInterval(interval);
window.WaulterSDK.appendDocument('your-element-id', 'YOUR_DOC_ID');
} else if (Date.now() >= deadline) {
clearInterval(interval);
console.warn('[Waulter] Timeout: SDK not ready after 8 s');
}
}, 100);
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', callWhenReady);
} else {
callWhenReady();
}
})();
</script>
The appendDocument call takes two required arguments:
| Argument | What to pass | Example |
|---|---|---|
elementId | The id of the wrapper <div> on your page — you choose this | 'cookie-policy', 'privacy-doc' |
documentId | The Document ID from the Waulter dashboard | 'YOUR_DOC_ID' |
This is the same pattern used for GTM Custom HTML tags.
Pattern 3: GTM Custom HTML tag¶
When deploying via GTM, create a Custom HTML tag with the polling pattern. This is useful when you want to manage document rendering through your tag manager.
See the full GTM Custom HTML guide for the annotated pattern.
Pattern 4: SPA / React¶
import { useEffect } from 'react';
function CookiePolicy() {
useEffect(() => {
const checkSDK = setInterval(() => {
if (window.WaulterSDK?.appendDocument) {
clearInterval(checkSDK);
window.WaulterSDK.appendDocument('your-element-id', 'YOUR_DOC_ID');
}
}, 100);
const timeout = setTimeout(() => clearInterval(checkSDK), 8000);
return () => {
clearInterval(checkSDK);
clearTimeout(timeout);
};
}, []);
return (
<div>
<h1>Cookie Policy</h1>
<div id="your-element-id">
<p>Loading cookie policy...</p>
</div>
</div>
);
}
export default CookiePolicy;
SPA navigation
If your SPA re-renders the component on navigation, appendDocument will be called again via the useEffect cleanup and re-run cycle. This is the expected behaviour.
Container element¶
The first argument to appendDocument is the id attribute of the HTML element where the document will be injected. The element must exist in the DOM when the function is called.
You can use any block-level element (div, section, article). The element can contain placeholder content (like "Loading...") which will be replaced when the document loads.
Styling documents¶
The injected document content inherits your site's CSS. Waulter wraps the content in a container with the .waulter-document class, which you can target for custom styling:
/* Custom styles for Waulter documents */
.waulter-document {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
font-size: 0.95rem;
line-height: 1.6;
}
.waulter-document h2 {
margin-top: 2rem;
border-bottom: 1px solid #eee;
padding-bottom: 0.5rem;
}
.waulter-document table {
width: 100%;
border-collapse: collapse;
}
.waulter-document table td,
.waulter-document table th {
padding: 0.5rem;
border: 1px solid #ddd;
}
Document types¶
The appendDocument method is not limited to cookie policies. Any document managed in the Waulter dashboard can be rendered on your site:
| Document type | Example use |
|---|---|
| Cookie Policy | Required by ePrivacy — explains what cookies your site uses and why |
| Privacy Policy | GDPR-required disclosure of personal data processing |
| Terms of Service | Legal terms governing use of your website or application |
| Custom legal content | Any informational or legal content you manage centrally |
All document types work identically with appendDocument — fetch the Document ID from the dashboard and render it into a container element on your page.
Custom URLs (external document links)¶
Instead of rendering documents inline via appendDocument, you can configure external URLs for your policy documents. When set, the consent banner links directly to those URLs rather than rendering inline content.
How to configure:
- Open your configuration in the Waulter dashboard.
- Navigate to the Documents section.
- For each document (Cookie Policy, Privacy Policy), choose between:
- Managed document — content hosted and rendered by Waulter via
appendDocument - External URL — a link to your own hosted page (e.g.
https://example.com/privacy-policy) - Save the configuration.
When to use external URLs
Use external URLs when you already have policy pages managed by your legal team or a third-party service, and you don't need Waulter to host the content. The banner will link to your pages instead of rendering inline documents.
Mixed approach
You can use a managed document for your Cookie Policy (rendered via appendDocument) and an external URL for your Privacy Policy (linking to your own page) — or vice versa. Each document type is configured independently.
Suppressing the consent banner on document pages¶
Cookie policy and privacy policy pages should not show the consent banner (to avoid a circular consent prompt). Add ?no_waulter_cb to the page URL:
Or configure the URL in your site's routing so the banner suppression parameter is always present on legal document pages.
appendDocument still works with ?no_waulter_cb
The ?no_waulter_cb parameter only suppresses the consent banner — it does not prevent the SDK from loading. Document rendering via appendDocument still works.
Troubleshooting¶
| Issue | Cause | Solution |
|---|---|---|
| Document does not appear | Container element missing from DOM | Add <div id="your-element-id"></div> to the page |
| Document does not appear | SDK not loaded | Use the polling pattern instead of a simple if check |
| "SDK not ready after 8 s" | SDK blocked (ad blocker, CSP, network) | Check browser Network tab for sdk.js errors |
| Document appears then disappears | SPA framework re-renders the component | Ensure appendDocument runs after re-render (use useEffect) |
| Styling looks wrong | Site CSS conflicts | Use .waulter-document selector to scope styles |
| Wrong document displayed | Incorrect Document ID | Verify the ID in the dashboard Documents section |