Blog

Forwarding raw POST bodies to a webhook destination

Sometimes the destination wants exactly what came in, byte for byte. Here is the pattern that does not mangle anything.

Most sGTM tag templates parse the incoming event into a structured object, then build a new request to send downstream. For some integrations (custom internal services, partner APIs that expect specific signatures, anything that signs the body), that round trip changes the bytes and breaks the destination.

When you need raw forwarding

  • The destination signs the request body with HMAC and verifies the signature.
  • The destination is a partner API that expects exactly its own protocol, not sGTM's normalised version.
  • You want to mirror traffic for debugging without altering it.

Reading the raw body

In a Custom Client, the getRequestBody() API returns the raw body as a string. Be aware: this is only available in clients, not in tags. Tags receive the parsed event object, which is one transformation removed from the raw body.

const body = getRequestBody();
const headers = getRequestHeaders();
sendHttpRequest('https://destination.example.com/ingest', {
  method: 'POST',
  body: body,
  headers: {
    'Content-Type': headers['content-type'],
    'X-Original-User-Agent': headers['user-agent']
  }
}, (statusCode) => {
  if (statusCode === 200) {
    returnResponse();
  }
});

Preserving headers

If the destination cares about specific headers (User-Agent for fingerprinting, X-Forwarded-For for IP, custom auth headers), forward them explicitly. The default behaviour of sendHttpRequest does not copy any headers from the incoming request.

When raw forwarding is the wrong answer

For most analytics destinations (GA4, Meta, TikTok), use the official tag template. The templates handle authentication, retries, and error handling that you would otherwise have to reimplement. Raw forwarding is the right tool for the long tail of niche integrations, not the workhorses.

If you need to mirror an event to a destination AND transform it for another, fire two tags on the same event. One does raw forwarding, the other does the standard transformed version. They are independent.

Watch for body size limits

sGTM has a request body size limit of about 1MB. Most events are well under, but batch events from clients like Adobe Analytics can exceed it. If your raw forwarding suddenly drops large requests, check the size; you may need to chunk on the source side or filter what gets forwarded.

For the simpler case of forwarding Stripe-style webhooks, there is a separate post that walks through the standard pattern.