The SDK writes four attributes onto the element it fills. Those attributes are the
integration surface — read them, don't race the fetch. It also pushes two events to
dataLayer, so GTM can react without any DOM code at all.
var el = document.getElementById("myDoc");
new MutationObserver(function () {
if (el.getAttribute("data-waulter-document-state") !== "loaded") return;
console.log({
from: el.getAttribute("data-waulter-document-resolved-from"),
servedId: el.getAttribute("data-waulter-document-resolved-id"),
language: el.getAttribute("data-waulter-document-language")
});
}).observe(el, {
attributes: true,
attributeFilter: [
"data-waulter-document-state",
"data-waulter-document-resolved-from",
"data-waulter-document-resolved-id",
"data-waulter-document-language"
]
});
loading permanently and looked like a failure.
MutationObserver fires whenever the SDK writes, however long that takes.
Both entry points push these, so GTM can trigger on them:
{ event: "waulter.document.loaded", elementId, requestedAgreementId, resolvedAgreementId }
{ event: "waulter.document.error", elementId, requestedAgreementId, reason }
// reason ∈ empty_agreement_id | no_element | no_content | fetch_failed | sdk_never_started