Blog

Read POST JSON in sGTM: the runQueryString gotcha

A misnamed API and a parsing trap. Here is the working pattern for reading JSON bodies in Custom Templates.

When a Custom Template needs to read a JSON POST body in sGTM, the documentation points to getRequestBody(). That returns the raw string, not parsed JSON, and the obvious next step (calling JSON.parse) fails in some sandbox versions because of how the parser handles certain edge cases.

The straightforward case

const JSON = require('JSON');
const body = getRequestBody();
const parsed = JSON.parse(body);
return parsed.event_name;

In most modern containers this works. The require statement is critical; without it, JSON refers to the unsandboxed global, which is not allowed.

When parse fails silently

If the request body contains a value with embedded newlines or unescaped quotes, JSON.parse can return null without throwing. Always check the result:

const parsed = JSON.parse(body);
if (!parsed) {
  log('Failed to parse body:', body);
  return undefined;
}

Form-encoded bodies

Not all POST bodies are JSON. The GA4 client speaks form-urlencoded; if you wrote a custom client to handle other formats, use the parseUrl helper:

const body = getRequestBody();
const parsed = parseUrl('?' + body);
const eventName = parsed.searchParams.en;

The leading ? is required because parseUrl expects a full URL fragment.

Streaming larger bodies

For batched events from clients like Adobe Analytics, the body can be hundreds of kilobytes. getRequestBody() returns the entire string in memory, which can exceed the template's runtime budget. If you hit this, either chunk on the source side or write a server-side filter that drops oversize requests with a 413.

Headers vs body

If a value can come from either a header or the body, prefer the header. Headers are smaller, faster to parse, and not subject to the same JSON.parse quirks. Common candidates for header-first reads: auth tokens, content type, custom signature headers.

For the broader pattern of passing through raw bodies to webhook destinations, the body just needs to be forwarded as-is rather than parsed.