Destination Payload Format: JSON, Form-Data and XML
How the body template you write becomes the request a destination receives. Covers the JSON, Form-Data and XML encodings, merge tags, why a field arrives blank, and how to see the exact payload before sending.
Every webhook destination has a format (JSON, Form-Data or XML) and a body template. The template is what you author; the format decides how that template is turned into the request your destination actually receives. They are not always the same thing, which is the source of most confusion on this screen.
Form-Data: why the template looks like JSON
A Form-Data body template is STORED as a JSON object and sent as form-urlencoded key=value pairs. Seeing JSON in the editor next to Format: Form-Data is correct. It is not a bug and nothing is mis-posted.
The key/value rows you fill in are saved as a JSON object behind the scenes, because that is a reliable way to store a list of fields. At send time each value is filled in and the whole thing is encoded as application/x-www-form-urlencoded, which is what your destination receives. So this template:
{
"firstName": "{{first_name}}",
"email": "{{email}}"
}is sent as this body:
firstName=Maria&email=maria%40example.comValues are percent-encoded, so a lead value containing an ampersand, an equals sign or a space cannot break the body or create an extra field.
JSON and XML
For JSON, the template is sent as-is with merge tags filled in, and values are escaped so a quote or newline in lead data cannot break the payload. If the finished body is not valid JSON, we do not send it: you get a clear message instead of your destination's error. For XML, values are escaped the same way, and a malformed document is flagged but still sent, because your destination's own reply is usually the better clue.
Content-Type is set for you
The Content-Type header comes from the format automatically: application/json, application/x-www-form-urlencoded, or application/xml. You do not need to add a Content-Type header row. If you add one that disagrees with the format, your row wins and your destination is told the body is something it is not.
Merge tags and why a field arrives blank
A merge tag that is not one of your campaign's fields is replaced with an EMPTY value, not with the text you typed. So a fixed value like an API key must be typed as the value itself, with no curly braces.
{{first_name}} pulls the first_name field from the lead. {{abc123}} pulls a field called abc123, and if no such field exists on the campaign, the destination receives that key with nothing in it. This is the most common silent setup mistake: an API key or a publisher id typed inside curly braces looks configured and posts empty on every lead.
| You want to send | Write this | Not this |
|---|---|---|
| A value from the lead | {{first_name}} | first_name |
| A fixed API key | CL3x3gceFqrZ3Yb | {{CL3x3gceFqrZ3Yb}} |
| A fixed publisher id | 1 | {{1}} |
See the exact payload before you send
Use Send Test on the destination to see the full request and your destination's reply. You can also ask the AI assistant to preview the payload for a destination: it returns the exact method, URL, headers and body, without sending anything, and flags any field that would arrive blank.
Order of operations
- Field transforms run first, so a merge tag sees the transformed value
- Merge tags in the endpoint URL are filled in and URL-encoded
- The body template is filled in and encoded for the chosen format
- The Content-Type header is set from the format, unless you supplied one
- Authentication is applied last, so it always beats a hand-written header
Frequently Asked Questions
My Format is Form-Data but the body template shows JSON. Is that broken?
Why is one field always empty at my destination?
Do I need to add a Content-Type header?
What happens if a lead value contains an ampersand or a quote?
Related Articles
If you have any questions, send us an email at support@leaddistro.ai