If someone recommends “Request Token Encoding: None” in a webhook, always push back

A client recently asked about connecting Marketo to Clay and we were dismayed at Clay’s official docs:

Step 5: Name and configure your Webhook.  To properly configure Marketo’s webhook to send data to Clay, please fill in the following details exactly as shown:  URL: The webhook URL you copied from Step 1 Payload Template: first_name={{lead.First Name:default=editme}} &last_name={{lead.Last Name:default=editme}} &company={{company.Company Name}} &title={{lead.Job Title}} &email={{lead.Email Address:default=editme}} &id={{lead.Id}} Request Token Encoding: None Request Type: Post Response Format: JSON
Snippet from Clay docs

See how they recommend Request Token Encoding: None? That’s totally wrong, since:

  1. There are {{lead.tokens}} in the body, i.e. request tokens.
  2. The body uses URL encoding, indicated by the & and = delimiters.
  3. You must URL-encode tokens in a URL-encoded body.

This webhook setup instantly breaks with a Company like Jack & Jill Construction. It’s kind of inconceivable that they tested it at all.

Moreover, when you actually set up a webhook receiver in the Clay app, it says the payload is supposed to be JSON-encoded, not URL-encoded! So their doc feels like a hallucination. You should actually be using a payload like this:

{
  "first_name" : {{lead.First Name}},
  "last_name" : {{lead.Last Name}}
}

And of course set Request Token Encoding: JSON.

Why would you ever use Request Token Encoding: None?

In 2 rare cases, None is correct:

  • When all fields in your payload — can’t just be a subset — are already JSON- or URL-encoded. In practice, this usually means the payload is a single field written via API, designed for sending as-is.

    For example, you might store a complete JSON object with properties and child objects in a Textarea, and the data is intended to have that same shape on the remote server. If you let Marketo encode {{lead.Big JSON Object}} again, that won’t work: it’ll be one giant string.
  • Even more rare is when the remote service expects text/plain payloads. Usually these are old internal services to which you send values like 1;NOT PROVIDED;TRUE (that's the entire payload!).

    You don't want to encode the 3 tokens that comprise that payload: it either becomes 1;NOT+PROVIDED;TRUE (URL) or 1;"NOT PROVIDED";"TRUE" (JSON) which aren't the same.

In both of these cases, something non-standard is happening and you should push back on the requester to explain the reason.

P.S. Tokens in the URL are always URL-encoded, even with None (but pretend you don’t know that)

An undocumented quirk is when the URL, not the request body, contains tokens:

https://hooks.example.com/123?first_name={{lead.First Name}}&last_name={{lead.Last Name]}

Those tokens are always URL-encoded, regardless of the Request Token Encoding setting. This is fine! But it’s confusing because the URL is undoubtedly part of the request: Request Token Encoding should be read as Request Body Token Encoding.

So be careful not to rest on whether a webhook “works” using GET. When the service switches to POST in a few months and you move those tokens to the body, it’ll break unless you have Request Token Encoding: Form/URL.